Ho due bean di entità definite come segue (roba non correlato rimosso):arresto Hibernate di aggiornare raccolte quando non hanno cambiato
@Entity
@Table(...)
public class MasterItem implements java.io.Serializable {
private Set<CriticalItems> criticalItemses = new HashSet<CriticalItems>(0);
@OneToMany(fetch = FetchType.EAGER, mappedBy = "masterItem", orphanRemoval = true,
cascade = {javax.persistence.CascadeType.DETACH})
@Cascade({CascadeType.SAVE_UPDATE, CascadeType.DELETE})
public Set<CriticalItems> getCriticalItemses() {
return this.criticalItemses;
}
}
CriticalItems è definito come segue:
@Entity
@Table(...)
public class CriticalItems implements java.io.Serializable {
private MasterItem masterItem;
@ManyToOne(fetch = FetchType.LAZY, optional = false,
cascade = {javax.persistence.CascadeType.DETACH})
@Cascade({CascadeType.SAVE_UPDATE})
@JoinColumn(name = "mi_item_id", nullable = false)
public MasterItem getMasterItem() {
return this.masterItem;
}
}
E nel mio Codice DAO - Ho questi metodi:
public MasterItem load(int id) {
MasterItem results = (MasterItem) getSessionFactory().getCurrentSession()
.get("com.xxx.MasterItem", id);
}
public void save(MasterItem master) {
// master has been changed by the UI since it
getSessionFactory().getCurrentSession().saveOrUpdate(master);
}
Quando carico un oggetto MasterItem, viene caricato correttamente, un d carica anche i CriticalItems Set con i dati, come indicato. Quindi, invio questi dati all'interfaccia utente e ottengo una copia aggiornata, che cerco quindi di mantenere. L'utente aggiorna i campi nell'oggetto MasterItem, ma non tocca il set di CriticalItems o qualcosa in esso contenuto - rimane invariato.
Quando viene richiamato il metodo save(), Hibernate richiede di inviare aggiornamenti SQL per ogni elemento nel Set di CriticalItems, anche se nessuno di essi è stato modificato in alcun modo.
Dopo aver scavato, ecco cosa penso stia succedendo. Quando eseguo un saveOrUpdate(), Hibernate vede il mio oggetto MasterItem in uno stato disconnesso, quindi tenta di ricaricarlo dal disco. Tuttavia, quando lo fa, sembra che stia usando un'istruzione preparata (che è stata creata automaticamente da Hibernate all'avvio) e questa istruzione preparata non tenta di unirsi ai dati di CriticalItems.
Quindi Hibernate ha il mio oggetto MasterItem aggiornato con un set completo di CriticalItems, ma usa un oggetto MasterItem senza raccolte come oggetto "previousState". Pertanto, tutti i CriticalItem vengono aggiornati tramite SQL (non inserito, che è di per sé interessante).
Ho fatto qualcosa nelle mie annotazioni che ha causato questo comportamento? So che posso usare un Interceptor per scoprire che l'oggetto è davvero cambiato, o cambiato il flag dirty per sovrascrivere l'algoritmo predefinito di Hibernate - ma questo sembra essere qualcosa che Hibernate dovrebbe gestire da solo senza il mio intervento.
Qualsiasi intuizione sarebbe apprezzata.
UPDATE: Sulla base di commenti, credo di capire la differenza tra saveOrUpdate() e unire(). Mi rendo conto che saveOrUpdate() comporterà in entrambi i casi un INSERT SQL o un UPDATE SQL e che, in teoria, l'unione genererà aggiornamenti solo se l'oggetto è cambiato dal suo stato persistente, ma per determinarlo, Hibernate deve ricaricare l'oggetto prima tramite SQL SELECT.
Quindi, ho pensato di poter tornare al mio codice e modificare saveOrUpdate() per unire() e funzionerebbe, ma non era proprio il caso.
quando ho usato merge(), mi è stato sempre
org.springframework.orm.hibernate3.HibernateSystemException: could not initialize proxy - no Session; nested exception is org.hibernate.LazyInitializationException: could not initialize proxy - no Session
ma ha funzionato bene se ho cambiato di nuovo a saveOrUpdate().
Ho finalmente scoperto perché - Non ho incluso CascadeType.MERGE
nella mia annotazione @Cascade
(ugh). Una volta risolto, l'eccezione è andata via.
avete una colonna di blocco ottimistica (una colonna annotata con @version con tipo data od int) – lweller