Transacciones Con Spring
Spring Framework permite configurar transacciones en forma declarativa. De esta forma, la manipulación de transacciones queda establecida en archivos de configuración, sin generar ningún impacto en el código.
Contenido
Transacciones en Spring
En Spring cualquier bean puede ser transaccional. La transaccionalidad se declara por método. En ningún momento se utiliza explícitamente JTA, sino que Spring inyecta funcionalidad de acuerdo a ciertas reglas (similares a las de EJB).
Rollback de transacciones
Por default, una transacción falla solamente cuando el método tira una unchecked exception (las que no se declaran, heredan de RuntimeException). Cuando una transacción falla, se realiza un rollback.
Noten que, por lo tanto, las excepciones de negocio no realizan un rollback (declaradas en el "throws" del método). Es decir, pese a que se lance una excepción de negocio, igualmente se hará un commit de la transacción. Este comportamiento puede cambiarse en la configuración, como veremos más adelante.
Configuración
Spring 2.x introduce una forma extensible de agregar tags en los archivos de configuración. Así, existen distintas extensiones que se pueden agregar a la configuración de Spring, ampliando la funcionalidad.
Esta configuración se agrega en el encabezado de cada XML de Spring, e indica que schemas utiliza dicho archivo.
Para la configuración de transacciones usaremos los schemas "aop" y "tx". El encabezado de nuestro archivo entonces queda:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd"> </beans>
Pointcuts y Advices
Con ese encabezado estamos entonces listos para configurar transacciones. Lo que haremos será declarar todos los objetos que necesitamos sean transaccionales, y luego aplicaremos transacciones a todo un grupo de objetos.
Es decir, la configuración de transacciones ya no será "por objeto", sino mucho más amplia (por ejemplo, "todos las clases del paquete business").
Crearemos entonces un Pointcut que indicará qué clases serán interceptadas para agregar transacción, al cual le agregaremos un Advice. El advice indicará qué tipo de transacción tendrán las clases interceptadas por el pointcut.
En la práctica, esto es realmente muy simple:
<tx:advice id="txAdvice" transaction-manager="txManager"> <tx:attributes> <tx:method name="buscar*" read-only="true"/> <tx:method name="*"/> </tx:attributes> </tx:advice>
<aop:config> <aop:pointcut id="businessOperation" expression="execution(* com.dosideas.business.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="businessOperation"/> </aop:config>
<bean id="business.PaisBo" class="com.dosideas.business.impl.PaisBoImpl"/> <bean id="business.ProvinciaBo" class="com.dosideas.business.impl.ProvinciaBoImpl"/>
<bean id="business.LocalidadBo" class="com.dosideas.business.impl.LocalidadBoImpl"/>
Todas las clases de "business.impl" serán interceptadas, y se les aplicará el advice "txAdvice". Este advice indica que los métodos que comienzan con "buscar" son de sólo lectura, y el resto serán transaccionales (de tipo "REQUIRED", que es el default).
Es posible tambien tener mas de un pointcut:
<aop:pointcut id="businessOperation" expression="execution(* com.dosideas.business.*.*(..)) and !execution(* com.dosideas.business.Cliente.guardar(..))"/>
<aop:pointcut id="businessOperationGuardarCliente" expression="execution(* com.dosideas.business.Cliente.guardar(..))"/>
El pointcut businessOperation referencia a todas las clases de business.impl, salvo el metodo guardar de la clase com.dosideas.business.Cliente (que es referenciado por el pointcut businessOperationGuardarCliente).
Noten entonces que la definición de transaccionalidad se escribe una única vez (o por paquete, o como sea más cómodo), y los los BO se declaran luego normalmente.
Configurando un Advice
Como sabemos, las transacciones realizan un rollback ante una RuntimeException, aunque esto es posible cambiarlo:
<tx:advice id="txAdvice" transaction-manager="txManager"> <tx:attributes> <tx:method name="buscar*" read-only="true" rollback-for="ObjetoNoEncontradoException"/> <tx:method name="*"/> </tx:attributes> </tx:advice>
Por otro lado, también es posible cambiar la propagación de las transacciones para determiandos métodos:
<tx:advice id="noTxAdvice"> <tx:attributes> <tx:method name="*" propagation="NEVER"/> </tx:attributes> </tx:advice>