2010-06-14 12 views
39

abbiamo:Partendo nuova transazione in primavera fagioli

@Transactional(propagation = Propagation.REQUIRED) 
public class MyClass implementes MyInterface { ... 

MyInterface ha un solo metodo: go().

Quando go() viene eseguito, viene avviata una nuova transazione che esegue il commit/rollback quando il metodo è completo - questo va bene.

Ora diciamo in go() chiamiamo un metodo privato in MyClass che ha @Transactional(propagation = Propagation.REQUIRES_NEW. Sembra che Spring "ignori" l'annotazione REQUIRES_NEW e non avvii una nuova transazione. Credo che questo sia dovuto al fatto che Spring AOP opera a livello di interfaccia (MyInterface) e non intercetta alcuna chiamata ai metodi MyClass. È corretto?

C'è un modo per avviare una nuova transazione all'interno della transazione go()? È l'unico modo per chiamare un altro bean gestito Spring che ha transazioni configurate come REQUIRES_NEW?


Aggiornamento: Aggiungendo che quando i client eseguono go() lo fanno tramite un riferimento all'interfaccia, non la classe:

@Autowired 
MyInterface impl; 

impl.go(); 

risposta

66

Dalla riferimento Spring 2.5:

Quando si utilizza proxy, l'annotazione @Transactional deve essere applicato solo a metodi con visibilità pubblica. Se si annotano metodi protetti, privati ​​o di tipo pacchetto con l'annotazione @Transactional, non verrà generato alcun errore , ma il metodo annotato non mostrerà le impostazioni di transazione configurate .

Quindi Spring ignora l'annotazione @Transactional su metodi non pubblici.

Inoltre,

In modalità proxy (che è il default), unico metodo 'esterno' chiamate in arrivo attraverso il proxy sarà intercettato. Ciò significa che "autoinvocazione", , ovvero un metodo all'interno dell'oggetto target che chiama un altro metodo dell'oggetto target , non porterà a una transazione effettiva in fase di esecuzione anche se il metodo invocato è contrassegnato con @Transactional!

Quindi, anche se si esegue il metodo public, richiamandolo da un metodo della stessa classe non verrà avviata una nuova transazione.

È possibile utilizzare la modalità aspectj nelle impostazioni della transazione in modo che il codice relativo alla transazione sia intrecciato nella classe e che non venga creato alcun proxy in fase di esecuzione.

Vedere the reference document per ulteriori dettagli.

Un altro possibile modo per farlo è andare a prendere il proxy primavera della classe nella classe stesso e chiamare i metodi su di esso piuttosto che this:

@Service 
@Transactional(propagation = Propagation.REQUIRED) 
public class SomeService { 

    @Autowired 
    private ApplicationContext applicationContext; 

    private SomeService getSpringProxy() { 
     return applicationContext.getBean(this.getClass()); 
    } 

    private void doSomeAndThenMore() { 
     // instead of 
     // this.doSometingPublicly(); 
     // do the following to run in transaction 
     getSpringProxy().doSometingPublicly(); 
    } 

    public void doSometingPublicly() { 
     //do some transactional stuff here 
    } 

} 
37

@Transactional sarà notato solo se è su un metodo public, a causa del modo in cui Spring AOP funziona.

Tuttavia, è possibile programmatically start a new transaction se lo si desidera, utilizzando TransactionTemplate, ad es.

TransactionTemplate txTemplate = new TransactionTemplate(txManager);     
txTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW); 
txTemplate.execute(new TransactionCallback<Object>() { 
    public Object doInTransaction(TransactionStatus status) { 
     // do stuff 
    } 
}); 
+0

Ma anche se '@ Transactional' è su un metodo pubblico di MyClass, sembra ancora che Spring non lo rilevi ** a meno che ** il metodo sia definito nell'interfaccia - corretto? –

+0

Per aggiungere, questo è perché il client che sta eseguendo il metodo go() in questione ha un riferimento all'interfaccia, non alla classe. –

+3

@Marcus: Sort of. Se 'MyClass' implementa un'interfaccia, Spring utilizzerà solo quell'interfaccia per generare il proxy transazionale e ignorerà anche i metodi pubblici non di interfaccia. Tuttavia, se 'MyClass' non implementa alcuna interfaccia, verranno utilizzati tutti i metodi pubblici. – skaffman

3

In breve è necessario chiamare il metodo però proxy per raggiungere il comportamento transazionale. È possibile chiamare un "REQUIRES_NEW" nello stesso bean, come richiesto nella domanda. Per fare ciò devi fare un riferimento "autonomo". In primavera non è semplice. Devi iniettarlo con annotazione @Resource.

@Service("someService") 
public class ServieImpl implements Service { 

    @Resource(name = "someService") 
    Service selfReference; 

    @Transactional 
    public void firstMethod() { 
     selfReference.secondMethod(); 
    } 

    @Transactional(propagation = Propagation.REQUIRES_NEW) 
    public void secondMethod() {  
     //do in new transaction 
    } 

} 

L'invocazione a firstMethod chiama il proxy e non "questo" che dovrebbe rendere l'operazione "REQUIRES_NEW" al lavoro.

Problemi correlati