I am working on java application and using JPA to interact with the database, I have two important questions:
I want to make a bi-directional link between two classes since I need to access the data on both sides. Let's take the case of two classes A and B with A *-1 B (as UML diagram, A has an unique B and B has several A ..).
in A:
#ManyToOne
private B attribute;
in B
#OneToMany
private List<A> list;
Is that enough to make the two-way link? or it is mandatory to use the mappedBy?
Which brings us to my second question, if the mappedBy is placed on the wrong side, it'll just impact the performance or even worse ? (data not persisted)? For example in the case of the cardinality 1-* we have no choice, the mappedBy should be in the side of OneToMany and in this case:
in B
#OneToMany(mappedBy = "attribute")
private List<A> list;
Knowing the fact that I will not create the class B, and create a List and assign objects, I will do nothing in the side B. I'll just create repeatedly classes A and every time I assign it an B object, so I may have several classes A that have the same affected object B and I want that B automatically updates this link and its list of A.
Is that enough to make the two-way link? or it is mandatory to use the
mappedBy?
For a Bi-directional relationship it is mandatory .
Which brings us to my second question, if the mappedBy is placed on
the wrong side, it'll just impact the performance or even worse ?
(data not persisted)?
Worse - It will not work but it will not be silent fail you will see exceptions.
Having said that is simple to understand . It goes with #OneToMany .
This might help you understanding this more.
mappedBy should be added to the entity which does not have a foreign key in its table (most likely B in this case).
If mappedBy is on the wrong side, you should see an exception.
Is that enough to make the two-way link? or it is mandatory to use the
mappedBy?
Not quite. You do need the MappedBy attribute for bidirectional many-to-one relationships on the inverse side - that is the side that does not have the foreign key, and is always the one side in a many-to-one relationship. You also need joincolumn information on the many side of the relationship.
So in summary:
Many-to-one side – the owning side of the relationship - #JoinColumn information is on this side.
This needs to be specified in both uni-directional and bidirectional relationships
One-to-many side – the inverse side – mappedBy Attribute on this side.
This needs to be specified if the relationship is bidirectional.
#Entity
public class A ……..
//Owning side of the relationship with the #JoinColumn annotation.
#ManyToOne
// Assume TABLEPK column holds PK of B's table
#JoinColumn (name = "TABLEBPK")
private B attribute;
#Entity
public class B ……
//Inverse side of the relationship with the MappedBy attribute.
#OneToMany(MappedBy = “attribute”)
private List<A> list;
Which brings us to my second question, if the mappedBy is placed on
the wrong side, it'll just impact the performance or even worse ?
It won't work. Just put it on the inverse side of the relationship.
Knowing the fact that I will not create the class B, and create a List
and assign objects, I will do nothing in the side B. I'll just create
repeatedly classes A and every time I assign it an B object, so I may
have several classes A that have the same affected object B and I want
that B automatically updates this link and its list of A.
In this scenario, you create a class A, with the attribute field populated with an instance of B. Now when you persist A - a new instance - it will contain an instance of B in the attribute field that may or may not be new. We want JPA to persiste A and then navigate accross the relationship and persist B also. If B already exists in the perssitence context then ignore it. Adding CascadeType.PERSIST will achieve this.
#Entity
public class A ……..
//Owning side of the relationship with the #JoinColumn annotation.
#ManyToOne(cascade = CascadeType.PERSIST)
#JoinColumn (name = "TABLEBPK")
private B attribute;
These guys write really well on this stuff....
"Pro JPA 2 Mastering the Java™ Persistence API" by Mike Keith and Merrick Schnicariol."
Related
I have an entity with #OneToOne association. Like this:
#Entity
public class Person {
#Id Long id;
#OneToOne(fetch = FetchType.LAZY) Address address;
}
When I load such entity Hibernate ignores LAZY. Vlad explains:
Lazy loading works except for the parent side of a #OneToOne association. This is because Hibernate has no other way of knowing whether to assign a null or a Proxy to this variable.
Similar statement here. I don't get it. There is FK column PERSON.ADDRESS_ID and in case there is any value Hibernate should know Proxy should be used. Am I missing something?
UPADTE:
My original code was in Kotlin. I have tried to create the same example in Java and surprisingly lazy loading works fine there.
Try to understand it using #OneToMany relationship.
When you have that, you specify some collection i.e List, for example we have an entity
class A {
#OneToMany
List<B> bs;
public List<B> getBs() {
return bs;
}
}
So when hibernate loads the A, it is able to identify that you have List<B> and you may call getBs() just after the class is loaded so hibernate creates a wrapper list which doesn't have any B yet and it will wait until you perform any operation on the list ie. iterate, add etc.
As soon as you perform the operation, hibernate will issue the query and load the objects into the set, hence lazy loading works fine here.
That's why one-to-many by default is lazy
Now let's take example of #OneToOne
class A {
#OneToOne
B b;
public B getB() {}
}
When hibernate loads A, it will see that user may call the getB just after A is loaded, so it needs to initialise B as well.
Now, even if B supports proxy, hibernate have to initialise it with proxy or null and how that decision will be made, it will have to query the B to check if it exists or not but if it queries just to check, why just check only, why not initialise it fully, hence it does it eagerly, ignoring the Lazy attribute1
But this is not true for child side, if you specify the #One-To-One on child side
class B {
#OneToOne(lazy)
A a;
public A getA() {}
}
Because this is the entity for table which holds the foreign key to A entity table, hibernate will initialise it with the proxy of A because hibernate knows that this entity is child entity and has foreign key associated, so it can lazy load when required, if it's null, you would get null A.
Correction:
The above behaviour is obvious for the optionable relation (optional = true) as I have already explained and you may find other answers stating that, but it is not obvious when you use optional=false.
With non-optional relation, we would think that hibernate identify that there would be a child present for the parent so hibernate will initialise the proxy and it should depict the lazy loading behaviour.
But to even initialise the proxy, hibernate will need minimum information like identifier and it would need to query from the child table, hence it becomes the same case as optional relation and loads eagerly.
There is one solution to still make it work though (at least I thought so), that if you share the primary key of your parent entity with the child entity using #MapsId
class A {
#Id
private Integer id;
#OneToOne(fetch = Lazy, mappedBy = "a", optional = false)
private B b;
}
class B {
#Id
private Integer id;
#OneToOne
#MapsId
private A a;
}
This should have worked, because now you are sharing the parent primary key with the child and hibernate now knows the identifier and it doesn't need to query it from the table and should be able to initialise the proxy easily.
However, it doesn't work and still loads eagerly which is strange and after a little digging, I found this issue reported by Vlad himself.2
Although I found a workaround in related issues and have also asked on the above issue about it if that is a valid one, that's why not posting here.
1Some of the older version of hibernate does support the lazy loading from parent side as well but that behaviour is removed in recent versions because they needed to check the existence of the child.
2I checked this behaviour using hibernate version 5.4.8.Final and 5.4.30.Final
How to properly map #OneToMany relationship where to create entity, on #One side of #OneToMany relationship, it is required to have atleast one entity from #Many side but entity on #Many side also requires entity on #One side to exist? To put this nightmare of a sentence simply, this is the scenario I have:
This is what I want:
[ENTITY A] 1 <-----> (1..*)[ENTITY B]
At the moment I have this:
[ENTITY A] 1 <-----> (0..*)[ENTITY B]
Which is easily done like this.
#OneToMany(cascade=CascadeType.ALL, mappedBy="customer")
public Set<Agreement> agreements = new HashSet<>();
and
#ManyToOne
#JoinColumn(name = "CUSTOMER_ID", nullable=false)
private Customer customer;
So the problem is my CUSTOMER table has no column corresponding to AGREEMENT table therefore I can't enforce rule of creating Customer only when Agreement is given. At the moment I can only setup rule to create Agreement when Customer is given because AGREEMENT table has column corresponding to CUSTOMER tabel, which is easily done by nullable=false condition.
It depends very much on what type of relationship you want to enforce. If the Agreement can exist independently from the Customer then this mean that the customer_id in agreement must be nullable.
If the Agreement can not exist independently this presumes that the customer id is not nullable in which case the Agreement can not be created in first place without the customer being created. This mean you have stronger association in between the customer and the corresponding Agreement.
Once we define that we have a relationship that is strong we need to investigate how strong it really is and who will own whom. Normaly the it is the Many side that owns the relationship and the updates are happening through the many side. This mean that your JoinColumn needs to be on the MANY and the mapped by needs to be on the ONE side.
It is interesting case when the ownership is inverse when the ONE side actually owns the relationship in this case the foreign key on the many side can not be NULL because there is no way for the owning ONE side to know what the MANY side key is.
JPA doesn't provide a way to validate this, but Hibernate Validator does:
#NotNull
#Size(min=1)
public Set<Agreement> agreements = new HashSet<>();
Then you have to manually test it via the Validator:
ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory();
Validator validator = validatorFactory.getValidator();
validator.validate(customer)
Why is that a bidirectional OneToOne relationship behaves a bit weird in the following case?:
I create 2 owner side entities with the same inverse side entity
With another run of the application, I find the second owner side entity (with EntityManager), and then reach the inverse side object
And at this point if I reach the owner object through the inverse side object's reference, I get the first owner (attached picture helps)
The entities are nothing special:
Employee contains:
#OneToOne(cascade = CascadeType.ALL)
private Person person;
while Person contains:
#OneToOne(mappedBy="person")
private Employee employee;
It seems confusing for me, misleading. Is this a bug maybe, or the programmer has to know about their possibilities?
Why do you reuse the same Person for two different employees? This is not a OneToOne relationship anymore.
Btw what I suspect happens in the back, Hibernate executes the following query:
SELECT e FROM employee e WHERE person_id = ?
In this case the result set will contain two rows and Hibernate will use the first one only, of course the ordering is undefined in this case (depending on the DB you are using).
You can double check this by enabling SQL logging.
I have two tables: Student and Address. A student has many addresses and I am trying to use #OneToMany on the Student table. A question came into mind what if I use #ManyToOne on the Address table to mention that one Address belongs to Many Students. Please help my clarify my concern.
A single Student has three addresses say Address1, Address2 and Address3.
The Relationship model for above will be, say Student id is a primary key in Student and will act as a foreign key in Address:-
Then in your Address class, you will define this relationship as below:-
#ManyToOne
#JoinColumn(name ="STUDENT_ID")
private Student student;
We use #OneToMany and #ManyToOne, two different annotations, so that we are able to tell Hibernate which object is the child/many part of the relationship and which object is the parent/one side of the relationship.
We are able to tell Hibernate which object is the parent object by assigning the #OneToMany annotation to the appropriate getter method… and which object is the child object by assigning the #ManyToOne annotation to the appropriate getter method.
Hence, Address becomes child side of relationship and Student becomes
parent side of relationship.
You might even use both if it makes sense. Basically you define in which direction the relation is navigable, e.g. if a student has an address you'll probably want to be able to navigate from that student to her address. However, an address might be shared by multiple persons which are not only students so the relation on that side might be different (or not needed at all).
Whatever you decide there's one thing you should keep in mind: you should define one side of the relation to be the owner (owning side) as otherwise you'd confuse Hibernate and get unexpected results. If there's only one side (i.e. Student->Adress but not the other way round) it's easy, if you got both sides you need to declare one the owning side - in most cases this will be the "many"-side, i.e. where you put the #ManyToOne. The other side must be declared as non-owning or you get two owning sides, e.g. by adding mappedBy="name_on_owning_side" to #OneToMany.
Example:
class Student {
#ManyToOne
Address address;
}
class Address {
#OneToMany( mappedBy = "address" )
Set<Student> residents;
}
Here Student is the owner of the bidirectional relation (which allows you to navigate student->address and address->resident(student) ) and only changes to Student.address would be written to the database.
A final note: as you can see that's quite a complex topic so you might want to have a look at some tutorial, e.g. here: https://en.wikibooks.org/wiki/Java_Persistence/ManyToOne and https://en.wikibooks.org/wiki/Java_Persistence/OneToMany
I am having some trouble understanding the difference between #OneToMany and #ManyToMany. When I use #OneToMany it defaults to create a JoinTable and if you add the mappedBy attribute you will have bidirectional relationship between two entities.
I have a Question that may belong to many Categories, and one Category may belong to many Questions. I don't understand if I should use #ManyToMany or #OneToMany because for me it seems exactly the same thing, but it is probably not.
Could somebody explain it?
Well, the difference is in the design you're trying to reflect using objects.
In your case, every Question can be assigned to multiple Categories - so that's a sign of #*ToMany relationship. Now you have to decide if:
each Category can have only one Question assigned to it (it will result in a unique constraint which means that no other Category can refer the same Question) - this will be #OneToMany relationship,
each Category can have multiple Questions assigned to it (there will be no unique constraint in the Category table) - this will be #ManyToMany relationship.
#OneToMany (Question -> Category)
This relationship can be represented by join table only if you explicitly define so using #JoinTable or when it is a unidirectional relationship in which the owning side is the 'One' side (it means that in the Question entity you have a collection of Categories, but in the Categories you don't have any reference to the Question).
If you think about it, it seems quite reasonable that the join table is used. There is no other way the DBMS could save a connection between one row in Question table with multiple rows in Categories table.
However, if you would like to model a bidirectional relationship you need to specify that the Category ('Many' side) is the owning side of the relationship. In this case the DBMS can create a join column with foreign key in the Category table because each Category row can be connected with only one Question.
In this way you don't have any join table but simple foreign keys (still, as pointed at the beginning, you can force to create the join table using #JoinTable).
#ManyToMany
This relationship must be represented as a join table. It basically works very similar to the unidirectional #OneToMany relationship, but in this case you may have multiple rows from Question joined with multiple rows from Categories.
#ManyToMany relationships have mutually referential foreign keys on both sides of the relationship. Sometimes, this relationship is mediated by an adjoining table.
#OneToMany relationships have a foreign key on the "one" side and not on the "many" side. In a #OneToMany relationship, one object is the "parent" and one is the "child". The parent controls the existence of the child.
Remember that a #ManyToMany bi-directional relationship need not be symmetric!
In your Questions & Categories case, you should use #ManyToMany relationship. #ManyToMany basically means that "a Question can belong to many Categories at the same time" and "a Category can contain many Questions at the same time". A new table will automatically be created to store the mappings. Your code would look like this:
#Entity
public class Question implements Serializable {
...
#ManyToMany
private List<Category> categories;
...
}
#Entity
public class Category implements Serializable {
...
#ManyToMany
private List<Question> questions;
...
}
If you use #OneToMany relationship for your Questions and Categories (let's say Category on the One side and Questions on the other), this means that "a Question can only belong to one Category" and "a Category can contain many Questions at the same time". No new table is needed to store the mapping. Instead, a new field will automatically be created in the Many side to record the ID of the One side. Your code would look like this:
#Entity
public class Question implements Serializable {
...
#ManyToOne
private Category theCategory;
...
}
#Entity
public class Category implements Serializable {
...
#OneToMany(mappedBy="theCategory")
private List<Question> questions;
...
}