sql >> Base de Datos >  >> RDS >> Oracle

Revertir A si B sale mal. bota de resorte, jdbctemplate

@Transactional La anotación en Spring funciona envolviendo su objeto en un proxy que a su vez envuelve métodos anotados con @Transactional en una transacción. Debido a esa anotación, no funcionará en métodos privados (como en su ejemplo) porque los métodos privados no se pueden heredar => no se pueden envolver (esto no es cierto si usa transacciones declarativas con aspectj, entonces las advertencias relacionadas con el proxy a continuación no se aplican).

Aquí hay una explicación básica de cómo @Transactional la magia de la primavera funciona.

Tú escribiste:

class A {
    @Transactional
    public void method() {
    }
}

Pero esto es lo que realmente obtienes cuando inyectas un 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
       }
   }
} 

Esto tiene limitaciones. No funcionan con @PostConstruct métodos porque se llaman antes de que el objeto sea proxy. E incluso si configuró todo correctamente, las transacciones solo se revierten si no están marcadas excepciones por defecto. Use @Transactional(rollbackFor={CustomCheckedException.class}) si necesita revertir alguna excepción marcada.

Otra advertencia frecuente que conozco:

@Transactional El método solo funcionará si lo llama "desde afuera", en el siguiente ejemplo b() no se incluirá en la transacción:

class X {
   public void a() {
      b();
   }

   @Transactional
   public void b() {
   }
}

También es porque @Transactional funciona mediante la representación de su objeto. En el ejemplo anterior a() llamará a X.b() no es un método mejorado de "proxy de primavera" b() por lo que no habrá transacción. Como solución, debe llamar a b() de otro frijol.

Cuando encontró alguna de estas advertencias y no puede usar una solución sugerida (haga que el método no sea privado o llame a b() de otro bean) puede usar TransactionTemplate en lugar de transacciones declarativas:

public class A {
    @Autowired
    TransactionTemplate transactionTemplate;

    public void method() {
        transactionTemplate.execute(status -> {
            A();
            B();
            return null;
        });
    }

...
} 

Actualizar

Respondiendo a la pregunta actualizada de OP utilizando la información anterior.

¿Qué método debe anotarse con @Transactional:changes()? cambios en la base de datos()?

@Transactional(rollbackFor={Exception.class})
public void changes() throws Exception {
    someLogicBefore();
    databaseChanges();
    someLogicAfter();
}

Asegúrate de changes() se llama "desde fuera" de un bean, no desde la clase en sí y después de que se instanciara el contexto (por ejemplo, esto no es afterPropertiesSet() o @PostConstruct método anotado). Entienda que la primavera revierte la transacción solo para las excepciones no verificadas de forma predeterminada (trate de ser más específico en la lista de excepciones verificadas).