@Transactional
anotação no spring funciona envolvendo seu objeto em um proxy que, por sua vez, envolve métodos anotados com @Transactional
em uma transação. Por causa dessa anotação não funcionará em métodos privados (como no seu exemplo) porque métodos privados não podem ser herdados => eles não podem ser encapsulados (isso não é verdade se você usar transações declarativas com aspectj, então as advertências relacionadas ao proxy abaixo não se aplicam). Aqui está uma explicação básica de como
@Transactional
a magia da primavera funciona. Você escreveu:
class A {
@Transactional
public void method() {
}
}
Mas isso é o que você realmente obtém quando injeta um bean:
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
}
}
}
Isso tem limitações. Eles não funcionam com
@PostConstruct
métodos porque eles são chamados antes do objeto ser proxy. E mesmo que você tenha configurado tudo corretamente, as transações só são revertidas em desmarcadas exceções por padrão. Use @Transactional(rollbackFor={CustomCheckedException.class})
se você precisar de reversão em alguma exceção verificada. Outra advertência frequentemente encontrada que eu conheço:
@Transactional
O método só funcionará se você o chamar de "de fora", no exemplo a seguir b()
não será envolvido na transação:class X {
public void a() {
b();
}
@Transactional
public void b() {
}
}
É também porque
@Transactional
funciona fazendo proxy do seu objeto. No exemplo acima a()
chamará X.b()
não é um método "spring proxy" aprimorado b()
então não haverá transação. Como solução alternativa, você deve chamar b()
de outro feijão. Quando você encontrar qualquer uma dessas advertências e não puder usar uma solução alternativa (torne o método não privado ou chame
b()
de outro bean) você pode usar TransactionTemplate
em vez de transações declarativas:public class A {
@Autowired
TransactionTemplate transactionTemplate;
public void method() {
transactionTemplate.execute(status -> {
A();
B();
return null;
});
}
...
}
Atualizar
Respondendo à pergunta atualizada do OP usando as informações acima.
Qual método deve ser anotado com @Transactional:changes()? databaseChanges()?
@Transactional(rollbackFor={Exception.class})
public void changes() throws Exception {
someLogicBefore();
databaseChanges();
someLogicAfter();
}
Certifique-se de
changes()
é chamado "de fora" de um bean, não da própria classe e depois que o contexto foi instanciado (por exemplo, isso não é afterPropertiesSet()
ou @PostConstruct
método anotado). Entenda que a transação de rollbacks de primavera apenas para exceções não verificadas por padrão (tente ser mais específico na lista de exceções verificadas rollbackFor).