2014-08-27 16 views
7

Logic Controller:Come risolvere StaleObjectStateException con JPA e Hibernate

def updateObject() { 

    Object o = Object.get(params.id as Long) 

    o.otherObjects.clear() 

    objectDataService.saveObject(o.id) 

    OtherObject newObject = new OtherObject; 

    o.addToOtherObjects(newObject) 

    objectDataService.saveObject(o.id) 

} 

ServiceLogic

def saveObject(long profileId) { 
    o.save(flush:true) 
} 

cosa succede

nel 90% dei casi questo sarà solo di lavoro.

problemi

ERROR errors.GrailsExceptionResolver - StaleObjectStateException occurred when processing request: [GET] /controller/updateObject - parameters: 
stuff[]: data 
Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect) : [com.path.Object#1]. 
Stacktrace follows: 
Message: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect) : [com.path.Object#1] 

Ho letto attraverso domande relative ed ha trovato le merge chiamata che vedete qui sopra. ha risolto circa il 50% dei casi, ma non tutti.

+0

non sarebbe meglio qui se si spostava questa logica in un metodo di servizio in modo che fosse tutto fatto con la stessa transazione? –

+0

Non voglio sovraccaricare il servizio. pensi che avrebbe senso? –

+0

È sempre meglio spostare la logica di business sui servizi e rendere i controller più sottili possibile (per sottigliezza intendo con meno linee di codice). Ciò ti aiuterà a riutilizzare il codice ovunque richiesto e a risparmiare molto tempo anche nei test. –

risposta

2

Per noi alcuni approcci differenti finalmente risolto lo StaleObjectException da verificano regolarmente:

object = object.refresh() 

rinfrescante oggetti dopo il recupero li risolto la maggior parte dei nostri StaleObjectExceptions. Soprattutto nei casi in cui c'era la possibilità che qualcuno avrebbe lavorato sullo stesso oggetto da qualche altra parte e cambiato alcuni dei suoi membri (la maggior parte dei problemi arrivavano con i membri della raccolta).

complessiva stabilità del progetto:

wrongly linked resources 

abbiamo avuto un 404 su un file di risorse specifiche in realtà non abbiamo bisogno e quindi ignorato per qualche tempo. scopre che il file mancante farebbe sì che la sessione sia mantenuta aperta, realizzando quindi StaleObjects a destra e sinistra.

dunque come un suggerimento a chiunque di fronte a più dei soliti (alcune StaleObjects potrebbero sempre si verificano - vedi sopra risposte) StaleObjectExceptions: Assicurarsi che tutti risorse sono collegate correttamente e gli strumenti di sviluppo (Chrome F12) non lo fanno segnalare eventuali file mancanti.

4

StaleObjectStateException può verificarsi in qualsiasi altro progetto. Ogni volta che due transazioni concorrenti caricano la stessa versione dell'entità e ciascuna di esse modifica quell'entità si finisce con il fallimento dell'ultimo thread in esecuzione, a causa di un errore di blocco ottimistico della collisione.

Nel tuo caso c'è una chiamata in più, al di fuori dei confini JPA, che possono facilitare questo problema:

Object o = PObject.lock(profileId) 

Ogni transazione è thread-bound e succede all'interno di una sessione, quindi due thread in competizione manterrà due riferimenti a oggetti per lo stesso ID entità. L'obiettivo di blocco ottimistico funziona in modo efficace senza alcun altro meccanismo di blocco esplicito.

Se si invia il riferimento di entità modificata alla versione saveObject e si ottiene comunque questa eccezione, significa che si ha una possibilità elevata per due thread in competizione di modificare le stesse entità.

In questo scenario, un pessimistic lock produrrà risultati migliori, poiché implica l'attesa rispetto allo optimistic fail-fast approach.

+0

ho aggiornato il mio codice. la chiamata .lock() non è più in là. Ho ancora l'occorrenza circa il 20% delle volte delle chiamate. non è concomitante (lo sto fabbricando da solo come utente singolo, quindi nessuna modifica simultanea) –

5

StaleObjectStateException:

Come già commentata su questa eccezione. Quando un oggetto dominio versione-server non valido si trova nella sessione durante uno svuotamento (esplicito o implicito), il numero di versione viene eseguito un urto ma non viene mantenuto nel database. Quindi, quando diventa nuovamente valido e salvato e svuotato, l'ibernazione lo considera obsoleto, poiché il numero di versione non corrisponde alla versione nel database e genera uno StaleObjectStateException .

Questo può essere risolto come.

  1. devi scoprire il valore versione prima di fare l'aggiornamento, se è 1, allora si deve aggiornare utilizzando il programma. in particolare l'operazione di aggiornamento.
  2. Utilizzando l'annotazione @version.

Circa @version:

Il meccanismo numero di versione per il blocco ottimistico è fornita attraverso un annotazione @Version. Esempio: Il @Version annotazione

@Entity 
public class Flight implements Serializable { 
... 
    @Version 
    @Column(name="OPTLOCK") 
    public Integer getVersion() { ... } 
} 

Qui, la proprietà versione è mappata alla colonna OPTLOCK, e il gestore di entità usa per rilevare gli aggiornamenti in conflitto e prevenire la perdita di aggiornamenti che sarebbero sovrascritto da una strategia di vincita dell'ultimo momento @version

Per ulteriori dettagli su Grails, fare riferimento al collegamento sottostante, sta avendo il codice hub Git.
test for GRAILS-8937: HibernateOptimisticLockingFailureException

+0

potresti elaborare le tue soluzioni? come utilizzerei l'annotazione @version e cosa aggiornerei in 1a prima soluzione? –

+1

@ SebastianFlückiger: quelli sono l'approccio per risolvere quell'eccezione, controllare più dettagli sulla struttura di ibernazione per capire l'utilizzo della versione. Ho aggiornato anche alcuni dettagli sulla versione. Detto altro dice sulla soluzione risolta di soluzione di Lotus.1st per leggere la versione n. dalla tabella e quindi aggiornare quella versione scrivendo il codice. – Rudra

Problemi correlati