La clave para comprender la abstracción de transacciones de Spring es el concepto de estrategia de transacción. La
estrategia de transacción está determinada por TransactionManager, en particular por la interfaz org.springframework.transaction.PlatformTransactionManager
para gestionar transacciones imperativas, así como por org.springframework.transaction.ReactiveTransactionManager
interfaz para la gestión de transacciones reactivas. La siguiente lista muestra la definición de API PlatformTransactionManager
:
public interface ReactiveTransactionManager extends TransactionManager {
Mono<ReactiveTransaction> getReactiveTransaction(TransactionDefinition definition) throws TransactionException;
Mono<Void> commit(ReactiveTransaction status) throws TransactionException;
Mono<Void> rollback(ReactiveTransaction status) throws TransactionException;
}
Esta es principalmente una interfaz de proveedor de servicios (SPI), aunque se puede utilizar programáticamente
desde el código de la aplicación. Debido a que PlatformTransactionManager
es una interfaz, se puede
implementar fácilmente como un objeto simulado o una función auxiliar según sea necesario. No está vinculado a una
estrategia de búsqueda como JNDI. Las implementaciones de PlatformTransactionManager
se definen como
cualquier otro objeto (o bean) en el contenedor Spring Framework IoC. Este beneficio por sí solo hace que las
transacciones desde Spring Framework sean una abstracción decente, incluso si está trabajando con JTA. Probar el
código transaccional se vuelve mucho más fácil que si se usara JTA directamente.
Nuevamente, de acuerdo con
la filosofía Spring, cualquiera de los puede generar una excepción TransactionException
métodos de
interfaz
PlatformTransactionManager
, no se puede verificar (es decir, extiende la clase
java.lang.RuntimeException
). Las fallas en la infraestructura de transacciones casi siempre son
críticas. En el raro caso de que el código de la aplicación pueda realmente recuperarse de una falla en la
transacción, el desarrollador de la aplicación aún puede interceptar y manejar TransactionException
. El
punto importante es que los desarrolladores pueden hacer esto sin coerción.
El método getTransaction(..)
devuelve un TransactionStatus
objeto dependiendo del parámetro TransactionDefinition
. El
TransactionStatus
devuelto puede representar una nueva transacción o puede representar una transacción
existente si existe una transacción correspondiente en la pila de llamadas actual. En el último caso, al igual que
con los contextos de transacción Java EE, TransactionStatus
está asociado con el hilo de ejecución.
A partir de Spring Framework 5.2, Spring también proporciona una abstracción de gestión de transacciones para
reactivos. aplicaciones que utilizan tipos reactivos o corrutinas de Kotlin. La siguiente lista muestra la
estrategia de transacción definida por org.springframework.transaction.ReactiveTransactionManager
:
public interface ReactiveTransactionManager extends TransactionManager {
Mono<ReactiveTransaction> getReactiveTransaction(TransactionDefinition definition) throws TransactionException;
Mono<Void> commit(ReactiveTransaction status) throws TransactionException;
Mono<Void> rollback(ReactiveTransaction status) throws TransactionException;
}
La interfaz TransactionDefinition
define:
Distribución: normalmente, todo el código en el alcance de una transacción se ejecuta en esa transacción. Sin embargo, puede configurar la lógica para que funcione en los casos en que se ejecuta un método transaccional cuando ya existe un contexto transaccional. Por ejemplo, la ejecución del código puede continuar en una transacción existente (el caso habitual), o se puede suspender una transacción existente y crear una nueva transacción. Spring ofrece todas las opciones de propagación de transacciones familiares de CMT de EJB. Para obtener una descripción general de la semántica de propagación de transacciones en Spring, consulte "Transaction Propagation".
Aislamiento: el grado en que una transacción determinada está aislada de la operación de otras transacciones. Por ejemplo, ¿esta transacción puede ver registros no confirmados de otras transacciones?
Tiempo de espera: el tiempo que tarda una transacción en completarse antes de completarse y revertirse automáticamente mediante el proceso de la transacción. infraestructura subyacente.
Estado de solo lectura: puede utilizar una transacción en modo de solo lectura, en el que su código leerá pero no modificará los datos. Las transacciones de solo lectura pueden ser útiles en un contexto de optimización en algunos casos, como si está utilizando Hibernate.
Estas opciones reflejan conceptos de transacciones estándar. Si es necesario, consulte los recursos que analizan los niveles de aislamiento de transacciones y otros conceptos básicos de transacciones. Comprender estos conceptos es esencial para utilizar Spring Framework o cualquier solución de gestión de transacciones.
La
interfaz TransactionStatus
permite que el código transaccional monitoree sin problemas la ejecución de
una transacción y consulte el estado de la transacción. Los conceptos deberían resultarle familiares, ya que son
comunes a todas las API de transacciones. La siguiente lista muestra la interfaz TransactionStatus
:
public interface TransactionStatus extends TransactionExecution, SavepointManager, Flushable {
@Override
boolean isNewTransaction();
boolean hasSavepoint();
@Override
void setRollbackOnly();
@Override
boolean isRollbackOnly();
void flush();
@Override
boolean isCompleted();
}
Ya sea que elija administrar transacciones en Spring de forma declarativa o programática, determinar la
implementación correcta de TransactionManager
es absolutamente necesario. Normalmente, esta
implementación se define mediante inyección de dependencia.
TransactionManager
las
implementaciones generalmente requieren conocimiento del entorno en el que operan: JDBC, JTA, Hibernate, etc. Los
siguientes ejemplos muestran cómo se puede definir una implementación local de
PlatformTransactionManager
(en este caso utilizando JDBC normal).
Puede definir un JDBC DataSource
creando un bean, similar al siguiente:
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${jdbc.driverClassName}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</bean>
La definición de bean PlatformTransactionManager
asociada se hace referencia a la definición de
DataSource
. La definición debería ser similar al siguiente ejemplo:
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
Si está utilizando JTA en un contenedor Java EE, entonces está utilizando el contenedor
DataSource
obtenido a través de JNDI, combinado con JtaTransactionManager
de Spring. El
siguiente ejemplo muestra cómo se vería la versión de búsqueda a través de JTA y JNDI:
<?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:jee="http://www.springframework.org/schema/jee"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/jee
https://www.springframework.org/schema/jee/spring-jee.xsd">
<jee:jndi-lookup id="dataSource" jndi-name="jdbc/jpetstore"/>
<bean id="txManager" class="org.springframework.transaction.jta.JtaTransactionManager" />
<!-- otras definiciones <bean/--> -->
</beans>
JtaTransactionManager
no necesita conocer DataSource
(ni ningún otro recursos
específicos) porque utiliza la infraestructura global de gestión de transacciones del contenedor.
dataSource
utiliza el <jndi-lookup/>
del espacio de nombres jee
. Para obtener más detalles, consulte Esquema JEE.
Para cualquier conjunto de valores especificados para las transacciones Spring, no es necesario cambiar el código de la aplicación. Puede cambiar la forma en que se administran las transacciones simplemente cambiando la configuración, incluso si el cambio significa pasar de transacciones locales a globales o viceversa.
Configuración de transacciones de Hibernate
También puede
utilizar fácilmente transacciones locales de Hibernate, como lo mostrarán los siguientes ejemplos. En este caso,
necesita definir un LocalSessionFactoryBean
para Hibernate que el código de su aplicación pueda usar
para obtener instancias de Session
de Hibernate.
Definir un bean DataSource
es
similar al ejemplo de JDBC local demostrado anteriormente y, por lo tanto, no se muestra en el siguiente ejemplo.
DataSource
(utilizada por
cualquier administrador de transacciones que no sea JTA) se realiza a través de JNDI y se administra a través de
Java EE; debe ser no transaccional porque Spring Framework (no el contenedor Java EE) administra las transacciones.
En este caso el bean txManager
es de tipo HibernateTransactionManager
. Así como
DataSourceTransactionManager
requiere una referencia a DataSource
, HibernateTransactionManager
requiere una referencia a SessionFactory
. El siguiente ejemplo declara los beans
sessionFactory
y txManager
:
<bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="mappingResources">
<list>
<value>org/springframework/samples/petclinic/hibernate/petclinic.hbm.xml</value>
</list>
</property>
<property name="hibernateProperties">
<value>
hibernate.dialect=${hibernate.dialect}
</value>
</property>
</bean>
<bean id="txManager" class="org.springframework.orm.hibernate5.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
Si está utilizando transacciones JTA administradas por contenedor de Hibernate y Java EE, debe utilizar
el mismo JtaTransactionManager
como en el ejemplo anterior con JTA para JDBC, como se muestra en el
siguiente ejemplo. También se recomienda hacer que Hibernate aprenda sobre JTA a través de su coordinador de
transacciones y posiblemente la configuración del modo de liberación de conexión:
<bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="mappingResources">
<list>
<value>org/springframework/samples/petclinic/hibernate/petclinic.hbm.xml</value>
</list>
</property>
<property name="hibernateProperties">
<value>
hibernate.dialect=${hibernate.dialect}
hibernate.transaction.coordinator_class=jta
hibernate.connection.handling_mode=DELAYED_ACQUISITION_AND_RELEASE_AFTER_STATEMENT
</value>
</property>
</bean>
<bean id="txManager" class="org.springframework.transaction.jta.JtaTransactionManager"/>
O puedes pasar taTransactionManager
a su LocalSessionFactoryBean
para proporcionar
los mismos valores predeterminados:
<bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="mappingResources">
<list>
<value>org/springframework/samples/petclinic/hibernate/petclinic.hbm.xml</value>
</list>
</property>
<property name="hibernateProperties">
<value>
hibernate.dialect=${hibernate.dialect}
</value>
</property>
<property name="jtaTransactionManager" ref="txManager"/>
</bean>
<bean id="txManager" class="org.springframework.transaction.jta.JtaTransactionManager"/>
GO TO FULL VERSION