Tranzacții cu Spring și JPA

1. Prezentare generală

Acest tutorial va discuta despre modul corect de configurare a Tranzacțiilor de primăvară , despre cum să utilizați adnotarea @Transactional și capcanele comune.

Pentru o discuție mai aprofundată despre configurația de bază a persistenței, consultați tutorialul Spring with JPA.

Practic, există două moduri distincte de configurare a Tranzacțiilor - adnotări și AOP - fiecare cu propriile avantaje. Vom discuta aici cea mai comună adnotare-config.

2. Configurați tranzacțiile

Spring 3.1 introduce @EnableTransactionManagement adnotarea pe care le putem folosi într - o @Configuration clasă și pentru a permite suport tranzacțional:

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

Cu toate acestea, dacă folosim un proiect Spring Boot și avem dependențe spring-data- * sau spring-tx pe clasa, atunci gestionarea tranzacțiilor va fi activată implicit .

3. Configurați tranzacțiile cu XML

Înainte de 3.1 sau dacă Java nu este o opțiune, iată configurația XML, folosind adnotarea și suportul spațiului de nume:

4. Adnotarea @Transactional

Cu tranzacțiile configurate, acum putem adnota un bean cu @Transactional fie la nivel de clasă, fie la nivel de metodă:

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

Adnotarea acceptă și alte configurări :

  • tipul de propagare al tranzacției
  • nivelul de izolare al tranzacției
  • un Timeout pentru operațiunea încheiată de tranzacție
  • un flag readOnly - un indiciu pentru furnizorul de persistență că tranzacția trebuie citită numai
  • rollback reguli pentru tranzacție

Rețineți că - în mod implicit, revenirea se întâmplă numai pentru runtime, excepții nebifate. Excepția bifată nu declanșează o revenire a tranzacției. Desigur, putem configura acest comportament cu parametrii de adnotare rollbackFor și noRollbackFor .

5. Posibile capcane

5.1. Tranzacții și mandatari

La un nivel ridicat, Spring creează proxy-uri pentru toate clasele adnotate cu @Transactional - fie pe clasă, fie pe oricare dintre metode. Proxy-ul permite cadrului să injecteze logică tranzacțională înainte și după metoda de rulare - în principal pentru începerea și angajarea tranzacției.

Ce este important de reținut este că, dacă beanul tranzacțional implementează o interfață, în mod implicit proxy-ul va fi un Java Dynamic Proxy. Aceasta înseamnă că vor fi interceptate numai apelurile de metode externe care vin prin proxy. Orice apel de auto-invocare nu va începe nicio tranzacție, chiar dacă metoda are adnotarea @Transactional .

O altă avertizare a utilizării proxy-urilor este că numai metodele publice ar trebui să fie adnotate cu @Transactional. Metodele oricărei alte vizibilități vor ignora adnotarea în mod silențios, deoarece acestea nu sunt înlocuite.

Acest articol discută aici alte capcane de proxy în detaliu.

5.2. Schimbarea nivelului de izolare

De asemenea, putem modifica nivelul de izolare a tranzacției:

@Transactional(isolation = Isolation.SERIALIZABLE)

Rețineți că acest lucru a fost introdus de fapt în primăvara 4.1; dacă rulăm exemplul de mai sus înainte de primăvara 4.1, va avea ca rezultat:

org.springframework.transaction.InvalidIsolationLevelException : JPA standard nu acceptă niveluri de izolare personalizate - utilizați un JpaDialect special pentru implementarea JPA

5.3. Tranzacții numai în citire

Numai de citire pentru pavilion generează confuzie , de obicei, mai ales atunci când se lucrează cu JPA; din Javadoc:

Aceasta servește doar ca un indiciu pentru subsistemul real al tranzacțiilor; nu va provoca neapărat eșecul încercărilor de acces la scriere. Un manager de tranzacții care nu poate interpreta sugestia numai în citire nu va face excepție atunci când i se solicită o tranzacție numai în citire.

Faptul este că nu putem fi siguri că o inserare sau o actualizare nu va avea loc când este setat semnalizatorul readOnly . Acest comportament este dependent de furnizor, în timp ce JPA este furnizor agnostic.

Este de asemenea important să se înțeleagă că numai de citire pentru pavilion este relevant numai în interiorul unei tranzacții. Dacă o operațiune are loc în afara unui context tranzacțional, semnalizatorul este pur și simplu ignorat. Un exemplu simplu în acest sens ar numi o metodă adnotată cu:

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

dintr-un context non-tranzacțional - o tranzacție nu va fi creată și semnalizatorul readOnly va fi ignorat.

5.4. Înregistrarea tranzacțiilor

O metodă utilă pentru a înțelege problemele legate de tranzacții este reglarea fină a înregistrării în pachetele tranzacționale. Pachetul relevant din primăvară este „ org.springframework.transaction”, care ar trebui să fie configurat cu un nivel de înregistrare TRACE.

6. Concluzie

Am acoperit configurația de bază a semanticii tranzacționale utilizând atât Java cât și XML, cum să utilizăm @Transactional și cele mai bune practici ale unei strategii tranzacționale.

Ca întotdeauna, codul prezentat în acest articol este disponibil pe Github.