Fallo de transacciones en Spring: escenarios comunes y diagnóstico

En aplicaciones basadas en Spring, la gestión de transacciones se implementa comúnmente mediante la anotación @Transactional. No obstante, existen condiciones que pueden impedir su correcto funcionamiento. Este artículo describe situaciones típicas donde las transacciones no se activan o no revierten cambios.

Casos en que la transacción no se inicia

1. Visibilidad de método no pública

Los métodos anotados con @Transactional deben ser públicos. Spring AOP utiliza proxies que solo interceptan métodos públicos; métodos con modificadores protected, private o default no serán gestionados.


@Transactional(rollbackFor = Exception.class)
private int guardar(OrdenEntity orden) {
    return ordenDao.insertar(orden);
}

En este ejemplo, el método guardar es privado, por lo que la anotación no tendrá efecto.

2. Métodos modificados como final o estático

Los proxies de AOP requieren la capacidad de sobrescribir métodos. Los métodos final o estáticos no permiten esta sobrescritura, impidiendo la aplicación de la transacción.


@Transactional(rollbackFor = Exception.class)
public final int guardar(OrdenEntity orden) {
    return ordenDao.insertar(orden);
}

Los métodos estáticos tampoco pueden ser interceptados por los proxies.

3. Invocación interna dentro de la misma clase

Si un método transaccional es llamado directamente desde otro método en la misma clase, se usa la instancia actual (this) en lugar del proxy, por lo que la transacción no se activa.


@Override
public OrdenEntity obtenerPorId(String id) {
    validarDatos();
    return ordenDao.obtenerPorId(id);
}

@Transactional(rollbackFor = Exception.class)
public void validarDatos() {
    System.out.println("Validación de parámetros");
}
}

Para solucionarlo, se puede inyectar el propio bean o utilizar ApplicationContext.

4. Clase no registrada como bean de Spring

Si la clase no está anotada con @Service, @Component u otra anotación de Spring, no será gestionada por el contenedor, y @Transactional no funcionará.

5. Uso de múltiples hilos

Las transacciones dependen de una conexión de base de datos única por hilo. Al crear nuevos hilos, se pierde el contexto de transacción.

6. Motor de base de datos incompatible

Algunos motores, como MyISAM en MySQL, no soportan transacciones. Esto debe verificarse en la configuración de la base de datos.

7. Configuración de transacciones no habilitada

En proyectos Spring tradicionales, es necesario configurar explícitamente un administrador de transacciones en el archivo XML. Spring Boot lo configura automáticamente.

8. Anidamiento complejo de operaciones

Si un método transaccional invoca otros métodos no transaccionales que realizan operaciones independientes, el aislamiento puede verse copmrometido, causando fallos en la consistencia.

Casos en que la transacción no revierte

1. Propagación de transacción mal configurada

El parápropagation de @Transactional controla el comportamiento de transacciones anidadas. Valores como NEVER impiden la reversión si ya existe una transacción activa.

2. Captura manual de excepciones sin propagación

Si una excepción es atrapada y no se relanza, Spring no detectará el error y no revertirá la transacción.


@Override
@Transactional
public OrdenEntity obtenerPorId(String id) {
    try {
        return ordenDao.obtenerPorId(id);
    } catch (Exception e) {
        registrarError(e);
        return null;
    }
}

Aquí, la excepción es manejada internamente, por lo que la transacción no se revierte.

3. Tipo de excepción no reconocido

Por defecto, Spring solo revierte RuntimeException y Error. Las excpeciones checked requieren configuración adicional mediante rollbackFor.


@Transactional(rollbackFor = Exception.class)
public OrdenEntity obtenerPorId(String id) throws ExcepcionPersonalizada {
    // lógica que puede lanzar ExcepcionPersonalizada
}

Al especificar Exception.class, se incluyen todas las excepciones checked y unchecked.

4. Inconsistencia en criterios de reversión

Cuando se llaman métodos transaccionales anidados con diferentes configuraciones de rollbackFor, la reversión puede no aplicarse como se espera.

Para mayor control, Spring ofrece transacciones programáticas mediante TransactionTemplate, que evita problemas de AOP y permite un manejo más granular.


@Autowired
private TransactionTemplate transactionTemplate;

public void procesarOrden(OrdenEntity orden) {
    transactionTemplate.execute(estado -> {
        ordenDao.insertar(orden);
        inventarioDao.actualizarStock(orden.getProductoId(), -1);
        return true;
    });
}

Este enfoque es útil cuando se necesita un control explícito del alcance de la transacción.

Etiquetas: Spring Framework transacciones AOP anotaciones TransactionTemplate

Publicado el 6-14 19:34