Hibernate best approach for one Java class and multiple tables? - java

Put another way: How do you model/map a heavily reused child class/table to many different parent entities?
I have several entity types each being persisted into its own table:
class A --> table A
class B --> table B
....
Now I need to make each of these classes the parent of a 1:M unidirectional child collection. The collection is a history of approvals the entity has gained over time. The Child domain class is called "ApprovalItem". The Approval class is exactly the same for all types of parents.
What is the best way to map this? If I create a single table to hold all ApprovalItems, then I can't enforce a FK relation to the PK of the entity and/or I am left with a bad database design.
On the other hand, I could create an ApprovalIems table for each entity type (e.g. A_ApprovalItems, B_ApprovalItems, etc.). This seems like a good schema on the database side, but then it seems I need to create a separate domain classes in Java for each entity approval (e.g. AAprrovalItem class, BApprovalItem class, etc.). This seems like a lot of hassle and complexity to create so many new classes in Java that do nothing other than allow me to put in different JPA mapping annotations.
Is there a mapping technique in Hibernate that will allow me to have one class in Java map to several different tables depending on who the parent owner of the collection is?

I could create an ApprovalItem table for each entity type (e.g. A_ApprovalItem, B_ApprovalItem, etc.). This seems like a good schema on the database side
But
It seems i need to create a separate domain classes in Java for each entity approval (e.g. AAprrovalItem class, BApprovalItem class, etc.).
You do not need it. you can create a single ApprovalItem class and create a #OneToMany relationship between your parent classes and your ApprovalItem. Hibernate takes care to create a linked table for each relationship.
#Entity
public class ClassA {
#Id
#GeneratedValue
private Integer id;
// Hibernate will create CLASSA_APPROVALITEM to link both class
#OneToMany
private List<ApprovalItem> approvalItemList;
}
#Entity
public class ClassB {
#Id
#GeneratedValue
private Integer id;
// Hibernate will create CLASSB_APPROVALITEM to link both class
#OneToMany
private List<ApprovalItem> approvalItemList;
}
And your ApprovalItem class
#Entity
public class ApprovalItem {
#Id
#GeneratedValue
private Integer id;
// Nothing else
}
But Let's see what Java Persistence with Hibernate book talks about it
You may have shared references to the Bid objects. As suggested earlier, a User may have a collection of references to the Bid instances they made. You can’t delete an item and all its bids without removing these references first. You may get an exception if you try to commit this transaction, because a foreign key constraint may be violated.
So keep it in mind when dealing with shared references.
In order to see how the target schema looks like, you can use the following
AnnotationConfiguration configuration = new AnnotationConfiguration();
configuration
.addAnnotatedClass(ClassA.class)
.addAnnotatedClass(ClassB.class)
.addAnnotatedClass(ApprovalItem.class)
.setProperty(Environment.USER, <TYPE_YOUR_USER>)
.setProperty(Environment.PASS, <TYPE_YOUR_PASSWORD>)
.setProperty(Environment.URL, <TYPE_YOUR_URL>)
.setProperty(Environment.DIALECT, <TYPE_YOUR_DIALECT>)
.setProperty(Environment.DRIVER, <TYPE_YOUR_DRIVER>);
SchemaExport schema = new SchemaExport(configuration);
schema.setOutputFile("schema.sql");
schema.create(<DO_YOU_WANT_TO_PRINT_TO_THE_CONSOLE>, <DO_YOU_WANT_TO_EXPORT_THE_SCRIPT_TO_THE_DATABASE>);
It will generate a file called schema.sql, which contains your target schema
regards,

Chapter 8. Inheritance Mapping of Hibernate Documentation might help.
Otherwise, I see no problem having multiple ApprovalItem derived class that "do nothing", like you say, since it does differentiate the Approval, it's like having a type of approval. Seeing your model like so, I would recommend using multiple classes, even if they only inherit from your base ApprovalItem class.
Have I well understood your question or am I missing something else more subtle?

Related

Why can't we make a relationship attribute in spring data JPA final as well?

