Repaso
English

¿Por qué @Transactional podría NO funcionar en este código?

@Service
public class ReportService {

    public void generate() {
        saveAll();               // call inside the same class
    }

    @Transactional
    public void saveAll() { ... }
}

Opciones

  1. Porque el método es público y debe ser privado
  2. Las transacciones usan un proxy; una llamada dentro de la clase se lo salta (Opción correcta)
  3. Porque @Transactional solo funciona en interfaces
  4. Porque el método no declara ningún parámetro

Respuesta

Porque Spring aplica las transacciones mediante un proxy, y una llamada desde otro método de la misma clase (self-invocation) se salta el proxy

Por qué

Spring no cambia tu clase. Envuelve el bean en un objeto proxy que inicia la transacción y hace commit alrededor de la llamada.

Cuando una clase externa llama a reportService.saveAll(), pasa por el proxy y funciona. Pero cuando generate() llama a saveAll() dentro del mismo objeto, es una llamada Java normal sobre this, así que se salta el proxy y no hay transacción.

El mismo problema ocurre con métodos private o final. Solución: mueve el método a otro bean, o pon @Transactional en el método que se llama desde fuera.

Recuerda

La self-invocation y los métodos privados se saltan el proxy de Spring.

Términos clave

Practica esta pregunta en Repaso