AOP (Aspect Oriented Programming) nos ofrece una forma diferente de declarar servicios para muestra aplicación y sin entrar en detalle en todo el mundo AOP, podemos utilizar solamente un subconjunto de su funcionalidad para agregar transaccionalidad declarativa a los servicios que desarrollemos, utilizando method interception.
Spring AOP integra el soporte AOP en la infraestructura misma de Spring, permitiéndonos declarar la transaccionalidad de los servicios en el mismo XML de configuración de nuestros beans.
Típicamente, en Spring 1.1.1, definimos una plantilla para nuestras transacciones:
<bean id="daoTxProxyTemplate" abstract="true"
class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="transactionManager"><ref bean="transactionManager"/></property>
<property name="transactionAttributes">
<props>
<prop key="save*">PROPAGATION_REQUIRED</prop>
<prop key="remove*">PROPAGATION_REQUIRED</prop>
<prop key="update*">PROPAGATION_REQUIRED</prop>
<prop key="*">PROPAGATION_REQUIRED,readOnly</prop>
</props>
</property>
</bean>
en la cual está especificado el
transactionManager que utilizaremos, y una serie de patterns de nombres de métodos, sobre los cuales definiremos que tipo de transacción es requerida para los métodos que se ajusten a ese pattern.
Luego solo queda comenzar a definir nuestros servicios, con la implementación como inner bean:
<bean id="itemManager" parent="daoTxProxyTemplate">
<property name="target">
<bean class="com.example.services.itemManagerImpl">
<property name="itemManagerDAO"><ref bean="itemManagerDAO"/></property>
</bean>
</property>
</bean>
Asi, ante cada llamada al método
saveItem de la clase
itemManagerImpl (definida en el bean
itemManager), Spring creará una transacción (o reutilizará una existente) antes de la llamada, y hará el
commit luego de ella.
Mas adelante podríamos seguir definiendo nuestro servicios, por ejemplo uno sin propiedades, y con los parámetros de propagación sobreescritos:
<bean id="miServicio" parent="daoTxProxyTemplate">
<property name="target">
<bean class="com.example.services.otroServicio"/>
</property>
<property name="transactionAttributes">
<props>
<prop key="load*">PROPAGATION_REQUIRED,readOnly</prop>
</props>
</property>
</bean>
En cuanto a los tipos de propagación para las transacciones y su nivel de aislamiento, lo mas utilizado es simplemente
PROPAGATION_REQUIRED, pero podemos optar por todos los definidos en la interfaz
TransactionDefinition, utilizando el formato
"PROPAGATION_NAME, ISOLATION_NAME, readOnly, timeout_NNNN, +Exception1, -Exception2", siendo
PROPAGATION_NAME el único requerido.
En la definición de la plantilla de transacciones, también definimos una propiedad
transactionManager. Este bean, puede tomar la forma de
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource"><ref bean="defaultDataSource"/></property>
</bean>
si trabajamos con JDBC directamente, o
<bean id="transactionManager"
class="org.springframework.orm.hibernate.HibernateTransactionManager">
<property name="sessionFactory"><ref local="sessionFactory"/></property>
</bean>
si utilizamos
Hibernate.
Queda para otro post como definir el
sessionFactory de Hibernate y el
dataSource (con pooling o sin el) necesarios.