2015-06-22 8 views
5

Unten ist eine 1: n-Beziehung von Department zu Employee angegeben.Eine verwaltete Entität auf der @ManyToOne-Seite zusammenführen

Department (Eltern):

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

Angestellter (Kind):

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

eine verwaltete Einheit Merging (Kind) wie folgt (unter Verwendung von 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); 

aktualisiert die entsprechende Mitarbeiterzeile in der Datenbanktabelle nicht. Es passiert nur, wenn CascadeType.MERGE aus dem Kind @ManyToOne Beziehung in Employee entfernt wird.

Warum wird die Zeile in der Tabelle nicht aktualisiert? Was ist der einzige Zweck von CascadeType.MERGE in Bezug auf dieses Beispiel?

Ich verwende derzeit EclipseLink 2.6.0 mit JPA 2.1.

+0

Ich denke, es ist ein Fehler in EclipseLink. Haben Sie schon andere Versionen von EclipseLink oder Hibernate ausprobiert? Der Zweck von 'CascadeType.MERGE' in diesem Beispiel ist, wie gewöhnlich auch eine' CascadeType.MERGE' im Feld 'Abteilung' auszulösen. –

+0

Das Entfernen dieser Zeile 'entityManager.merge (employee);' sollte die Zeile in der Datenbank aktualisieren, da es sich um eine verwaltete Entität handelt.Durch das Entfernen dieser Zeile wird jedoch die gesamte Liste der mit einer Abteilung verknüpften Mitarbeiter ("employee.setDepartment (department);") mehrmals neu eingefügt, es sei denn, "CascadeType.PERSIST" wird aus "@ ManyToOne" in "Employee" entfernt '. ** Das geht am Hibernate einwandfrei. ** Daher ist es ein Fehler in EclipseLink. – Tiny

+0

Ich habe trotzdem ein Problem auf [Bugzilla] (https://bugs.eclipse.org/bugs/show_bug.cgi?id=470697) erstellt. – Tiny

Antwort

2

Kaskadierung sollte immer propagate from a Parent to a Child und nicht umgekehrt sein.

In Ihrem Fall müssen Sie die Cascade von der Kinderseite entfernen:

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

und stellen Sie sicher, dass Sie beide Seiten des Vereins festgelegt:

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

, wenn Sie eine Abteilung für eine abgetrennte Abteilung zuordnen, die dann vermutlich eine getrennte Mitarbeitergruppe hat. Wenn sich Ihr aktueller Mitarbeiter mit dem neuen xyz-Namen in dieser Liste befindet, werden seine Änderungen vom Namen der gelöschten Instanz überschrieben.

zum Beispiel, nachdem Sie Mitarbeiter employee.setDepartment (Abteilung) anrufen; Mitarbeiter (1L) -> Abteilung '-> Mitarbeiter (1L)'

Aufruf Merge auf der Mitarbeiter (1L) Instanz wird nichts tun, da die Namensänderung bereits sichtbar ist, aber es wird an die Abteilung casacade. Die Zusammenführungsabteilung kaskadiert dann auf die Instanz des Mitarbeiters (1L), die den alten Namen hat. Wenn Sie den Wert von employee.getEmployeeName() überprüft haben, würden Sie feststellen, dass die Zusammenführung zum Zurücksetzen geführt hat. Wahrscheinlich sehen Sie deshalb kein Datenbank-Update.

Das Aufrufen von Merge ist jedoch keine Option, da Sie immer noch einen Mitarbeiter haben, der auf eine abgetrennte Abteilung verweist, was ein Ausnahmefall sein soll. Aus diesem Grund gibt der Anbieter Inserts aus.

Vermutlich haben Sie Kaskadenzusammenführung für die Beziehungen festgelegt, da das vom Client bereitgestellte Abteilungsobjekt auch Änderungen an Mitarbeitern enthalten kann. Wenn ja, um diese Änderungen zu synchronisieren und die employeeName ändern, verwenden Sie:

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

Dies sowohl die Mitarbeiter in der Abteilung hinzufügen können, wenn sie nicht da ist, und immer noch die Namensänderung vornehmen, wenn es ist.

+0

Vielen Dank, Chris. Ich habe den technischen Fehler im Code gefunden. – Tiny

+0

'employee.setDepartment (department);' sollte 'employee.setDepartment (managedDepartment);'. Es sollte ein typografischer Fehler sein, denke ich. – Tiny

1

Ich lese andere Antworten und das Bugzilla-Problem, aber ich bin immer noch davon überzeugt, dass dieses Verhalten ein Fehler ist, oder zumindest ein fehlendes Feature.

Bitte folgen mein Denken:

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

Mitarbeiter ist eine verwaltete Einheit;

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

Abteilung ist eine losgelöste Einheit;

Da der Mitarbeiter bereits verwaltet wird, wird der Merging-Mitarbeiter nur eine Merge-Kaskade auf Abteilung auslösen.
Also:

merge(department) 

Jetzt Abteilung: Auch verwaltet wird; und dies wird Kaskade auf Mitarbeiter auslösen:

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

aber es gibt zwei verschiedene Szenarien:

  1. department.getEmployeeList ist bereits geholt
    es enthält freistehende Einheiten: merge wird anhängen Mitarbeiter # und sollte nicht Kaskade auf Abteilung auslösen, da es bereits aufgerufen wurde (andernfalls einen unendlichen Zyklus erzeugend).
    Beachten Sie, dass Mitarbeiter in employeeList nicht enthalten ist.
    In diesem Fall muss alles funktionieren.
  2. department.getEmployeeList ist noch nicht geholt und faul ist jetzt geladen
    es enthält verwaltete Einheiten: wurde, werden merge sollte nichts tun, da die Mitarbeiter # bereits geschafft und Kaskade auf Abteilung bereits genannt.
    Aber ..

In diesem Fall ist Mitarbeiter in employeeList enthalten?

drei Möglichkeiten (abhängig von anderen Variablen wie UP-Modus, Auto-commit, ...):

  1. no: alles
  2. ja funktionieren soll, und es ist das gleiche Beispiel: alles funktionieren soll
  3. ja, aber es ist eine andere Instanz: wahrscheinlich führt zu Veränderungen
  4. überschreiben

ich den „Fehler“ denken könnte hier sein, wenn ein träges Laden: es nicht possibl sein sollte e um zwei verwaltete Instanzen derselben Entität im selben EntityManager zu haben.

Ich kann nicht ein einziges Gegenbeispiel denken.

+1

Ob dies ein Bug ist oder nicht, aber die Software-Community fügt neue Funktionen ziemlich rechtzeitig hinzu, aber es kümmert sich nicht viel um die Behebung von Fehlern :) – Tiny

+1

Sie haben recht, insbesondere für EclipseLink. Vielleicht wird es eines Tages in einem schwarzen Loch zusammenbrechen und alle unsere Anwendungen verschlucken ... –

Verwandte Themen