An Owner entity has a #ManyToOne - #OneToMany relationship with the teacher entity. When I annotate each like this
#Entity
public class Product {
...
#ManyToOne(cascade = MERGE)
private final Owner owner;
On the other side, in the Owner Class,
#Entity
public class Owner {
...
#OneToMany(mappedBy = "owner", cascade = MERGE)
private final List<Product> products;
What happens now is that "owner" in the line mappedBy = "owner" turns red. Hovering over it, I get the error that the owner attribute cannot be found.
The error: Cannot find inverse attribute
The solution was simply to remove the final keyword in the attribute owner in the Product class.
It becomes private Owner owner and the error disappears. I don't understand why adding the keyword final causes this problem.
Why does this happen?
Is there a workaround? Can I still make the owner attribute final?
The main idea from the getgo was to make the product class immutable. While this is a challenge, I've managed to find a workaround for most things, but I didn't know how to fix this problem.
JPA does not support immutability. A JPA entity does require a default constructor and getters and setters for properties.
And the fields must not be final.
Technically it would be possible to implement an ORM that ignores final attributes but why should it?
The keyword final says: This gets assigned a value at construction time and never changes after that. This is just not true for JPA entities which get constructed via no-args-constructor and then populated in second step.
If you are looking for an ORM that has better support for immutable classes (constructor with arguments, "wither" methods) you might want to check out Spring Data JDBC.
Full disclosure: I'm Spring Data developer working on both Spring Data JPA and Spring Data JDBC.
I think you have understood immutability concept wrong. Immutability is a concept being forced by the Java language. For example String class is immutable because of the security, caching etc. But in your case Product is an entity class and if you save it in a persistent layer, it is already unique on it's own. So even if you make the Product class immutable, how are you going to keep that consistency during two application loads?. If you are trying to make a Product having owned by only one owner, then do a db check rather than trying to make it immutable in memory.

Efficient way to map a subset of entity fields as separate class in Hibernate

I'm designing a solution for dealing with complex structure (user related stuff with lots of relations) in a simplier and possibly more efficient way than getting all the related data from DB. The only part of data I really need in my use case is basically contained withing the non-relational 'main' entity fields.
As for now I extracted the basic fields from 'main' class (let it be class OldMain) to another class (let's call it abstract class Extracted), used #MappedSuperclass and created 2 classes that extends it- Basic (which is empty class as Extracted gives it all the data I need and mapped to table 'X') and Extended (which is also mapped to table 'X' but with all the extra relations). It basically works but the code structure looks odd and makes me thinking if that's a proper way of dealing with such a problem.
I also tried with lazy initiation on relational fields (which i guessed on the beginning would serve here well), but I wasn't able to get it to work as I wanted with Jackson (only non-lazy fields in JSON, without fetching lazy related data- it couldn't be serialized or fired another several dozen of relation queries).
Another thing i stumbled upon in some tutorial was making DTO from 'OldMain' entity to not touch the lazy fields but haven't tried it yet as I started with the #MappedSuperClass way.
#Table(name = "X")
#MappedSuperclass
public abstract class Extracted{
//all the non-relational fields from OldMain
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String name;
private String surname;
private String userName;
private String email;
}
#Table(name = "X")
#Entity
public class Basic extends Extracted{
//empty
}
#Table(name = "X")
#Entity
public class Extended extends Extracted{
//all relational fields from OldMain, no data fields
}
Also the general question is- is there any good practices when dealing with need of using only a subset of a bigger entity?
There is no obligation for a JPA Entity to map all existing columns in the corresponding table in the database. That is, given a table my_entity with columns col1, col2, col3, the Entity mapped to this table could map only col1 and col2 and ignore col3. That being said, plus the fact that you only need the non-relational attributes, you could directly use your Extracted class with the attributes you need and ignore the fact that other relational field exists. Furthermore, if all the relational fields are nullable then you could even be able to persist new instances of Extracted class. And Jackson would only (un)marshal the declared attributes in Extracted class.
In other case, I suggest to follow the approach you already are in and define new Entity classes that extend your Extracted class with the required attributes. I don't see how "code structure looks odd", other than having a Basic class with no new attributes than Extracted - you could easily make Extracted non-abstract and use it directly, and get rid of Basic.

Is mapping of an #Embeddable through a wrapper #Entity possible?

I am currently building a persistence layer for a number of data classes that I can NOT change. These classes have no Id property/field which makes them ill suited for being used in ORM.
A best case scenario for me would have been some sort of auto-generated Ids that would only be present inside the database to set the objects in relation to each other. Sadly this does not seem to be possible using the JPA apis.
Since the above approach did not work out, I decided on trying to use simple wrapper #Entity objects like so:
#Entity
public class ThirdPartyObjectWrapper {
#Id private long id;
#Embedded private ThirdPartyObject myThirdPartyObject;
}
This approach works out nicely in the database, but I am having problems getting the object out of the wrapper and into its place inside another third party object.
public class AnotherThirdPartyObject {
private ThirdPartyObject object; //Actually in a Many-To-One-Relationship
}
Because they are third party objects I'm mapping them through the orm.xml file defining the relationships there. At this point in time the relationship mapping looks like so:
<many-to-one name="object"
target-entity="ThirdPartyObjectWrapper"/>
But with this setup Hibernate tries to insert the ThirdPartyObjectWrapper.id into the AnotherThirdPartyObject.object field, which obviously fails.
My question now is:
Is what I am trying even possible?

How can I manage to create a many-to-many relationship. The generated entity will have extra attributes.

I have 2 entities: Class(of students) and Student. A student can be in many classes(like in college) and a class has many students. The problem is how to ensure that this entity, generated in the middle, has 2 primary keys, the ids of each other entity (Student and Class).
I need to know how to create it using annotations. I use EJB3 and JPA Annotations in the project.
First, you don't need a middle entity. You have two entities and a join table between them.
You need a middle entity only if you have additional information about the relation - for example a StudentClass may have timesAbsent column.
In case you really need the third entity, you can have:
an #EmbeddedId, where you define a separate class holding the two parts of the primary key. That class must be #Embeddable
an #IdClass which will let you specify two #Id fields. You'll again need another class to hold the two fields representing the key.
See this question for which option to choose.
Note that you thus have a composite primary key, not two primary keys (which you can't have)
I know how to make this happen using hibernate. May be it'll help.
Make the collection type Set.
public class CollegeClass {
private Set<Student> students;
}
public class Student {
private Set<CollegeClass> classes;
}

One-To-Many with the same entity

I'm doing reverse engineering from the database schema (using Hibernate) and I want to have following in the resulting entity:
public class Task implements Serializable {
...
List<Task> dependentTasks = new ArrayList<Task>(0);
...
}
If I do it as a 1:N relationship, it will generate this:
public class Task implements Serializable {
...
Task task;
List<Task> dependentTasks = new ArrayList<Task>(0);
...
}
If I do it as a M:N relationship, it will generate two same Lists:
public class Task implements Serializable {
...
List<Task> dependentTasks_1 = new ArrayList<Task>(0);
List<Task> dependentTasks_2 = new ArrayList<Task>(0);
...
}
EDIT -- your reverse engineering tool is creating Task task; to make the relationship bidirectional. You can remove the property from the object and the resulting configuration files, but the relationship will be unidirectional -- you will no longer be able to go from children to parents.
I bet the underlying table has a column for a task's parent called something like task_id. If you remove the reference to the parent, that column will no longer be used by your domain model.
This is the danger of using tools to do the work for you. You should dig into the documentation and understand the difference between unidirectional and bidirectional relationships in hibernate. Just curious, why does your domain class have to not have the 'task' property?
EDIT -- in reference to you comment about changing the constraint on the table, be careful. The legacy data model you have IMPLIES that tasks should have references to their parents. So in changing this, you are changing the semantics of the relationships your legacy model contains. You might break things.
I think it is a better id to leave the DB where it is, and make the model you are building conform to the semantics of the underlying relationship. In other words, to say 'we don't want the task' property doesn't make sense -- your table structure implies that you want that, and it might have been designed that way for a reason.

Categories