martes, 23 de febrero de 2010

Implementando Transacciones con Spring y Anotaciones @Transactional Parte 3

En esta oportunidad vamos a ver como podemos propagar las transacciones mediante spring. Se debe de entender por propagación de la transacción al mecanismo que extiende la transacción hacia otros métodos también transaccionales. En los ejemplos anteriores habíamos visto transacciones encapsuladas en un solo método, por ejemplo:

@Transactional
public class ClaseTransaccionalImpl implements ClaseTransaccional{
public void metodoTransaccional01(){
dao01.ejecutar();
}
public void metodoTransaccional02(){
dao02.ejecutar();
}
}

En el código anterior cada método(invocado independientemente) inicia y cierra una transacción, pero que pasa si cualquiera de los dos métodos llama al otro como a continuación:

@Transactional
public class ClaseTransaccionalImpl implements ClaseTransaccional{
public void metodoTransaccional01(){
dao01.ejecutar();
metodoTransaccional02();
}
public void metodoTransaccional02(){
dao02.ejecutar();
}
}

Ahora vemos que el método metodoTransaccional01() invoca al método metodoTransaccional02(), pero que es lo que pasa con la transacción iniciada por la invocación de metodoTransaccional01()?.
se realiza un commit antes de la ejecución del siguiente método?
la transacción se suspende y se crea otra transacción por la invocación del nuevo método?
la transacción se extiende(PROPAGA) y la ejecución de los dos métodos se ejecutan en una sola transacción?
Recordando del post anterior los atributos transaccionales por default a nivel de método son los siguientes:
propagation: REQUIRED.
isolation: DEFAULT(READ_COMMITED para Oracle)
timeout: -1
readOnly: false

Entonces la clase anterior definida explicitamente quedaría como:
@Transactional
public class ClaseTransaccionalImpl implements ClaseTransaccional{

@Transactional(propagation=Propagation.REQUIRED,isolation=Isolation.READ_COMMITTED,readOnly=false)
public void metodoTransaccional01(){
dao01.ejecutar();
metodoTransaccional02();
}

@Transactional(propagation=Propagation.REQUIRED,isolation=Isolation.READ_COMMITTED,readOnly=false)
public void metodoTransaccional02(){
dao02.ejecutar();
}
}

Entonces que se puede deducir?, pues que la transacción se propaga hacia el método metodoTransaccional02(), ya que el atributo de propagación de la transacción es REQUIRED. Veamoslo más despacio:

1. Supongamos que desde una clase cualquiera ejecutamos lo siguiente:
ClaseTransaccional servicio = (ClaseTransaccional)context.getBean("claseTransaccional");
servicio.metodoTransaccional01();

2. Lo que hará spring es detectar que el método a sido marcado como transaccional e iniciará una transacción.

3. La ejecución del método se realiza e invoca al DAO: dao01.ejecutar();

4. La ejecución del método continua y encuentra el método metodoTransaccional02() antes de ejecutar este método spring identifica que es transaccional y que su tipo de propagación es REQUIRED, es decir que si ya existe un contexto transaccional entonces comparte dicho contexto, y si es que no existe el contexto transaccional entonces debe de crear una nueva transacción, para el ejemplo el método transaccional ha sido invocado en un contexto transaccional definido por metodoTransaccional01() por lo que la transacción se propaga a este nuevo método.

5. Si el método metodoTransaccional02() termina exitosamente, es decir no lanza alguna excepción entonces el método metodoTransaccional01() continua su ejecucion y si termina satisfactoriamente realiza un commit de la transacción, dando la posta al hilo que lo invocó.
6. Si en el punto 5 al metodo metodoTransaccional01() le hemos aplicado un aspecto afterReturning entonces la transacción se propagará a dicho aspecto.

5 comentarios:

  1. Hola Carlos, he estado haciendo pruebas con tu ejemplo modificandolo un poco, poniendo el metodo metodoTransaccional02 con Propagation.REQUIRES_NEW ya que me interesa abrit otra transaccion para que haga ese metodo un commit . El problama es que en este metodo no me lo trata como REQUIRES_NEW sino como REQUIRED enganchandose a la transacción anterior.
    La unica manera de hacerlo funcionar es declarando el metodo metodoTransaccional02 en otra clase ... ¿Sabes a que puede ser debido??
    Muchas gracias

    ResponderBorrar
  2. Hola que tal, esto es debido a que spring implementa las transacciones mediante aspectos y como sabrás no puedes aplicar aspectos a métodos de la misma clase que dispara el aspecto y por lo tanto la transaccionalidad tampoco. Cuando ejecutas 'metodoTransaccional01' la instancia que lo ejecuta es de una clase X y el aspecto transaccional se dispara(AroundAdvice para detectar el inicio y fin de la transacción); pero 'metodoTransaccional02' se ejecuta dentro de su misma instancia por lo que no es posible aplicar un aspecto, lo único que le queda a spring es propagar la transacción con los valores por default(Required).

    ResponderBorrar
  3. muchas gracias por la contestación, he estado mirando y parece que si utilizas AspectJ para esto si que pueden llamar entere metodos de la misma clase ...En cuanto tenga algo te lo envío ...

    ResponderBorrar
  4. Hola que tal, soy nuevo en esto
    pero me gustaria saber el bean para el Xml para las anotaciones

    ResponderBorrar
  5. Por que a todos estos programadores que ponen cursos, se les olvida siempre poner las sentencias import, como si uno conociera la libreria a fondo para saber cuales lleva.

    ResponderBorrar

Es bueno comunicarnos, comenta!!.