1.2 理解Spring框架事物抽象
Spring事务抽象的关键是事务策略的概念。事务策略由TransactionManager
定义,特别是用于命令式事务管理的org.springframework.transaction.PlatformTransactionManager
接口和用于响应式事务管理的org.springframework.transaction.ReactiveTransactionManager
接口。以下清单显示了PlatformTransactionManager
API的定义:
public interface PlatformTransactionManager extends TransactionManager {
TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException;
void commit(TransactionStatus status) throws TransactionException;
void rollback(TransactionStatus status) throws TransactionException;
}
尽管你可以从应用程序代码中以编程方式使用它,但它主要是一个服务提供接口(SPI)。由于PlatformTransactionManager
是接口,因此可以根据需要轻松对其进行模拟或存根。它与JNDI
之类的查找策略无关。与Spring框架IoC容器中的任何其他对象(或bean)一样,定义了PlatformTransactionManager
实现。这一优点使Spring框架事务成为值得抽象的,即使在使用JTA
时也是如此。与直接使用JTA
相比,你可以更轻松地测试事务代码。
同样,为了与Spring的理念保持一致,可以由任何PlatformTransactionManager
接口方法抛出的TransactionException
未检查异常(也就是说,它扩展了java.lang.RuntimeException
类)。事物基础架构故障几乎总是致命的。在极少数情况下,应用程序代码实际上可以从事务失败中恢复,应用程序开发人员仍然可以选择捕获和处理TransactionException
。实际一点是,开发人员没有被迫这样做。
getTransaction(..)
方法根据TransactionDefinition
参数返回TransactionStatus
对象。如果当前调用堆栈中存在匹配的事务,则返回的TransactionStatus
可能表示一个新事务或一个现有事务。后一种情况的含义是,与Java EE事务上下文一样,TransactionStatus
与执行线程相关联。
从Spring框架5.2开始,Spring还为使用响应式类型或Kotlin
协程的响应式应用程序提供了事务管理抽象。以下清单显示了由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;
}
响应式事务管理器主要是服务提供接口(SPI),尽管你可以从应用程序代码中以编程方式使用它。由于ReactiveTransactionManager
是接口,因此可以根据需要轻松对其进行模拟或存根。
TransactionDefinition
接口指定:
- 传播:通常,事务范围内的所有代码都在该事务中运行。但是,如果在已存在事务上下文的情况下运行事务方法,则可以指定行为。例如,代码可以在现有事务中继续运行(常见情况),或者可以暂停现有事务并创建新事务。Spring提供了
EJB
CMT
熟悉的所有事务传播选项。要了解有关Spring中事务传播的语义的信息,请参阅事务传播。 - 隔离:此事务与其他事务的工作隔离的程度。例如,此事务能否看到其他事务未提交的写入?
- 超时:该事务在超时之前将运行多长时间,并由基础事务基础结构自动回滚。
- 只读状态:当代码读取但不修改数据时,可以使用只读事务。在某些情况下,例如使用
Hibernate
时,只读事务可能是有用的优化。
这些设置反映了标准的事物概念。如有必要,请参考讨论事务隔离级别和其他核心事务概念的资源。了解这些概念对于使用Spring框架或任何事务管理解决方案至关重要。
TransactionStatus
接口为事务代码提供了一种控制事务执行和查询事务状态的简单方法。这些概念应该很熟悉,因为它们对于所有事务API都是通用的。以下清单显示了TransactionStatus
接口:
public interface TransactionStatus extends TransactionExecution, SavepointManager, Flushable {
@Override
boolean isNewTransaction();
boolean hasSavepoint();
@Override
void setRollbackOnly();
@Override
boolean isRollbackOnly();
void flush();
@Override
boolean isCompleted();
}
无论你在Spring中选择声明式还是编程式事务管理,定义正确的TransactionManager
实现都是绝对必要的。通常,你可以通过依赖注入来定义此实现。TransactionManager
实现通常需要了解其工作环境:JDBC
、JTA
、Hibernate
等。
TransactionManager
实现通常需要了解其工作环境:JDBC
、JTA
、Hibernate
等。以下示例显示了如何定义本地PlatformTransactionManager
实现(在这种情况下,使用纯JDBC
)。
你可以通过创建类似于以下内容的bean来定义JDBC
数据源:
<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>
然后,相关的PlatformTransactionManager
Bean定义将引用DataSource
定义。它应类似于以下示例:
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
如果你在Java EE容器中使用JTA
,则可以使用通过JNDI
获得的容器DataSource
以及Spring的JtaTransactionManager
。以下示例显示了JTA
和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" />
<!-- other <bean/> definitions here -->
</beans>
JtaTransactionManager
不需要了解数据源(或任何其他特定资源),因为它使用了容器的全局事务管理基础结构。
dataSource
bean的先前定义使用jee名称空间中的标记。有关更多信息,参考JEE Schema。
你还可以轻松使用Hibernate
本地事务,如以下示例所示。在这种情况下,你需要定义一个Hibernate
LocalSessionFactoryBean
,你的应用程序代码可使用该Hibernate
LocalSessionFactoryBean
获取Hibernate
Session
实例。
DataSource
bean定义与先前显示的本地JDBC
示例相似,因此在以下示例中未显示。
如果通过
JNDI
查找数据源(由任何非JTA
事务管理器使用)并由Java EE容器管理,则该数据源应该是非事务性的,因为Spring框架(而不是Java EE容器)管理事务。
在这种情况下,txManager
bean是HibernateTransactionManager
类型。就像DataSourceTransactionManager
需要引用数据源一样,HibernateTransactionManager
需要引用SessionFactory
。以下示例声明了sessionFactory
和txManager
bean:
<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>
如果使用Hibernate
和Java EE容器管理的JTA
事务,则应使用与前面的JDBC
JTA
示例相同的JtaTransactionManager
,如以下示例所示:
<bean id="txManager" class="org.springframework.transaction.jta.JtaTransactionManager"/>
如果使用
JTA
,则无论使用哪种数据访问技术(无论是JDBC
、Hibernate
JPA
或任何其他受支持的技术),事务管理器定义都应该相同。这是由于JTA
事务是全局事务,它可以征用任何事务资源。
在所有这些情况下,无需更改应用程序代码。你可以仅通过更改配置来更改事务的管理方式,即使更改意味着从本地事务转移到全局事务,反之亦然。
作者
个人从事金融行业,就职过易极付、思建科技、某网约车平台等重庆一流技术团队,目前就职于某银行负责统一支付系统建设。自身对金融行业有强烈的爱好。同时也实践大数据、数据存储、自动化集成和部署、分布式微服务、响应式编程、人工智能等领域。同时也热衷于技术分享创立公众号和博客站点对知识体系进行分享。
博客地址: http://youngitman.tech