@Transactional
annotatie in de lente werkt door uw object in een proxy te wikkelen die op zijn beurt methoden omhult die zijn geannoteerd met @Transactional
bij een transactie. Daarom werkt annotatie niet op privémethoden (zoals in uw voorbeeld) omdat privémethoden niet kunnen worden overgenomen => ze kunnen niet worden ingepakt (dit is niet waar als u declaratieve transacties met aspectj gebruikt, dan zijn de proxygerelateerde voorbehouden hieronder niet van toepassing).
Hier is een basisuitleg over hoe @Transactional
lente magie werkt.
Je schreef:
class A {
@Transactional
public void method() {
}
}
Maar dit krijg je eigenlijk als je een boon injecteert:
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
}
}
}
Dit heeft beperkingen. Ze werken niet met @PostConstruct
methoden omdat ze worden aangeroepen voordat het object wordt geproxyd. En zelfs als je alles correct hebt geconfigureerd, worden transacties alleen teruggedraaid op uitgeschakeld uitzonderingen standaard. Gebruik @Transactional(rollbackFor={CustomCheckedException.class})
als u terugdraait op een gecontroleerde uitzondering.
Nog een veelvoorkomend voorbehoud dat ik ken:
@Transactional
methode werkt alleen als je het "van buitenaf" noemt, in het volgende voorbeeld b()
zal niet worden verpakt in transactie:
class X {
public void a() {
b();
}
@Transactional
public void b() {
}
}
Het is ook omdat @Transactional
werkt door uw object te proxyen. In het bovenstaande voorbeeld a()
zal X.b()
. aanroepen geen verbeterde "spring proxy"-methode b()
dus er vindt geen transactie plaats. Als tijdelijke oplossing moet je b()
. aanroepen van een andere boon.
Wanneer u een van deze waarschuwingen tegenkomt en geen voorgestelde oplossing kunt gebruiken (maak de methode niet-privé of bel b()
van een andere boon) kunt u TransactionTemplate
. gebruiken in plaats van declaratieve transacties:
public class A {
@Autowired
TransactionTemplate transactionTemplate;
public void method() {
transactionTemplate.execute(status -> {
A();
B();
return null;
});
}
...
}
Bijwerken
Beantwoorden van OP bijgewerkte vraag met behulp van bovenstaande informatie.
Welke methode moet worden geannoteerd met @Transactional:changes()? databaseChanges()?
@Transactional(rollbackFor={Exception.class})
public void changes() throws Exception {
someLogicBefore();
databaseChanges();
someLogicAfter();
}
Zorg ervoor dat changes()
wordt "van buitenaf" van een boon genoemd, niet van de klasse zelf en nadat de context is geïnstantieerd (dit is bijvoorbeeld niet afterPropertiesSet()
of @PostConstruct
geannoteerde methode). Begrijp dat spring rollback-transacties standaard alleen voor niet-aangevinkte uitzonderingen worden teruggedraaid (probeer specifieker te zijn in de lijst met rollbackVoor gecontroleerde uitzonderingen).