2010-01-05 12 views
13

Tutto ciò che so di questa eccezione è da Spring's documentation e alcuni post del forum con sviluppatori frastagliati che incollano enormi tracce di stack e nessuna risposta.UnexpectedRollbackException - un'analisi completa dello scenario

Dalla documentazione della Primavera:

generata quando un tentativo di commettere una transazione ha comportato un rollback inaspettato

voglio capire una volta per tutte

  1. Esattamente cosa lo causa?

    • Dove si è verificato il rollback? nel codice dell'App Server o nel database?
    • È stato causato da una specifica eccezione di base (ad esempio qualcosa da java.sql. *)?
    • E 'collegato a Hibernate? È collegato a Spring Transaction Manager (non JTA nel mio caso)?
  2. Come evitarlo? c'è qualche buona pratica per evitarlo?

  3. Come eseguire il debug? sembra difficile da riprodurre, qualche modo provato per risolverlo?
+0

Quel particolare viene generata un'eccezione solo in determinate circostanze specifiche, da dentro l'infrastruttura Spring TX. Quale messaggio era contenuto all'interno di 'UnexpectedRollbackException'? Questo ci aiuterà a rintracciarlo. – skaffman

risposta

8

Scorri un po 'più indietro nel registro (o aumenta la dimensione del buffer) e vedrai cosa ha causato esattamente l'eccezione.

Se non ci fosse, controllare i metodi getMostSpecificCause() e getRootCause() di UnexpectedRollbackException - potrebbero essere utili.

+4

Se i registri (con una maggiore dimensione del buffer) contenevano la causa esatta, non lo avrei chiesto qui. Voglio capire gli scenari in cui viene generata questa eccezione. Perché la mia configurazione è pertinente? una configurazione Hibernate + Spring molto comune con HibernateTransactionManager dal modo in cui –

+1

è un errore comune saltare la causa principale dei rollback delle transazioni, perché è molto indietro nello stacktrace e talvolta vi sono query sql in uscita. Ho chiesto il tuo setup perché altrimenti io (o chiunque altro) non posso indovinare cosa potrebbe effettivamente causare questo. Ad ogni modo, controlla la mia risposta aggiornata per un altro suggerimento. – Bozho

+0

@Bozho - +1 per l'aggiornamento, per accettare una risposta, tuttavia, voglio capire se il rollback si è verificato nel database o nel server (ad esempio nel gestore delle transazioni software o nel database tramite un'eccezione JDBC) forse I ' Non sto facendo la domanda giusta? –

11

ho trovato questo per essere rispondendo il resto della domanda: https://jira.springsource.org/browse/SPR-3452

Credo che abbiamo bisogno di distinguere tra 'logico' operazione ambiti e le transazioni 'fisici' qui ...

Cosa PROPAGATION_REQUIRED crea è un ambito di transazione logico per ogni metodo a cui viene applicato. Ciascun ambito di transazione logica può decidere individualmente nello stato di rollback solo con lo stato della transazione esterna che è logicamente indipendente da l'ambito della transazione interna. Del corso , in caso di comportamento standard PROPAGATION_REQUIRED, essi verranno associati alla stessa transazione fisica . Quindi un indicatore di rollback solo impostato nello scope della transazione interna influisce sulla possibilità della transazione esterna di eseguire effettivamente il commit.Tuttavia, dal momento che l'ambito di transazione esterno non decidere su un rollback per sé, il rollback (in silenzio innescata dalla portata transazione interna) viene fornito inaspettato a quel livello - che è il motivo per cui un UnexpectedRollbackException si butta.

PROPAGATION_REQUIRES_NEW, al contrario, utilizza un transazione completamente indipendente per ciascun interessato ambito di transazione. In tal caso, le transazioni fisiche di base saranno diverse e potranno quindi eseguire il commit o il rollback in modo indipendente, con una transazione esterna non influenzata dallo stato di rollback della transazione interna .

PROPAGATION_NESTED è ancora diversa dal fatto di utilizzare una singola transazione fisica con più punti di salvataggio che possa ripristinare. Tali parziali rollbacks permettono un interno transazione scopo di innescare un ripristino per la sua portata, con la transazione esterno poter continuare la transazione fisica nonostante alcune operazioni essendo stato rotolato. Questo è tipicamente mappato sui punti di salvataggio JDBC, , quindi funzionerà solo con le transazioni J2BC transazioni (Spring's DataSourceTransactionManager).

Per completare la discussione: UnexpectedRollbackException può anche essere gettato senza l'applicazione mai aver impostato un rollback-unico marcatore sé. Invece, l'infrastruttura della transazione potrebbe aver deciso che l'unico risultato possibile è un rollback , a causa di vincoli nello stato corrente della transazione . Questo è particolarmente rilevante con le transazioni XA .

come ho suggerito sopra, un'eccezione al interno di transazione ambito, quindi la cattura di tale eccezione al l'ambito esterno e tradurlo in una chiamata in setRollbackOnly silenziosa ci dovrebbe funzionare per lo scenario. Un chiamante della transazione esterna non visualizzerà mai un'eccezione, quindi . Dal momento che si solo preoccuparsi di tali ripristini silenziosi a causa dei requisiti speciali imposti da un chiamante, vorrei anche sostengo che la corretta soluzione architettonica è quella di utilizzare le eccezioni all'interno il livello di servizio, e per tradurre tali eccezioni in rollback silenziosi al livello della facciata del servizio (a destra prima di tornare al chiamante speciale ).

Dal momento che il problema non è forse solo sulle eccezioni di rollback, ma piuttosto su eventuali eccezioni generate dal vostro livello di servizio, si potrebbe anche uso standard di eccezione-driven rollback tutta la strada in tutta si livello di servizio, e poi prendere e registrare tali eccezioni una volta che la transazione ha già completato, in qualche facciata servizio adattamento che traduce eccezioni del livello di servizio in specifici UI errore stati.

Juergen

3

Beh vi posso dire come riprodurre l'UnexpectedRollbackException. Stavo lavorando al mio progetto e ho ottenuto questa UnexpectedRollbackException nella seguente situazione. Sto avendo controller, servizi e livelli dao nel mio progetto. Quello che ho fatto è nella mia classe di servizio strato,

class SomeServiceClass { 
    void outerTransaction() { 
     // some lines of code 
     innerTransaction(); 
     //AbstractPlatformTransactionManager tries to commit but UnexpectedRollbackException is thrown, not here but in controller layer class that uses SomeServiceClass. 
    } 

    void innerTransaction() { 
     try { 
      // someDaoMethod or someDaoOperation that throws exception 
     } catch(Exception e) { 
      // when exception is caught, transaction is rolled back, outer transaction does not know about it. 
      // I got this point where inner transaction gets rolled back when I set HibernateTransactionManager.setFailEarlyOnGlobalRollbackOnly(true) 
      // FYI : use of following second dao access is wrong, 
      try { 
       // again some dao operation 
      } catch(Exception e1) { 
       throw e2; 
      } 
     } 
    } 
}