簡體   English   中英

合並@ManyToOne端的托管實體

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

下面給出了從DepartmentEmployee的一對多關系。

部門(母公司):

@OneToMany(mappedBy = "department", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private List<Employee> employeeList = new ArrayList<Employee>(0);

員工(孩子):

@JoinColumn(name = "department_id", referencedColumnName = "department_id")
@ManyToOne(fetch = FetchType.LAZY, cascade = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REFRESH, CascadeType.DETACH})
private Department department;

合並一個托管實體(子代碼),如下所示(在EJB中使用CMT),

Employee employee = entityManager.find(Employee.class, 1L);
employee.setDepartment(department); // department is supplied by a client.
employee.setEmployeeName("xyz");
entityManager.merge(employee);

不更新數據庫表中相應的員工行。 僅當從Employee的子@ManyToOne關系中刪除CascadeType.MERGE時才會發生這種情況。

為什么表中的行不會更新? CascadeType.MERGE關於這個例子的唯一目的是什么?

我目前正在使用具有JPA 2.1的EclipseLink 2.6.0。

級聯應始終從父傳播到子級,而不是相反。

在您的情況下,您需要從子端刪除Cascade:

@JoinColumn(name = "department_id", referencedColumnName = "department_id")
@ManyToOne(fetch = FetchType.LAZY)
private Department department;

並確保設置關聯的兩側:

Employee employee = entityManager.find(Employee.class, 1L);
employee.setDepartment(department);
employee.setEmployeeName("xyz");
department.getEmployeeList().add(employee);
entityManager.merge(department);

這個代碼是有風險的,因為您正在關聯一個分離的部門實例,然后可能會有一個獨立的員工集合。 如果具有新xyz名稱的當前員工位於該列表中,則其更改將被分離實例的名稱覆蓋。

例如,在你調用employee.setDepartment(department)之后; 員工(1L) - >部門' - >員工(1L)'

在employee(1L)實例上調用合並將不會執行任何操作,因為名稱更改已經可見,但它會傳送到部門。 合並部門然后級聯到具有舊名稱的雇員(1L)'實例。 如果您檢查了employee.getEmployeeName()的值,您會看到合並導致它重置,這可能是您沒有看到數據庫更新的原因。

不調用merge雖然不是一個選項,因為你仍然有員工引用一個獨立的部門,這應該是一個例外情況。 這就是提供者發布插入的原因。

據推測,您在關系上有級聯合並集,因為客戶端提供的部門對象也可能包含員工更改。 如果是這樣,要同步這些更改並更改employeeName,您將使用:

Department managedDepartment = entityManager.merge(department);
Employee employee = entityManager.find(Employee.class, 1L);
employee.setDepartment(managedDepartment);
managedDepartment.getEmployeeList().add(employee);
employee.setEmployeeName("xyz");

如果員工不在那里,這兩者都可以將員工添加到部門,如果是,則仍然會更改名稱。

我也閱讀了其他答案和bugzilla問題,但我仍然相信這種行為是一個錯誤,或者至少是一個缺失的功能。

請按照我的想法:

Employee employee = entityManager.find(Employee.class, 1L);

員工是管理實體;

employee.setDepartment(department); // department is supplied by a client.

部門是一個獨立的實體;

entityManager.merge(employee);

由於員工已經被管理,合並員工只會觸發部門的合並級聯。
所以:

merge(department)

現在也管理着沮喪; 這將觸發員工級聯:

merge(employee1)
merge(employee2)
...
merge(employeeN)

但有兩種不同的情況:

  1. department.getEmployeeList已被提取
    它包含分離的實體:merge將附加employee#並且不應該觸發部門級聯,因為它已被調用(否則生成無限循環)。
    請注意,employeeList 中不包含 employee。
    在這種情況下,一切都必須工作。
  2. department.getEmployeeList 還沒有取現在是懶加載
    它包含托管實體:合並應該什么也不做,因為員工#已經被管理,並且已經調用了部門級聯。
    但..

在這種情況下,員工是否包含在employeeList中?

三種可能性(取決於其他變量,如flush-mode,auto-commit,......):

  1. 不:一切都應該有效
  2. 是的,它是同一個實例:一切都應該有效
  3. 是的,但它是一個不同的實例:可能導致更改覆蓋

延遲加載時,我認為“bug”可能就在這里: 不應該在同一個EntityManager中有兩個相同實體的托管實例

我想不出一個反例。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM