2015-06-22 14 views
5

Dato di seguito una relazione uno-a-molti da Department a Employee.Unione di un'entità gestita sul lato @ManyToOne

Dipartimento (genitore):

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

Employee (bambino):

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

Unione di entità gestita (bambino) nel modo seguente (utilizzando 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); 

non aggiorna la riga corrispondente del dipendente nella tabella del database. Succede solo quando CascadeType.MERGE viene rimosso dalla relazione figlio @ManyToOne in Employee.

Perché la riga della tabella non viene aggiornata? Qual è l'unico scopo di CascadeType.MERGE in merito a questo esempio?

Attualmente sto utilizzando EclipseLink 2.6.0 con JPA 2.1.

+0

Penso che sia un bug in EclipseLink. Hai provato altre versioni di EclipseLink o Hibernate? Lo scopo di "CascadeType.MERGE" in questo esempio è, come di solito, anche attivare un 'CascadeType.MERGE' nel campo' dipartimento'. –

+0

Anche rimuovendo questa riga 'entityManager.merge (impiegato);' dovrebbe aggiornare la riga nel database, poiché è un'entità gestita.Rimuovendo questa riga, tuttavia, l'intero elenco di dipendenti associato a un 'Department' (' employee.setDepartment (dipartimento); ') deve essere reinserito più volte a meno che' CascadeType.PERSIST' sia rimosso da '@ ManyToOne' in' Employee '. ** Questo va perfettamente bene su Hibernate. ** Pertanto, è sicuramente un bug in EclipseLink. – Tiny

+0

Ho creato un problema su [Bugzilla] (https://bugs.eclipse.org/bugs/show_bug.cgi?id=470697). – Tiny

risposta

2

Il collegamento in cascata deve sempre propagate from a Parent to a Child e non viceversa.

Nel tuo caso, è necessario rimuovere la Cascade dal bambino-side:

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

e assicurarsi di impostare entrambi i lati della associazione:

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

Questo codice è rischioso, come si associa un'istanza di reparto distaccato, che quindi presumibilmente ha una raccolta distaccata di dipendenti. Se il tuo attuale dipendente con il nuovo nome xyz si trova in quell'elenco, le sue modifiche verranno sovrascritte dal nome dell'istanza distaccata.

ad esempio, dopo aver chiamato employee.setDepartment (dipartimento); dipendente (1L) -> dipartimento '-> impiegato (1L)'

Chiamare l'unione sull'istanza dipendente (1L) non farà nulla poiché la modifica del nome è già visibile, ma verrà eseguita in casacade nel reparto. Il reparto unione si sovrappone all'istanza del dipendente (1L) che ha il vecchio nome. Se hai controllato il valore di employee.getEmployeeName(), vedresti che l'unione ha causato il reset, il che probabilmente è il motivo per cui non vedi un aggiornamento del database.

Non chiamare la merge sebbene non sia un'opzione, poiché si ha ancora un dipendente che fa riferimento a un reparto distaccato, che dovrebbe essere un caso di eccezione. Questo è il motivo per cui i problemi del provider si inseriscono.

Presumibilmente si dispone di un insieme di sovrapposizioni a cascata sulle relazioni perché l'oggetto dipartimento fornito dal client potrebbe contenere anche le modifiche dei dipendenti. Se è così, per sincronizzare questi cambiamenti e cambiare l'employeeName, si usa:

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

questo sia possibile aggiungere il dipendente al reparto se non è lì, e ancora fare il cambio di nome se lo è.

+0

Grazie mille, Chris. Sono riuscito ad agguantare l'errore tecnico nel codice. – Tiny

+0

'employee.setDepartment (dipartimento);' dovrebbe essere 'employee.setDepartment (managedDepartment);'. Dovrebbe essere un errore tipografico, penso. – Tiny

1

Ho letto altre risposte e anche il problema di bugzilla, ma sono comunque convinto che questo comportamento sia un bug o almeno una caratteristica mancante.

Si prega di seguire il mio pensiero:

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

dipendente è un'entità gestita;

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

dipartimento è un'entità distaccata;

entityManager.merge(employee); 

poiché il dipendente è già gestito, la fusione di un dipendente attiverà solo la fusione in cascata sul reparto.
Quindi:

merge(department) 

Ora depatment è gestito troppo; e questo innescherà cascata su dipendenti:

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

ma ci sono due diversi scenari:

  1. department.getEmployeeList è già recuperato entità
    esso contiene per un lato: merge attribuirà dipendente # e non dovrebbe trigger cascade sul reparto, poiché è già stato chiamato (altrimenti genera un ciclo infinito).
    Si noti che il dipendente non è contenuto in Liste dei dipendenti.
    In questo caso tutto deve funzionare.
  2. department.getEmployeeList è non ancora inverosimile ed è pigro caricato ora
    esso contiene entità gestita: merge deve fare nulla in quanto dipendenti # sono già gestite e cascata su reparto è stato già chiamato.
    Ma ..

In questo caso, è dipendente contenuta in employeeList?

tre possibilità (a seconda delle altre variabili come filo-mode, auto-commit, ...):

  1. no: tutto dovrebbe funzionare
  2. sì, ed è la stessa istanza: tutto dovrebbe funzionare
  3. sì, ma è un esempio diverso: probabilmente porta a cambiamenti sovrascrivere

penso che il "bug" potrebbe essere qui, quando caricamento pigro: non dovrebbe essere possibl e per avere due istanze gestite della stessa entità nello stesso EntityManager.

Non riesco a pensare a un singolo controesempio.

+1

Che si tratti di un bug o meno, ma la comunità del software aggiunge nuove funzionalità abbastanza in tempo ma non si preoccupa molto di correggere bug, se ce ne sono :) – Tiny

+1

Hai ragione, in particolare per EclipseLink. Forse un giorno o l'altro collasserà in un buco nero, inghiottendo tutte le nostre applicazioni ... –

Problemi correlati