@Transactional
annotation in spring fungerar genom att ditt objekt lindas in i en proxy som i sin tur omsluter metoder som är kommenterade med @Transactional
i en transaktion. På grund av den kommer anteckningen inte att fungera på privata metoder (som i ditt exempel) eftersom privata metoder inte kan ärvas => de kan inte lindas (detta är inte sant om du använder deklarativa transaktioner med aspectj, då gäller inte proxyrelaterade varningar nedan).
Här är en grundläggande förklaring av hur @Transactional
vårens magi fungerar.
Du skrev:
class A {
@Transactional
public void method() {
}
}
Men det här är vad du faktiskt får när du injicerar en böna:
class ProxiedA extends A {
private final A a;
public ProxiedA(A a) {
this.a = a;
}
@Override
public void method() {
try {
// open transaction ...
a.method();
// commit transaction
} catch (RuntimeException e) {
// rollback transaction
} catch (Exception e) {
// commit transaction
}
}
}
Detta har begränsningar. De fungerar inte med @PostConstruct
metoder eftersom de anropas innan objektet proxias. Och även om du har konfigurerat allt korrekt, återställs transaktioner endast vid avmarkerad undantag som standard. Använd @Transactional(rollbackFor={CustomCheckedException.class})
om du behöver återställa något markerat undantag.
En annan varning som jag ofta stöter på vet jag:
@Transactional
Metoden fungerar bara om du kallar den "från utsidan", i följande exempel b()
kommer inte att inkluderas i transaktionen:
class X {
public void a() {
b();
}
@Transactional
public void b() {
}
}
Det beror också på att @Transactional
fungerar genom att proxyservera ditt objekt. I exemplet ovan a()
anropar X.b()
inte en förbättrad "spring proxy"-metod b()
så det blir ingen transaktion. Som en lösning måste du anropa b()
från en annan böna.
När du stött på någon av dessa varningar och inte kan använda en föreslagen lösning (gör metoden icke-privat eller anrop b()
från en annan böna) kan du använda TransactionTemplate
istället för deklarativa transaktioner:
public class A {
@Autowired
TransactionTemplate transactionTemplate;
public void method() {
transactionTemplate.execute(status -> {
A();
B();
return null;
});
}
...
}
Uppdatera
Svarar på OP uppdaterad fråga med hjälp av info ovan.
Vilken metod ska noteras med @Transactional:changes()? databaseChanges()?
@Transactional(rollbackFor={Exception.class})
public void changes() throws Exception {
someLogicBefore();
databaseChanges();
someLogicAfter();
}
Se till att changes()
kallas "från utsidan" av en böna, inte från själva klassen och efter att sammanhanget instansierades (t.ex. är det inte afterPropertiesSet()
eller @PostConstruct
annoterad metod). Förstå att våren återställer transaktionen endast för omarkerade undantag som standard (försök att vara mer specifik i återställningslistan för markerade undantag).