@Transactional
annotation in spring fungerer ved at pakke dit objekt ind i en proxy, som igen ombryder metoder, der er kommenteret med @Transactional
i en transaktion. På grund af den annotering vil den ikke virke på private metoder (som i dit eksempel), fordi private metoder ikke kan nedarves => de kan ikke ombrydes (dette er ikke sandt, hvis du bruger deklarative transaktioner med aspectj, så gælder proxy-relaterede forbehold nedenfor ikke).
Her er en grundlæggende forklaring på hvordan @Transactional
forårets magi virker.
Du skrev:
class A {
@Transactional
public void method() {
}
}
Men dette er, hvad du faktisk får, når du injicerer en bønne:
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
}
}
}
Dette har begrænsninger. De virker ikke med @PostConstruct
metoder, fordi de kaldes før objektet er proxy. Og selvom du har konfigureret alt korrekt, bliver transaktioner kun rullet tilbage ved ikke markeret undtagelser som standard. Brug @Transactional(rollbackFor={CustomCheckedException.class})
hvis du har brug for tilbagerulning af en eller anden markeret undtagelse.
En anden ofte stødt advarsel, jeg kender:
@Transactional
metoden vil kun fungere, hvis du kalder den "udefra", i følgende eksempel b()
vil ikke blive pakket ind i transaktionen:
class X {
public void a() {
b();
}
@Transactional
public void b() {
}
}
Det er også fordi @Transactional
fungerer ved at proxygive dit objekt. I eksemplet ovenfor a()
kalder X.b()
ikke en forbedret "spring proxy"-metode b()
så der vil ikke være nogen transaktion. Som en løsning skal du kalde b()
fra en anden bønne.
Når du stødte på nogen af disse advarsler og ikke kan bruge en foreslået løsning (gør metoden til ikke-privat, eller kald b()
fra en anden bønne) kan du bruge TransactionTemplate
i stedet for deklarative transaktioner:
public class A {
@Autowired
TransactionTemplate transactionTemplate;
public void method() {
transactionTemplate.execute(status -> {
A();
B();
return null;
});
}
...
}
Opdater
Besvarelse af OP opdateret spørgsmål ved hjælp af info ovenfor.
Hvilken metode skal annoteres med @Transactional:changes()? databaseChanges()?
@Transactional(rollbackFor={Exception.class})
public void changes() throws Exception {
someLogicBefore();
databaseChanges();
someLogicAfter();
}
Sørg for at changes()
kaldes "udefra" af en bønne, ikke fra selve klassen og efter konteksten blev instansieret (f.eks. er dette ikke afterPropertiesSet()
eller @PostConstruct
annoteret metode). Forstå, at foråret rollback-transaktion kun for umarkerede undtagelser som standard (prøv at være mere specifik i rollbackFor checked undtagelsesliste).