2011-10-25 13 views
6

Sto avendo la cosa più strana che accade e non riesco a capire perché. Il modo migliore per descrivere questo è quello di fornire un esempio semplicistico:L'eccezione si propaga dopo essere stato catturato

@Service 
@Transactional 
public class Foo{ 
    public ModelAndView delete(@ModelAttribute("abc") Long id) { 
     ModelAndView mav = new ModelAndView(); 
     try { 
      getDaoService().delete(id); //Calls Bar.delete() 
     } catch (final Exception e) { 
      // Add a custom error message to the mav for the user to see 
      mav.getModelMap().addAttribute(blah, blah); 
     } 
     return mav; 
    } 
} 

@Service 
@Transactional 
public class Bar { 
    public void delete(final E entity) throws HibernateException { 
     if (null != entity) { 
      try { 
       sessionFactory.getCurrentSession().delete(entity); 
      } finally { 
       sessionFactory.getCurrentSession().flush(); 
      } 
     } 
    } 
} 

In questo caso particolare, sto cercando di eliminare un oggetto che ha una violazione di vincolo (ORA-02.292). Mi aspetto che l'eliminazione fallisca a causa di questo. Quando l'eliminazione fallisce, desidero mostrare all'utente un messaggio personalizzato appropriato.

Invece di essere in grado di mostrare all'utente un messaggio personalizzato, la chiamata non riesce e visualizza il seguente sullo schermo:

org.springframework.transaction.UnexpectedRollbackException: Transaction rotolato indietro perché è stato contrassegnato come solo rollback

Quando utilizzo un debugger, è possibile verificare che l'errore sia stato rilevato correttamente e che l'oggetto ModelAndView abbia il messaggio personalizzato al suo interno. Quindi, non ho idea del perché venga lanciata un'eccezione dopo che è stata catturata e gestita. Qualcuno ha idea del perché questo sta accadendo?

risposta

5

Nell'annotazione @Transactional, è possibile specificare se annullare la transazione a causa di una determinata eccezione utilizzando l'attributo noRollbackForClassName. Puoi farlo in modo simile a questo.

@Service 
@Transactional(noRollbackForClassName = "java.lang.Exception") 
public class YourClass { 
    ... 
} 

Si noti tuttavia che solo dicendo noRollbackForClassName = "java.lang.Exception" significherebbe non sarà rollback alcuna eccezione (o delle sue sottoclassi), quindi la sua non è una buona pratica.

Quello che dovresti fare è capire quale eccezione sia effettivamente lanciata per prima (può essere stampando lo e.getClass().getName()), quindi impostare il nome della classe come valore noRollbackForClassName.

Ragione saggio, questo sta accadendo perché se qualche eccezione viene generata durante il tentativo di eliminare(), la transazione corrente viene contrassegnato automaticamente come rollback solo, e se si cerca di essere impegnati, l'eccezione che vedete verrà generata . Il modo per superare questo è di dichiarare esplicitamente che questa certa eccezione non dovrebbe causare un rollback.

+0

Impressionante, ha funzionato come un fascino! Grazie!!! – user973479

1

L'eccezione viene lanciata in Bar # delete e viene rilevata in Foo # delete. C'è un'annotazione @Transactional su Bar # delete che viene attraversata prima che venga rilevata l'eccezione. Questa transazione interna partecipa alla transazione esterna e pertanto l'intera transazione viene contrassegnata per il rollback.

Per evitare questo, è possibile rimuovere l'annotazione @Transactional per Bar # delete. Questo metodo è già chiamato nell'ambito dell'altra transazione.

+0

Fantastico, ha funzionato come un fascino! Grazie!!! – user973479

4

Il problema è dovuto al fatto che una volta generata un'eccezione, Spring segna internamente il tx solo come rollback. Questo è completamente separato dalla gestione delle eccezioni Java. Sono disponibili diverse opzioni:

  • Assicurarsi che l'eccezione prevista non generi eccezioni che si estendono RuntimeException; Spring restituisce solo tx's quando è un tipo RuntimeException (see this page, sezione 10.5.3). HibernateException extends RuntimeException, ecco perché stai ricevendo il marker di rollback.
  • Eseguire ogni tx nella propria transazione spostando il metodo transazionale nella propria classe e annotandolo utilizzando @Transactional(propagation=Propagation.REQUIRES_NEW).Quindi ogni chiamata verrà eseguita nel proprio tx e non influenzerà il tx generale.
  • Utilizzare lo stile noRollbackForClassName menzionato. Ma usa con cautela, per la ragione menzionata.
+0

Quindi, cosa posso fare quando l'eccezione estende RuntimeException? – sebnukem

0

Aggiungi proprietà "globalRollbackOnParticipationFailure" alla definizione di fagioli hibernateTransactionManager come segue.

<bean id="hibernateTransactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"> 
    <property name="sessionFactory" ref="hibernateSessionFactory" /> 
    **<property name="globalRollbackOnParticipationFailure" value="false" />** 
</bean> 
Problemi correlati