简体   繁体   English

合并@ManyToOne端的托管实体

[英]Merging a managed entity on the @ManyToOne side

Given below a one-to-many relationship from Department to Employee . 下面给出了从DepartmentEmployee的一对多关系。

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: 但有两种不同的情况:

  1. department.getEmployeeList is already fetched department.getEmployeeList已被提取
    it contains detached entities: merge will attach employee# and should not trigger cascade on department, since it has been already called (otherwise generating an infinite cycle). 它包含分离的实体:merge将附加employee#并且不应该触发部门级联,因为它已被调用(否则生成无限循环)。
    Note that employee is not contained in employeeList. 请注意,employeeList 中不包含 employee。
    In this case everything has to work. 在这种情况下,一切都必须工作。
  2. department.getEmployeeList is not yet fetched and is lazy loaded now department.getEmployeeList 还没有取现在是懒加载
    it contains managed entities: merge should do nothing since employees# are already managed and cascade on department has been already called. 它包含托管实体:合并应该什么也不做,因为员工#已经被管理,并且已经调用了部门级联。
    But.. 但..

In this case, is employee contained in employeeList? 在这种情况下,员工是否包含在employeeList中?

Three possibilities (depending on other variables like flush-mode, auto-commit, ...): 三种可能性(取决于其他变量,如flush-mode,auto-commit,......):

  1. no: everything should work 不:一切都应该有效
  2. yes, and it's the same instance: everything should work 是的,它是同一个实例:一切都应该有效
  3. yes, but it's a different instance: probably leads to changes overwrite 是的,但它是一个不同的实例:可能导致更改覆盖

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM