2015-11-28 19 views
5

È possibile aggiornare l'entità nel database senza modificare la versione dell'entità utilizzando l'ibernazione?È possibile disattivare l'incremento della versione di ibernazione per un particolare aggiornamento?

Utilizzando la mia applicazione Web, gli utenti possono creare o aggiornare entità. E dove c'è un altro processo asincrono che "elabora" queste entità dopo ogni operazione dell'utente. Se l'utente apre l'entità per l'aggiornamento prima che l'entità sia "elaborata", ma tenta di salvarla dopo che è stata "elaborata", l'utente otterrà "OptimisticLockException" e tutti i dati inseriti verranno persi. Ma vorrei sovrascrivere i dati aggiornati in processo asincrono con i dati forniti dall'utente.

pezzo di codice presentato per dimostrare il motivo per cui ho bisogno di tale comportamento (JPA + Hibernate):

//user creates entity by filling form in web application 
Entity entity = new Entity(); 
entity.setValue("some value"); 
entity.setProcessed (false); 
em.persist(entity); 
em.flush(); 
em.clear(); 

//but after short period of time user changes his mind and again opens entity for update 
entity = em.find(Entity.class, entity.getId()); 
em.clear(); //em.clear just for test purposes 

//another application asynchronously updates entities 
List entities = em.createQuery(
       "select e from Entity e where e.processed = false") 
       .getResultList(); 
for (Object o: entities){ 
    Entity entityDb = (Entity)o; 
    someTimeConsumingProcessingOfEntityFields(entityDb); //update lots of diferent entity fields 
    entityDb.setProcessed(true); 
    em.persist(entityDb); 
}   
em.flush(); //version of all processed entities are incremented.  
//Is it possible to prevent version increment? 
em.clear(); 

//user modifies entity in web application and again press "save" button 
em.merge(entity); //em.merge just for test purposes 
entity.setValue("some other value"); 
entity.setProcessed (false); 
em.persist(entityDb); 
em.flush(); //OptimisticLockException will occur.   
//Is it possible to prevent this exception from happening? 
//I would like to overwrite data updated in asynchronous process 
//with user provided data. 

E il mio soggetto:

@Entity 
@Table(name = "enities") 
public class Entity implements Serializable { 
    @Id 
    @GeneratedValue(strategy = GenerationType.AUTO) 
    private Long id; 
    @Version 
    private int versionNum; 
    @Column 
    private String value 
    @Column 
    private boolean processed; 
    //… and so on (lots other properties) 
} 

In realtà ho molto di più classi con problemi simili - così ho Sto cercando una soluzione elegante non invadente.

Mi sembra questo scenario abbastanza usuale. Ma non sono riuscito a trovare alcuna informazione su come ottenere tale funzionalità.

risposta

0

Hibernate blocco ottimistico può essere bypassato utilizzando hibernate Session (http://docs.jboss.org/hibernate/orm/5.0/javadocs/org/hibernate/Session.html) replicare (...) Metodo.

Esempio di codice che non incrementa la versione:

//Detaching to prevent hibernate to spot dirty fields. 
//Otherwise if entity exists in hibernate session replication will be skipped 
//and on flush entity will be updated with version increment. 
em.detach(entityDb); 
someTimeConsumingProcessingOfEntityFields(entityDb); 

//Telling hibernate to save without any version modifications. 
//Update hapends only if no newer version exists. 
//Executes additional query DB to get current version of entity. 
hibernateSession.replicate(entity, ReplicationMode.LATEST_VERSION); 

Penso che questa soluzione è migliore di aggiornamento SQL nativo, perché:

  • mappature ORM (anotations o * .hbm.xml) sono usi (non è necessario duplicare oggetti Java < -> Mappatura tabelle DB in query native);
  • non è necessario eseguire manualmente flush (prestazioni);
  • Le cache db e hibernate sono nello stesso stato, non è necessario rimuovere le entità dalla cache (perfomance);
  • e hai ancora tutte le funzionalità fornite da ORM come il blocco ottimistico e così via ...;
0

La versione verrà sempre incrementata (a meno che non si utilizzi JPQL). L'idea di una versione è di tenere traccia delle modifiche e, nel caso di JPA, anche di abilitare il blocco ottimistico in modo che le applicazioni possano lavorare contemporaneamente con le stesse entità.

Ma per aggirare questo problema è possibile leggere l'ultima versione dal database e applicare tutti i valori dalla versione dell'entità su cui l'utente ha lavorato.

Qualcosa di simile a questo:

void updateWithUserInput(Entity userVersion) { 
    Entity dbVersion = entityManager.find(Entity.class, userVersion.getId); 
    // apply changes... 
    dbVersion.setX(userVersion.getX()); 
    // ...and so on 
    // Also: We're dealing with an attached entity so a merge call should not be necessary 
} 

Suppongo che tu rendi conto che questo scarta eventuali modifiche eseguite dalla chiamata asincrona, rendendo così obsoleto così si potrebbe anche non farlo e non avrebbe bisogno di sovrascrivere eventuali modifiche. :)

+0

Questa soluzione disattiverà completamente il controllo di blocco ottimistico, quindi più utenti modificano la stessa entità utilizzando l'interfaccia web, ma non voglio che ciò accada. Il problema non è quindi l'utente aggiorna l'entità, ma il processo asincrono aggiorna l'entità. Quindi penso di aver bisogno in qualche modo di modificare l'aggiornamento dell'entità nel processo asincrono. – Palladium

+1

È inoltre possibile associare l'entità a una classe diversa senza l'attributo di versione e utilizzarla esclusivamente nella chiamata asincrona. – Brian

+0

Sì, funzionerebbe, ma sfortunatamente ciò richiederebbe un sacco di refactoring extra (ridisegnare completamente le mie classi di dominio). Nella mia applicazione tutte le classi di dominio (più di 100 classi) estende la super classe con la versione fieldNum e una parte significativa di esse viene aggiornata con alcuni processi asincroni. Quindi cambiare dominio per questa particolare ragione sarebbe una soluzione molto "intrusiva". In tal caso, penso che sarebbe più facile sovrascrivere la classe DefaultFlushEntityEventListener di hibernate con possibilità di girare l'incremento della versione in alcuni casi (ma ciò significa "ramificazione" di ibernazione). – Palladium

0

È possibile bypass Sospensione meccanismo di blocco ottimistico emettendo un aggiornamento di query nativo per l'elaborazione asincrona:

em 
    .createNativeQuery("update entities set processed = :1 where id =:2") 
    .setParameter(1, true) 
    .setParameter(2, entityDb.getId()) 
    .executeUpdate(); 
+0

someTimeConsumingProcessingOfEntityFields (entityDb) aggiorna più campi complessi dell'entità di classe, quindi sarebbe piuttosto difficile duplicare tutto questo codice utilizzando le query native. (In realtà ho molte più lezioni con problemi simili - quindi sto cercando una soluzione elegante e non invadente). E il secondo problema con l'utilizzo di query native con ibernazione lo lascia oggetto di dominio in stato "incoerente". – Palladium

+0

E il secondo problema con l'utilizzo di query native con ibernazione: lascia oggetto di dominio nello stato "incoerente" (dopo gli aggiornamenti di query native gli oggetti di dominio direttamente nella sessione di ibernazione DB conterranno valori di oggetti diversi). Questo può essere un problema per la riusabilità del codice (non sarà possibile passare gli oggetti correnti ad altri metodi - in primo luogo è necessario aggiornarli dal DB) – Palladium