Varios conocen como se configuran las transacciones en Spring o frameworks similares, pero no muchos saben las ventajas por las cuales conviene utilizar este tipo específico de demarcación de transacciones. Voy a intentar contarles un poco acerca de que problemas soluciona este tipo de configuración, en contraposición a manejar las transacciones de forma programática.
Hace mucho, mucho tiempo, en una galaxia muy lejana, los programadores se encontraron frente al desafío de tener que lidiar con transacciones, según la definición wikipedista: "Una transacción es una interacción con una estructura de datos compleja, compuesta por varios procesos que se han de aplicar uno después del otro. La transacción debe realizarse de una sola vez y sin que la estructura a medio manipular pueda ser alcanzada por el resto del sistema hasta que se hayan finalizado todos sus procesos."
El enfoque programático
Para poder permitir que esa estructura a "medio manipular" pueda ser alcanzada por el resto del sistema, el primer aproach, fue el de utilizar dos sentencias para poder definir el inicio y el fin de la porción de código que lidiaba con un fragmento transaccional: es decir los famosos "begin" & "commit", con su infaltable contraparte "rollback".
Esas tres sentencias se usaron por mucho tiempo para manejar transacciones, y hoy en día mucha gente piensa que es la forma mediante la cual hay que lidiar con las mismas. En realidad no hay problemas en utilizar esto, pero a medida que los sistemas aumentan en complejidad, comienzan a notarse las falencias de este enfoque.
El primer gran problema es que claramente esto se trata de un "aspecto" del código que desarrollamos, y como tal, puede traer problemas si no es siempre tratado de similar manera. Por lo tanto es necesario implementarlo de forma separa de nuestro código para evitar olvidos o la posibilidad de codificarlo de forma diferente para cada funcionalidad.
Pero el verdadero gran problema es que el código tiene a complicarse mucho, si tenemos el caso de varias clases con diferentes métodos de lógica de negocio que se invocan entre sí. Cuando esto sucede, lo que normalmente se requiere, es que los métodos puedan "compartir" las transacciones entre sí, de tal manera de que solo uno de los métodos la inicie (el primero en ser invocado por la capa de presentación) y luego los otros utilicen este contexto transaccional ya creado. Esto normalmente se conoce como "propagación" de la transacción.
Si esto no se implementa de una forma declarativa, es decir "declarando" como cada método va a utilizar el aspecto de la transaccionalidad, es muy dificil de lograr un comportamiento similar, sin caer en el caso de tener que usar varios flags que se pasen como parámetro y cadenas de if que aumentan su complejidad a medida de que se van encadenando más y más métodos.
Declaremos
Utilizando Spring, como framework de inyección de dependencias, existen tres maneras de utilizar el enfoque declarativo, al momento de definir las transacciones de nuestro código:
- Utilizando Proxys: Este es el enfoque más antiguo. Lo que hacemos es generar de forma dinámica un sustituto de nuestro Servicio, que lo que hace es agregar el manejo de transacciones, antes de cada método que respete un patrón de nombres que podemos especificar. Estos proxys se definen de una manera similar a la siguiente:
1: <bean id="abstractTransactionManager" abstract="true">
2: <property name="transactionAttributes">
3: <props>
4: <prop key="save*">PROPAGATION_REQUIRED,ISOLATION_READ_COMMITTED</prop>
5: <prop key="remove*">PROPAGATION_REQUIRED,ISOLATION_READ_COMMITTED</prop>
6: <prop key="update*">PROPAGATION_REQUIRED,ISOLATION_READ_COMMITTED</prop>
7: <prop key="ejecutar*">PROPAGATION_REQUIRED,ISOLATION_READ_COMMITTED</prop>
8: <prop key="*">PROPAGATION_REQUIRED,readOnly,ISOLATION_READ_COMMITTED</prop>
9: </props>
10: </property>
11: </bean>
12: <bean id="feriadosManager" parent="abstractTransactionManager" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
13: <property name="target">
14: <bean id="feriadosService" class="com.example.mysystem.service.FeriadosService">
15: <property name="feriadosDAO" ref="feriadosDAO" />
16: </bean>
17: </property>
18: <property name="transactionManager" ref="transactionManager_gateweb" />
19: <property name="proxyTargetClass" value="true" />
20: </bean>
- Utilizando Spring AOP: Como Spring tiene el "poder" de instanciar nuestras clases, el mecanismo de proxys se puede implementar de una forma aún más dinámica, definiendo la teoría de aspect-oriented-programming, con el sabor de Spring:
1: <tx:advice id="commonTxAdvice" transaction-manager="transactionManager">
2: <tx:attributes>
3: <tx:method name="save*" propagation="REQUIRED" />
4: <tx:method name="remove*" propagation="REQUIRED" />
5: <tx:method name="delete*" propagation="REQUIRED" />
6: <tx:method name="update*" propagation="REQUIRED" />
7: <tx:method name="execute*" propagation="REQUIRED" />
8: <tx:method name="*" read-only="true" />
9: </tx:attributes>
10: </tx:advice>
11: <aop:config>
12: <!-- Pointcut -->
13: <aop:pointcut id="servicePointCut" expression="execution(* com.example.mysystem.service.impl.*.*(..))" />
14: <!-- Advisor -->
15: <aop:advisor advice-ref="commonTxAdvice" pointcut-ref="servicePointCut" />
16: </aop:config>
De esta manera no es necesario recordar envolver a nuestros beans de servicio con un proxy transaccional, ya que se hace de manera automática de acuerdo al nombrado de los paquetes y de los nombres de los métodos. Esta manera es, según mi opinión, la mejor, ya que requiere mucho menos código y es más fácil de implementar.
- Utilizando anotaciones: En este caso, utilizamos una anotación sobre el servicio que queremos que posea un comportamiento transaccional. La anotación utilizada es @Transactional:
1: @Transactional
2: public class DefaultFooService implements FooService {
3: Foo getFoo(String fooName);
4: Foo getFoo(String fooName, String barName);
5: void insertFoo(Foo foo);
6: void updateFoo(Foo foo);
7: }
Lo bueno de todo esto, es que los tres approachs se pueden usar side-by-side, sin ningún problema.
Igualmente hay que recordar, que dependiendo del enfoque, hay que tener en cuenta algunas configuraciones especiales. Lo mejor es, después de este artículo, pegarle una leída a la documentación oficial, que debería ser el primer lugar al cual referirse cuando se quiere conocer como se implementa algo utilizando un framework basado en una comunidad.
Antes que me olvide: no nos olvidemos que spring no es el primer framework y/o estándar en aplicar esta forma de manejar las transacciones, esto ya se viene implementando de una forma similar, desde los tiempos de los EJBs. Otros frameworks de DI, también tienen aproachs similares.
Nos vemos en el próximo post!
0 comentarios:
Publicar un comentario