OneToOne's mappedBy behaves weird - java

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.

Related

JPA #OneToMany with 1 - 1..* relationship

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)

What is the difference in using #OneToMany vs #ManyToOne to the otherside for the same relationship?

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

JPA Annotations and mappedBy

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."

Chicken or egg type hibernate mapping problem

I got a class with a one-to-one relation. Basically, in "class A" I have a one-to-one relation to "class B". This relation uses a primary key join column. Now my issue is as follows, if I try and create a instance of A, I cant save it because I haven't added a instance of B to it yet. But, I cant create a instance of B because I need the id of A first.
A easy solution would be to make the ID in B the automatically generated one, so I could then create a instance of B before creating a instance of A. However, I'm sure there is a better way of doing this? :)
I can see in the database that hibernate created a additional index on the id column of A, which im guessing is a foreign key constraint. And I can see the the documentation that the XML version of the one-to-one mapping have a attribute to specify if the relation is constrained or not, however, the #OneToOne annotation doesnt seem to have this option? :S
It seems you have two relationships between A and B tables (you have: A has a_id, b_id; B has b_id, a_id). To model one to one you need only one relationship. Determine which table is 'main' and then drop column from 'secondary' table (should be: A has a_id, b_id; B has b_id). After that hibernate (and any other schema client) will be able to insert to B first, then to A with reference to B table.
For example for egg and chicken. There are multiple relations between eggs and chickens (one chicken can laid many eggs; one egg can produce one chicken). So for the one to one relationship egg-produces-chicken, it is reasonable to have parent_egg_id column in chicken table, so an egg can be created first and then a chicken with reference to that egg.
Hibernate mapping could look like the following:
In Chicken class:
#OneToOne
#JoinColumn(name = "parent_egg_id")
public Egg getParentEgg() {
return parentEgg;
}
In Egg class:
#OneToOne(mappedBy = "parentEgg")
public Chicken getChildChicken() {
return childChicken;
}
Update:
The same thing as constrained in xml, optional property in OneToOne interface will do. It is defaulted to true, so the relationship is nullable by default.
/**
* (Optional) Whether the association is optional. If set
* to false then a non-null relationship must always exist.
*/
boolean optional() default true;
According to your comments rows to A are inserted first. I would consider having dependency from B to A, not from A to B. In this case to create item in A then in B, two insert statements are required (with relation from A to B - additional update A statement is required.).

How can I retrieve the foreign key from a JPA ManyToOne mapping without hitting the target table?

I have the following two annotated classes that I use to build a graph:
#Entity
#Table(name = "Edge")
public class Edge
{
/* some code omitted for brevity */
#ManyToOne
#JoinColumn(name = "ixNodeFrom", nullable = false)
private Node _nodFrom;
#ManyToOne
#JoinColumn(name = "ixNodeTo", nullable = false)
private Node _nodTo;
/* some code omitted for brevity */
}
#Entity
#Table(name = "Node")
public class Node
{
/* some code omitted for brevity */
#OneToMany(mappedBy = "_nodTo")
private Set<Edge> _rgInbound;
#OneToMany(mappedBy = "_nodFrom")
private Set<Edge> _rgOutbound;
/* some code omitted for brevity */
}
Now, when I build the graph, I issue two queries to fetch all rows from either table and set up the child / parent references, for which I need the ids stored in the Edge table.
Because I have defined the relation between the two tables in JPA, accessing the edge object to get the two nodes' ids triggers two SQL statements per edge, when the JPA provider lazily * loads the associated nodes. Since I already have the node objects, and the ids have already been loaded from the edge table, I want to skip those queries, as they take an awfully long time for larger graphs.
I tried adding these lines to the Edge class, but then my JPA provider wants me to make one mapping read-only, and I can't seem to find a way how to do that:
#Column(name = "ixNodeTo")
private long _ixNodeTo;
#Column(name = "ixNodeFrom")
private long _ixNodeFrom;
I'm using Eclipselink and MySQL, if it matters.
**The default behaviour for #ManyToOne actually is eager loading, see Pascal's answer*
I got three good answers that were equally helpful, and by now none percolated to the top by public vote, so I'm merging them together here for a single comprehensive answer:
a) Change the query
You can load the whole graph at once by changing the query, thereby giving the JPA provider a chance to realize that it already has everything in memory and doesn't need to go back to the DB:
List<Node> nodes = em.createQuery(
"SELECT DISTINCT n FROM Node n LEFT JOIN FETCH n._rgOutbound")
.getResultList();
(via axtavt)
b) Use read-only fields for the FKs
Loading the FKs into their own fields, as described in the question, will also work if, as the JPA provider is demanding, the fields are declared to be readonly, which is done like this:
#Column(name = "ixNodeTo", insertable = false, updatable = false)
(via bravocharlie)
c) Use property access
If you are using property access instead of field access, the JPA provider also gets a chance to realize it already has the FK and doesn't need to fetch the referenced object. In short, property access means that you put the JPA annotations on the getter, thereby "promising" the JPA provider that your getter won't go and access the rest of the object. More details in this question. This will work for Hibernate, and for Eclipselink, it will work (assumed in the original answer, experimentally confirmed by me) with weaving enabled. (via Pascal Thivent)
Additionally, as Pascal points out in his answer, #ManyToOne, contrary to my original post, is not lazy-loading, but eager-loading by default, and changing that will require weaving as well.
Have you tried
#Column(name = "ixNodeTo", insertable = false, updatable = false)
How can I retrieve the foreign key from a JPA ManyToOne mapping without hitting the target table?
In theory, a JPA provider should be able to not trigger a query when calling
someEdge.getNodeFrom().getId()
as it already has the id (as FK).
I'm 100% sure Hibernate can (assuming you're using property access). In the case of EclipseLink, I don't know (if it does, it will probably requires weaving).
Because I have defined the relation between the two tables in JPA, accessing the edge object to get the two nodes' ids triggers two SQL statements per edge, when the JPA provider lazily loads the associated nodes. Since I already have the node objects, and the ids have already been loaded from the edge table, I want to skip those queries, as they take an awfully long time for larger graphs.
Note that #ManyToOne uses an EAGER strategy by default. If you want to make it LAZY, you have to decalre it explicitly (but again, this will require weaving of your classes with EclipseLink).
I think you should try to optimize your query rather than change the mapping. For example, the following query fetches the whole graph at once (tested in Hibernate):
List<Node> nodes = em.createQuery(
"SELECT DISTINCT n FROM Node n LEFT JOIN FETCH n._rgOutbound")
.getResultList();
How about using getReference()?
For example:
Node fkNode = em.getReference(edge.getNodeFrom()); // [1]
fkNode.getId()
[1] This will not trigger a SQL query to retrieve the nodeFrom

Categories

Resources