[英]Merging a managed entity on the @ManyToOne side
Given below a one-to-many relationship from Department
to Employee
. 下面给出了从Department
到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), 合并一个托管实体(子代码),如下所示(在EJB中使用CMT),
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
. 仅当从Employee
的子@ManyToOne
关系中删除CascadeType.MERGE
时才会发生这种情况。
Why doesn't the row in the table get updated? 为什么表中的行不会更新? What is the sole purpose of CascadeType.MERGE
regarding this example? CascadeType.MERGE
关于这个例子的唯一目的是什么?
I am currently using EclipseLink 2.6.0 having JPA 2.1. 我目前正在使用具有JPA 2.1的EclipseLink 2.6.0。
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: 在您的情况下,您需要从子端删除Cascade:
@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);
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. 如果具有新xyz名称的当前员工位于该列表中,则其更改将被分离实例的名称覆盖。
for example, after you call employee.setDepartment(department); 例如,在你调用employee.setDepartment(department)之后; employee(1L) -> department' -> employee(1L)' 员工(1L) - >部门' - >员工(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. 在employee(1L)实例上调用合并将不会执行任何操作,因为名称更改已经可见,但它会传送到部门。 Merging department then cascades to the employee(1L)' instance which has the old name. 合并部门然后级联到具有旧名称的雇员(1L)'实例。 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. 如果您检查了employee.getEmployeeName()的值,您会看到合并导致它重置,这可能是您没有看到数据库更新的原因。
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. 不调用merge虽然不是一个选项,因为你仍然有员工引用一个独立的部门,这应该是一个例外情况。 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: 如果是这样,要同步这些更改并更改employeeName,您将使用:
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. 如果员工不在那里,这两者都可以将员工添加到部门,如果是,则仍然会更改名称。
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. 我也阅读了其他答案和bugzilla问题,但我仍然相信这种行为是一个错误,或者至少是一个缺失的功能。
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? 在这种情况下,员工是否包含在employeeList中?
Three possibilities (depending on other variables like flush-mode, auto-commit, ...): 三种可能性(取决于其他变量,如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 . 延迟加载时,我认为“bug”可能就在这里: 不应该在同一个EntityManager中有两个相同实体的托管实例 。
I can't think a single counterexample. 我想不出一个反例。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.