Transazioni con Spring e JPA

1. Panoramica

Questo tutorial discuterà il modo giusto per configurare le transazioni Spring , come utilizzare l' annotazione @Transactional e le insidie ​​comuni.

Per una discussione più approfondita sulla configurazione della persistenza principale, dai un'occhiata al tutorial Spring with JPA.

Fondamentalmente, ci sono due modi distinti per configurare le transazioni - annotazioni e AOP - ciascuno con i propri vantaggi. Discuteremo qui la più comune annotation-config.

2. Configurare le transazioni

Spring 3.1 introduce l' annotazione @EnableTransactionManagement che possiamo usare in una classe @Configuration e abilitare il supporto transazionale:

@Configuration @EnableTransactionManagement public class PersistenceJPAConfig{ @Bean public LocalContainerEntityManagerFactoryBean entityManagerFactoryBean(){ //... } @Bean public PlatformTransactionManager transactionManager(){ JpaTransactionManager transactionManager = new JpaTransactionManager(); transactionManager.setEntityManagerFactory( entityManagerFactoryBean().getObject() ); return transactionManager; } }

Tuttavia, se stiamo utilizzando un progetto Spring Boot e abbiamo dipendenze spring-data- * o spring-tx sul classpath, la gestione delle transazioni sarà abilitata per impostazione predefinita .

3. Configurare le transazioni con XML

Prima della 3.1 o se Java non è un'opzione, ecco la configurazione XML, utilizzando il supporto per annotazioni e spazio dei nomi:

4. L' annotazione @Transactional

Con le transazioni configurate, ora possiamo annotare un bean con @Transactional a livello di classe o di metodo:

@Service @Transactional public class FooService { //... }

L'annotazione supporta anche ulteriori configurazioni :

  • il tipo di propagazione della transazione
  • il livello di isolamento della transazione
  • un Timeout per l'operazione inclusa nella transazione
  • un flag readOnly - un suggerimento per il provider di persistenza che la transazione dovrebbe essere di sola lettura
  • le regole di rollback per la transazione

Si noti che, per impostazione predefinita, il rollback viene eseguito solo per le eccezioni di runtime, non controllate. L'eccezione selezionata non attiva un rollback della transazione. Possiamo, ovviamente, configurare questo comportamento con i parametri di annotazione rollbackFor e noRollbackFor .

5. Potenziali insidie

5.1. Transazioni e deleghe

Ad un livello alto, Spring crea proxy per tutte le classi annotate con @Transactional , sia sulla classe che su uno qualsiasi dei metodi. Il proxy consente al framework di iniettare logica transazionale prima e dopo il metodo in esecuzione, principalmente per l'avvio e il commit della transazione.

Ciò che è importante tenere a mente è che, se il bean transazionale sta implementando un'interfaccia, per impostazione predefinita il proxy sarà un Java Dynamic Proxy. Ciò significa che verranno intercettate solo le chiamate a metodi esterni che arrivano tramite il proxy. Qualsiasi chiamata di auto-invocazione non avvierà alcuna transazione, anche se il metodo ha l' annotazione @Transactional .

Un altro avvertimento sull'uso dei proxy è che solo i metodi pubblici dovrebbero essere annotati con @Transactional. I metodi di qualsiasi altra visibilità ignoreranno semplicemente l'annotazione in silenzio poiché non sono proxy.

Questo articolo discute ulteriori insidie ​​proxy in grande dettaglio qui.

5.2. Modifica del livello di isolamento

Possiamo anche modificare il livello di isolamento della transazione:

@Transactional(isolation = Isolation.SERIALIZABLE)

Notare che questo è stato effettivamente introdotto nella primavera 4.1; se eseguiamo l'esempio precedente prima della Spring 4.1, il risultato sarà:

org.springframework.transaction.InvalidIsolationLevelException : JPA standard non supporta livelli di isolamento personalizzati: utilizza un JpaDialect speciale per l'implementazione JPA

5.3. Transazioni di sola lettura

Il flag readOnly di solito genera confusione, specialmente quando si lavora con JPA; dal Javadoc:

Questo serve solo come suggerimento per il sottosistema di transazione effettivo; esso non necessariamente causare fallimento dei tentativi di accesso in scrittura. Un gestore delle transazioni che non può interpretare il suggerimento di sola lettura non genererà un'eccezione quando viene richiesta una transazione di sola lettura.

Il fatto è che non possiamo essere sicuri che un inserimento o un aggiornamento non avvenga quando è impostato il flag readOnly . Questo comportamento dipende dal fornitore, mentre JPA è indipendente dal fornitore.

È anche importante capire che il flag readOnly è rilevante solo all'interno di una transazione. Se un'operazione si verifica al di fuori di un contesto transazionale, il flag viene semplicemente ignorato. Un semplice esempio di ciò chiamerebbe un metodo annotato con:

@Transactional( propagation = Propagation.SUPPORTS,readOnly = true )

da un contesto non transazionale - non verrà creata una transazione e il flag readOnly verrà ignorato.

5.4. Registrazione delle transazioni

Un metodo utile per comprendere i problemi relativi alle transazioni è l'ottimizzazione della registrazione nei pacchetti transazionali. Il pacchetto rilevante in Spring è " org.springframework.transaction", che dovrebbe essere configurato con un livello di registrazione TRACE.

6. Conclusione

Abbiamo coperto la configurazione di base della semantica transazionale utilizzando sia Java che XML, come utilizzare @Transactional e le migliori pratiche di una strategia transazionale.

Come sempre, il codice presentato in questo articolo è disponibile su Github.