2009-05-07 15 views
27

Ho un metodo che fa un sacco di cose; tra di loro facendo un numero di inserti e aggiornamenti. È dichiarato così ...esiste un modo per forzare un rollback transazionale senza incontrare un'eccezione?

@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, readOnly = false) 
public int saveAll(){ 
//do stuff; 
} 

Funziona esattamente come dovrebbe e non ho problemi con esso. Ci sono comunque situazioni in cui voglio forzare il rollback nonostante non ci sia un'eccezione ... al momento, sto forzando un'eccezione quando incontro le giuste condizioni, ma è brutto e non mi piace.

Posso richiamare attivamente il rollback in qualche modo? L'eccezione lo chiama ... sto pensando che forse posso farlo anch'io.

+0

controllo http://static.springsource.org /spring/docs/2.0.x/reference/transaction.html sezione 9.5.3 – shrini1000

risposta

15

Chiama setRollbackOnly() su SessionContext se sei in un EJB.

è possibile iniettare SessionContext in questo modo:

public MyClass { 
    @Resource 
    private SessionContext sessionContext; 

    @Transactional(propagation = Propagation.REQUIRED, 
        isolation = Isolation.DEFAULT, 
        readOnly = false) 
    public int saveAll(){ 
     //do stuff; 
     if(oops == true) { 
      sessionContext.setRollbackOnly(); 
      return; 
     } 
    } 

setRollbackOnly() è membro di EJBContext. SessionContext estende EJBContext: http://java.sun.com/j2ee/1.4/docs/api/javax/ejb/SessionContext.html Nota è disponibile solo negli EJB di sessione.

@Resource è un'annotazione Java EE standard, quindi è consigliabile verificare la configurazione in Eclipse. Ecco un example su come iniettare SessionContext utilizzando @Resource.

Ho il sospetto che questa non sia probabilmente la soluzione, dal momento che sembra che tu non stia lavorando con gli EJB, spiegando perché Eclipse non sta trovando @Resource.

In questo caso, sarà necessario interagire direttamente con la transazione, consultare il modello di transazione.

+0

Eclipse non riconosce @Resource come annotazione e setRollbackOnly() non è un membro di SessionContext (quando importa da org.springframework.orm .hibernate3.SpringSessionContext). Quindi ... sono più vicino, ma non abbastanza vicino :) –

+0

Grazie per la risposta ... – user961690

3

È necessario iniettare a molla il gestore delle transazioni. Quindi puoi semplicemente chiamare il metodo di rollback su di esso.

+0

che sembra il modo giusto per andare ... Sto indagando su SavepointManager ora. –

+0

questo sta causando un'eccezione alla fine "Transazione ripristinata perché è stata contrassegnata come solo rollback" – Guillaume

17

Nelle transazioni di primavera, si utilizza TransactionStatus.setRollbackOnly().

Il problema che hai qui è che stai utilizzando @Transactional per delimitare le tue transazioni. Questo ha il vantaggio di essere non invasivo, ma significa anche che se vuoi interagire manualmente con il contesto della transazione, non puoi.

Se si desidera un controllo rigoroso dello stato della transazione, è necessario utilizzare le transazioni programmatiche piuttosto che le annotazioni dichiarative. Ciò significa utilizzare Spring's TransactionTemplate o utilizzare direttamente PlatformTransactionManager. Vedere la sezione 9.6 del manuale di riferimento Spring.

Con TransactionTemplate, si fornisce un oggetto di richiamata che implementa TransactionCallback e il codice in questa richiamata ha accesso agli oggetti TransactionStatus.

Non è bello come lo @Transactional, ma si ottiene un controllo più preciso dello stato del proprio tx.

+4

Utilizzerebbe 'TransactionInterceptor.currentTransactionStatus(). SetRollbackOnly()' è un modo di interagire manualmente con la transazione in il mezzo di un metodo che usa '@ Transactional'? Principalmente per la sostituzione dei metodi EJB 'setRollbackOnly'. Sarebbe interessato se hai un commento su questo. Grazie! –

-3

Lanciare un'eccezione e utilizzare il framework come progettato altrimenti non utilizzare la gestione dichiarativa delle transazioni e seguire skaffman advise sopra. Mantienilo semplice.

2

Ho metodi di servizio annotati con @Transactional.Quando la convalida fallisce e ho già un'entità collegata all'unità di lavoro corrente, utilizzo sessionFactory.getCurrentSession().evict(entity) per assicurarmi che nulla venga scritto nel database. In questo modo non ho bisogno di lanciare un'eccezione.

+0

Questa potrebbe essere una soluzione di compromesso in una lunga sessione. – armysheng

15

Non utilizziamo EJB, ma Spring semplice e abbiamo scelto l'approccio AOP. Abbiamo implementato la nuova annotazione @TransactionalWithRollback e utilizzato AOP per avvolgere quei metodi annotati con i consigli "intorno". Per attuare il consiglio che usiamo menzionato TransactionTemplate. Questo significa un po 'di lavoro all'inizio, ma come risultato possiamo semplicemente annotare un metodo con @TransactionalWithRollback come se usassimo @Transactional negli altri casi. Il codice principale sembra pulito e semplice.

// 
// Service class - looks nice 
// 
class MyServiceImpl implements MyService { 
    @TransactionalWithRollback 
    public int serviceMethod { 
     // DO "read only" WORK 
    } 
} 

// 
// Annotation definition 
// 
@Retention(RetentionPolicy.RUNTIME) 
@Target({ElementType.METHOD}) 
public @interface TransactionalWithRollback { 
} 

// 
// the around advice implementation 
// 
public class TransactionalWithRollbackInterceptor { 
    private TransactionTemplate txTemplate; 
    @Autowired private void setTransactionManager(PlatformTransactionManager txMan) { 
     txTemplate = new TransactionTemplate(txMan); 
    } 

    public Object doInTransactionWithRollback(final ProceedingJoinPoint pjp) throws Throwable { 
     return txTemplate.execute(new TransactionCallback<Object>() { 
      @Override public Object doInTransaction(TransactionStatus status) { 
       status.setRollbackOnly(); 
       try { 
        return pjp.proceed(); 
       } catch(RuntimeException e) { 
        throw e; 
       } catch (Throwable e) { 
        throw new RuntimeException(e); 
       } 
      } 
     }); 
    } 
} 

// 
// snippet from applicationContext.xml: 
// 
<bean id="txWithRollbackInterceptor" class="net.gmc.planner.aop.TransactionalWithRollbackInterceptor" /> 

<aop:config> 
    <aop:aspect id="txWithRollbackAspect" ref="txWithRollbackInterceptor"> 
     <aop:pointcut 
      id="servicesWithTxWithRollbackAnnotation" 
      expression="execution(* org.projectx..*.*(..)) and @annotation(org.projectx.aop.TransactionalWithRollback)"/> 
     <aop:around method="doInTransactionWithRollback" pointcut-ref="servicesWithTxWithRollbackAnnotation"/> 
    </aop:aspect> 
</aop:config> 
+0

+1 per aver trovato il tempo di rispondere a una domanda che ho chiesto 4 anni fa e che ha già una risposta accettata più volte in upvoted! :) –

+0

Mi piace che le cose siano complete :-) – Jakub

10

questo funziona per me:

TransactionInterceptor.currentTransactionStatus().setRollbackOnly(); 
Problemi correlati