Given below a one-to-many relationship from Department
to Employee
.
Department (parent) :
@OneToMany(mappedBy = "department", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private List<Employee> employeeList = new ArrayList<Employee>(0);
Employee (child) :
@JoinColumn(name = "department_id", referencedColumnName = "department_id")
@ManyToOne(fetch = FetchType.LAZY, cascade = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REFRESH, CascadeType.DETACH})
private Department department;
Merging a managed entity (child) like the following (using CMT in EJB),
Employee employee = entityManager.find(Employee.class, 1L);
employee.setDepartment(department); // department is supplied by a client.
employee.setEmployeeName("xyz");
entityManager.merge(employee);
does not update the corresponding employee row in the database table. It happens only when CascadeType.MERGE
is removed from the child @ManyToOne
relationship in Employee
.
Why doesn't the row in the table get updated? What is the sole purpose of CascadeType.MERGE
regarding this example?
I am currently using EclipseLink 2.6.0 having JPA 2.1.
Cascading should always propagate from a Parent to a Child and not the other way around.
In your case, you need to remove the Cascade from the child-side:
@JoinColumn(name = "department_id", referencedColumnName = "department_id")
@ManyToOne(fetch = FetchType.LAZY)
private Department department;
and make sure you set both sides of the association:
Employee employee = entityManager.find(Employee.class, 1L);
employee.setDepartment(department);
employee.setEmployeeName("xyz");
department.getEmployeeList().add(employee);
entityManager.merge(department);
I read other answers and the bugzilla issue as well, but I'm still convinced that this behavior is a bug, or at least a missing feature.
Please follow my thinking:
Employee employee = entityManager.find(Employee.class, 1L);
employee is a managed entity;
employee.setDepartment(department); // department is supplied by a client.
department is a detached entity;
entityManager.merge(employee);
since employee is already managed, merging employee will only trigger merge cascade on department.
So:
merge(department)
Now depatment is managed too; and this will trigger cascade on employees:
merge(employee1)
merge(employee2)
...
merge(employeeN)
but there are two different scenarios:
In this case, is employee contained in employeeList?
Three possibilities (depending on other variables like flush-mode, auto-commit, ...):
I think the "bug" could be here, when lazy loading: it should not be possible to have two managed instances of the same entity in the same EntityManager.
I can't think a single counterexample.
This code is risky, as you are associating a detached department instance, which then presumably has a detached collection of employees. If your current employee with the new xyz name is in that list, then its changes will get overridden by the detached instance's name.
for example, after you call employee.setDepartment(department); employee(1L) -> department' -> employee(1L)'
Calling merge on the employee(1L) instance will do nothing as the name change is already visible, but it will casacade to the department. Merging department then cascades to the employee(1L)' instance which has the old name. If you checked the value of the employee.getEmployeeName(), you would see that merge caused it to reset, which is likely why you do not see a database update.
Not calling merge though is not an option, because you still have employee referencing a detached department, which is supposed to be an exception case. This is why the provider issues inserts.
Presumably you have cascade merge set on the relationships because the department object supplied by the client could contain employee changes as well. If so, to synchronize these changes and change the employeeName, you would use:
Department managedDepartment = entityManager.merge(department);
Employee employee = entityManager.find(Employee.class, 1L);
employee.setDepartment(managedDepartment);
managedDepartment.getEmployeeList().add(employee);
employee.setEmployeeName("xyz");
This both can add the employee to the department if it isn't there, and still make the name change if it is.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With