@Transactional
анотацията в spring работи, като обвива вашия обект в прокси, който от своя страна обвива методи, анотирани с @Transactional
в сделка. Поради тази анотация няма да работи върху частни методи (както във вашия пример), тъй като частните методи не могат да бъдат наследени => те не могат да бъдат обвити (това не е вярно, ако използвате декларативни транзакции с aspectj, тогава свързаните с прокси предупреждения по-долу не важат).
Ето основно обяснение как @Transactional
пролетната магия работи.
Вие написахте:
class A {
@Transactional
public void method() {
}
}
Но това всъщност получавате, когато инжектирате боб:
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
}
}
}
Това има ограничения. Те не работят с @PostConstruct
методи, защото се извикват преди обектът да бъде проксиран. И дори ако сте конфигурирали всичко правилно, транзакциите се връщат обратно само при без отметка изключения по подразбиране. Използвайте @Transactional(rollbackFor={CustomCheckedException.class})
ако имате нужда от връщане назад на някакво проверено изключение.
Друго често срещано предупреждение, което знам:
@Transactional
методът ще работи само ако го извикате "отвън", в следващия пример b()
няма да бъде обвит в транзакция:
class X {
public void a() {
b();
}
@Transactional
public void b() {
}
}
Това е също така, защото @Transactional
работи чрез прокси на вашия обект. В примера по-горе a()
ще извика X.b()
не е подобрен метод "прокси прокси" b()
така че няма да има транзакция. Като заобиколно решение трябва да извикате b()
от друг боб.
Когато срещнете някое от тези предупреждения и не можете да използвате предложено решение (направете метода нечастен или извикайте b()
от друг bean) можете да използвате TransactionTemplate
вместо декларативни транзакции:
public class A {
@Autowired
TransactionTemplate transactionTemplate;
public void method() {
transactionTemplate.execute(status -> {
A();
B();
return null;
});
}
...
}
Актуализиране
Отговаряне на актуализиран въпрос от ОП с помощта на информация по-горе.
Кой метод трябва да бъде анотиран с @Transactional:changes()? databaseChanges()?
@Transactional(rollbackFor={Exception.class})
public void changes() throws Exception {
someLogicBefore();
databaseChanges();
someLogicAfter();
}
Уверете се, че changes()
се нарича "отвън" на бийн, а не от самия клас и след инстанцирането на контекста (напр. това не е afterPropertiesSet()
или @PostConstruct
анотиран метод). Разберете, че пружинното връщане на транзакцията само за непроверени изключения по подразбиране (опитайте се да бъдете по-конкретни в списъка с rollbackFor проверени изключения).