@Transactional
anotace na jaře funguje tak, že váš objekt zabalíte do proxy, která zase obalí metody anotované @Transactional
v transakci. Kvůli tomu nebude anotace fungovat na soukromých metodách (jako ve vašem příkladu), protože soukromé metody nelze zdědit => nelze je zabalit (to není pravda, pokud používáte deklarativní transakce s aspectj, pak neplatí níže uvedená upozornění související s proxy).
Zde je základní vysvětlení toho, jak @Transactional
jarní magie funguje.
Napsali jste:
class A {
@Transactional
public void method() {
}
}
Ale to je to, co ve skutečnosti získáte, když si vstříknete fazole:
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
}
}
}
To má svá omezení. Nefungují s @PostConstruct
metody, protože jsou volány před proxy serverem. A i když jste vše nakonfigurovali správně, transakce jsou vráceny zpět pouze při nezaškrtnutém ve výchozím nastavení výjimky. Použijte @Transactional(rollbackFor={CustomCheckedException.class})
pokud potřebujete vrátit zpět nějakou zaškrtnutou výjimku.
Další často se vyskytující upozornění, které znám:
@Transactional
metoda bude fungovat pouze tehdy, když ji zavoláte "zvenčí", v následujícím příkladu b()
nebude zabalené do transakce:
class X {
public void a() {
b();
}
@Transactional
public void b() {
}
}
Je to také proto, že @Transactional
funguje na základě proxy vašeho objektu. V příkladu výše a()
zavolá X.b()
nejde o vylepšenou "jarní proxy" metodu b()
takže k žádné transakci nedojde. Jako náhradní řešení musíte zavolat b()
z jiné fazole.
Když narazíte na některé z těchto upozornění a nemůžete použít navrhované řešení (označte metodu jako nesoukromou nebo zavolejte b()
z jiného bean) můžete použít TransactionTemplate
místo deklarativních transakcí:
public class A {
@Autowired
TransactionTemplate transactionTemplate;
public void method() {
transactionTemplate.execute(status -> {
A();
B();
return null;
});
}
...
}
Aktualizovat
Odpověď na aktualizovanou otázku OP pomocí výše uvedených informací.
Která metoda by měla být anotována pomocí @Transactional:changes()? databaseChanges()?
@Transactional(rollbackFor={Exception.class})
public void changes() throws Exception {
someLogicBefore();
databaseChanges();
someLogicAfter();
}
Ujistěte se, že changes()
je voláno „zvenčí“ beanu, nikoli ze samotné třídy a po vytvoření instance kontextu (např. toto není afterPropertiesSet()
nebo @PostConstruct
anotovaná metoda). Uvědomte si, že ve výchozím nastavení jarní rollback transakce pouze pro nekontrolované výjimky (zkuste být konkrétnější v seznamu rollbackFor kontrolovaných výjimek).