Oracle
 sql >> Base de Dados >  >> RDS >> Oracle

Reverta A se B der errado. inicialização de mola, jdbctemplate


@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).