JPA: impedance mismatch issue in OneToMany relationship

I have a question about JPA-2.0 relationships (provider - Hibernate) and their corresponding Java management. Suppose I have a Department and an Entity of an employee:

@Entity public class Department { ... @OneToMany(mappedBy = "department") private Set<Employee> employees = new HashSet<Employee>(); ... } @Entity public class Employee { ... @ManyToOne(targetEntity = Department.class) @JoinColumn private Department department; ... } 

Now I know that I need to manage Java relationships myself, as in the following unit test:

 @Transactional @Test public void testBoth() { Department d = new Department(); Employee e = new Employee(); e.setDepartment(d); d.getEmployees().add(e); em.persist(d); em.persist(e); assertNotNull(em.find(Employee.class, e.getId()).getDepartment()); assertNotNull(em.find(Department.class, d.getId()).getEmployees()); } 

If I leave either e.setDepartment(d) or d.getEmployees().add(e) , the statement fails. So far, so good. What if I do a database transaction between?

 @Test public void testBoth() { EntityManager em = emf.createEntityManager(); em.getTransaction().begin(); Department d = new Department(); Employee e = new Employee(); e.setDepartment(d); d.getEmployees().add(e); em.persist(d); em.persist(e); em.getTransaction().commit(); em.close(); em = emf.createEntityManager(); em.getTransaction().begin(); assertNotNull(em.find(Employee.class, e.getId()).getDepartment()); assertNotNull(em.find(Department.class, d.getId()).getEmployees()); em.getTransaction().commit(); em.close(); } 

Do I still need to manage both sides of the relationship? No, as it turned out, I do not need. With this modification

 e.setDepartment(d); //d.getEmployees().add(e); 

statements are still doing well. However, if I just set the other side:

 //e.setDepartment(d); d.getEmployees().add(e); 

allegations fail. What for? Is this because Employee is the party that owns this relationship? Can I change this behavior by annotating in different ways? Or is it always the “one” side of “OneToMany” that determines when the foreign key field in the database is populated?

+4
source share
3 answers

The essence of relationships in JPA has its own and reverse sides. The database update is determined by the state of the owner. In your case, Employee is its own side due to the mappedBy attribute.

From JPA 2.0 specification :

2.9 Entity Relationships

...

Relations can be bidirectional or unidirectional. Bidirectional relationships have both their own side and the opposite (non-owning) side. unidirectional relationship has only the owner. The own side of the relationship determines the updates for the relationship in the database, as described in section 3.2.4.

For bidirectional relationships, the following rules apply:

  • The flip side of a bi-directional relationship should relate to its own using the mappedBy element of OneToOne, OneToMany, or ManyToMany annotations. The mappedBy element denotes a property or field in the person who owns the relationship.
  • Many one-to-many / many-to-one parties have a bidirectional relationship; therefore, the mappedBy element cannot be specified on the ManyToOne annotation.
  • For a one-to-one bi-directional relationship, the owning party corresponds to the party that contains the corresponding foreign key.
  • For many-to-many, bidirectional relationships of both parties can be the ruling party.
+5
source

I don’t know what your test is trying to demonstrate, but it’s a fact: you must handle both sides of the association when working with bidirectional associations. Wrong. Period.

Update: Although the spec specification mentioned by axtavt is certainly accurate, I insist you should definitely establish both sides of the bidirectional association. This is not so, and the connection between your entities in the first context of persistence is broken . The JPA wiki book puts it like this:

As with all bidirectional relationships, your object model and application responsibility should support relationships in both directions. There is no magic in JPA, if you add or remove one side of a collection, you must also add or remove the other side, see damage to an object . Technically, the database will be correctly updated if you add / remove only relations on your own side, but then your object model will not be synchronized, which can cause problems.

In other words, the only right and safe way to manage your bi-directional association in Java is to set both sides of the link. This is usually done using secure link management techniques, for example:

 @Entity public class Department { ... @OneToMany(mappedBy = "department") private Set<Employee> employees = new HashSet<Employee>(); ... public void addToEmployees(Employee employee) { this.employees.add(employee); employee.setDepartment(this); } } 

I repeat, this is not true. Your test only works because you end up in a database in a new persistence context (that is, in a very specific situation, and not in general), but the code will be broken in many other situations.

+7
source

The reason that the second test succeeds in the new save context if you only update your own side in the previous context is because the save provider obviously cannot know that you did not update the other side during the save either. He only cares about his own side for persistent purposes. However, when you receive persistent objects from a conservation provider, the provider correctly sets up bidirectional associations on both sides (it is assumed that they were also saved). However, as many others have already noted, the persistence provider is not responsible for completing the created bi-directional associations, and you should always properly support bi-directional associations in your code.

+2
source

All Articles