13

Lets assume we have the following Entities:

    @Entity
    public class Department {

        @OneToMany(mappedBy="department")
        private List<Employee> employees;
    }

    @Entity
    public class Employee {

        @ManyToOne
        private Department department
    }

It is understandable on an update that we need to maintain both sides of the relationship as follows:

Employee emp = new Employee();
Department dep = new Department();
emp.setDepartment(dep);
dep.getEmployees().add(emp);

All good up till now. The question is should I apply merge on both sides as follows, and an I avoid the second merge with a cascade?

entityManager.merge(emp);
entityManager.merge(dep);

Or is merging the owning side enough? Also should these merges happen inside a Transaction or EJB? Or doing it on a simple controller method with detached entities is enough?

ChrisGeo
  • 3,807
  • 13
  • 54
  • 92
  • 1
    If you're interacting with the database, you should be inside a transaction. That's true regardless of whether you're using JPA or not. It doesn't matter whether the transaction is explicit or implicitly started by a call to an EJB; both will do the job. – Tom Anderson Nov 19 '13 at 11:48
  • What TomAnderson said is *almost* always true. The exception is when you make a find, and you find with a lock other than `LockModeType.NONE`. – V G Nov 19 '13 at 13:12

2 Answers2

14

The question is should I apply merge on both sides as follows, and an I avoid the second merge with a cascade?

You can use the cascade annotation element to propagate the effect of an operation to associated entities. The cascade functionality is most typically used in parent-child relationships.

The merge operation is cascaded to entities referenced by relationships from Department if these relationships have been annotated with the cascade element value cascade=MERGE or cascade=ALL annotation.

Bidirectional relationships between managed entities will be persisted based on references held by the owning side (Employee) of the relationship. It is the developer’s responsibility to keep the in-memory references held on the owning side (Employee) and those held on the inverse side (Department) consistent with each other when they change. So, with below series of statements, the relationship will be synchronized to the database with a single merge:

Employee emp = new Employee();
Department dep = new Department();
emp.setDepartment(dep);
dep.getEmployees().add(emp);
...
entityManager.merge(dep);

These changes will be propagated to database at transaction commit. The in-memory state of the entities can be synchronized to the database at other times as well when a transaction is active by using the EntityManager#flush method.

Debojit Saikia
  • 10,532
  • 3
  • 35
  • 46
  • "Bidirectional relationships between managed entities will be persisted based on references held by the owning side (Employee) of the relationship." Doesnt this mean that you have to merge the owning side (Employee)? Or you just merge the side from where you want to apply the cascade effect? – ChrisGeo Nov 19 '13 at 11:25
  • you need to merge the side from where you want to apply the cascade effect. you need to merge `Department` and the changes will be cascaded to the associated `employees`. – Debojit Saikia Nov 19 '13 at 11:31
  • 6
    @ChrisGeo: There are two separate rules at work here. Firstly, which objects get saved to the database is determined by cascade attributes on relationships. Secondly, what state gets saved to the database for each object is determined by the state of the object on the owning side. So, if you merge the `Department`, and the `employees` property is marked as cascading merges, then the `Employee`s will get merged. And if the `Employee`s states are correct, then the correct data will get written to the database. – Tom Anderson Nov 19 '13 at 11:57
3

You have there a persist operation (made with em.merge()). Persisting a new employee does not mean that the department is also persisted (you have no cascading), so it will throw an exception because of another reason (simply try it and post the Exception).To avoid that, you either add a cascading type, or persist both of them (as you made in your example).

About your question: the only part considered would be the owning side. In the JPA 2.0 spec, Chapter 3 Entity Operations => 3.2.4 Syncrhonization to the Database is the follwoing:

Bidirectional relationships between managed entities will be persisted based on references held by the owning side of the relationship. It is the developer’s responsibility to keep the in-memory references held on the owning side and those held on the inverse side consistent with each other when they change. In the case of unidirectional one-to-one and one-to-many relationships, it is the developer’s responsibil- ity to insure that the semantics of the relationships are adhered to.[29]

Related to the need of a transaction: yes, you need for the merge operation an active transaction. Excerpt from JPA 2 specification:

The persist, merge, remove, and refresh methods must be invoked within a transaction con- text when an entity manager with a transaction-scoped persistence context is used. If there is no transac- tion context, the javax.persistence.TransactionRequiredException is thrown.

On how a transaction is started/how you starat a transaction: it depends on the type of the EntityManager (on the way how you get one). In EJB it is much easier to handle that for generic situations. Also, according to the documentation of the merge method, a TransactionRequiredException is thrown, if invoked on a container-managed entity manager of type PersistenceContextType.TRANSACTION and there is no transaction.

V G
  • 18,822
  • 6
  • 51
  • 89