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