Spring详解9.事务
1 Spring事务模板
- Spring为事务管理提供了一致的编程模板,在高层次建立了统一的事务抽象。不管是选择Spring JDBC、Hibernate、JPA还是MyBatis,Spring都可以让用户用统一的编程模型进行事务管理。
- Spring提供了事务模板类TransactionTemplate,通过TransactionTemplate并配合使用事务回调TransactionCallback指定具体的持久化操作,就可以通过编程方式实现事务管理,而无须关注资源获取、复用、释放、事务同步和异常处理等操作。
- Spring事务管理的亮点在于声明式事务管理。 Spring允许通过声明方式,在IoC配置中指定事务的边界和事务属性,Spring自动在指定的事务边界上应用事务属性。
- Spring深刻地认识到,大部分应用都是基于单数据源的,只有为数不多的应用需要使用多数据源的JA事务。因此在单数据源的情况下,Spring直接使用底层的数据源管理事务。只有在面对多数据源的应用时, Spring才寻求 Java EE应用服务器的支持,通过引用应用服务器中的JNDI资源完成JTA事务。
1.1 JTA事务与JDBC事务
JTA 深度历险 - 原理与实现
JDBC事务和JTA事务的区别
1.2 事务管理抽象层

Spring的事务管理抽象层主要包括3个接口:PlatformTransactionManager、TransactionDefinition、TransactionStatus。TransactionDefinition用于描述事务的隔离级别、超时时间、是否为只读事务和事务传播规则等控制事务具体行为的事务属性;PlatformTransactionManager根据TransactionDefinition提供的事务属性配置信息创建事务,并用TransactionStatus描述这个激活事务的状态。
TransactionDefinition
TransactionDefinition定义了Spring的事务属性,这些属性对事务管理控制的若干方面进行配置。
- 事务隔离:当前事务和其他事务的隔离程度。该接口定义了和java.sql.Connection接口中同名的4个隔离级别,还定义了一个默认的隔离级别表示使用底层数据库的默认隔离级别。
- 事务传播:通常在一个事务中执行的所有代码都会运行于同一事务上下文中,但是Spring也提供了几个可选的事务传播类型。
- 事务超时:事务在超时前能运行多久,超过时间后,事务被回滚。有些事务管理器不支持事务过期的功能,这时,如果设置TIMEOUT_DEFAULT之外的其他值,则将抛出异常。
- 只读状态:只读事务不修改任何数据,资源事务管理者可以针对可读事务应用些优化措施,提高运行性能。
TransactionStatus
TransactionStatus代表一个事务的具体运行状态。事务管理器可以通过该接口获取事务运行期的状态信息,也可以通过该接口间接地回滚事务,它相比于在抛出异常时回滚事务的方式更具可控性。该接口继承于SavepointManager接口,SavepointManager接口基于JDBC3.0保存点的分段事务控制能力提供了嵌套事务的机制。
- Object createSavepoint:创建一个保存点对象,以便在后面可以利用rollbackToSavepoint方法使事务回滚到特定的保存点上,也可以通过releaseSavepoint方法释放一个已经不用的保存点。
- void rollbackToSavepoint:将事务回滚到特定的保存点上,被回滚的保存点将自动释放。
- void releaseSavepoint:释放一个保存点。如果事务提交,则所有的保存点会被自动释放,无须手工清除。
- boolean hasSavepoint:判断当前事务是否在内部创建了一个保存点,该保存点是为了支持Spring的嵌套事务而创建的。
- boolean isNewTransaction:判断当前事务是否是一个新的事务,如果返回false,则表示当前事务是一个已经存在的事务,或者当前操作未运行在事务环境中。
- boolean isCompleted():判断当前事务是否已经结束(已经提交或回滚)。
- boolean isRollbackOnly():判断当前事务是否已经被标识为rollback-only。
- void setRollbackOnly():将当前事务设置为rollback-only。通过该标识通知事务管理器只能将事务回滚,事务管理器将通过显式调用回滚命令或抛出异常的方式回滚事务。
PlatformTransactionManager
该接口描述了事务管理这个概念,它定义了3个接口方法,它们是SPI(Service Provider Interface)高层次的接口方法。这些访问都没有和JNDI绑定在一起,可以像 Spring容器中普通的Bean一样对待PlatformTransactionManager实现者。
public interface PlatformTransactionManager {
// 该方法根据事务定义信息从事务环境中返回一个已存在的事务,或者创建一个新的事务
// 并用TransactionStatus描述这个事务的状态
TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException;
// 根据事务的状态提交事务
// 如果事务状态已经被标识为rollback-only,则该方法将执行一个回滚事务的操作
void commit(TransactionStatus status) throws TransactionException;
// 将事务回滚,当 commit方法抛出异常时,rollback方法会被隐式调用。
void rollback(TransactionStatus status) throws TransactionException;
}
1.3 事务管理器的实现类
Spring将事务管理委托给底层具体的持久化实现框架来完成。因此,Spring为不同的持久化框架提供了PlatformTransactionManager接口的实现类,这样就可以通过Spring所提交的高级抽象对不同种类的事务实现使用相同的方式进行管理,而不用关心具体的实现。具体实现类如下所示:
事务 | 说明 |
---|---|
JpaTransactionManager | 使用JPA进行持久化时,使用该事务管理器 |
HibernateTransactionManager | 使用 Hibernate X.0(X可为3,4,5)版本进行持久化时,使用该事务管理器 |
DataSourceTransactionManager | 使用Spring JDBC或MyBatis等基于DataSource数据源的持久化技术时,使用该事务管理器 |
JdoTransactionManager | 使用JDO进行持久化时,使用该事务管理器 |
JtaTransactionManager | 具有多个数据源的全局事务使用该事务管理器 |
1.4 事务同步管理器
Spring将JDBC的Connection、Hibernate的Session等访问数据库的连接或会话对象统称为资源,这些资源在同一时刻是不能多线程共享的。为了让DAO、Service类可能做到singleton, Spring的事务同步管理器类TransactionSynchronizationManager使用ThreadLocal为不同事务线程提供了独立的资源副本,同时维护事务配置的属性和运行状态信息。不管用户使用的是编程式事务管理,还是声明式事务管理,都离不开事务同步管理器。
工具类
Spring框架为不同的持久化技术提供了一套从TransactionSynchronizationManager中获取对应线程绑定资源的工具类,这些工具类都提供了静态的方法,通过这些方法可以获取和当前线程绑定的资源,如下所示:
持久化技术 | 线程绑定资源获取工具 |
---|---|
Spring JDBC或MyBatis | DataSourceUtils |
Hibernate | HibernateSessionFactoryUtils |
JPA | EntityManagerUtils |
JDO | PersistenceManagerFactoryUtils |
TransactionSynchronizationManager
Spring为不同的持久化技术提供了模板类,模板类在内部通过资源获取工具类间接访问TransactionSynchronizationManager中的线程绑定资源。所以,如果DAO使用模板类进行持久化操作,这些DAO就可以配置成singleton。如果不使用模板类,也可以直接通过资源获取工具类访问线程相关的资源。TransactionSynchronizationManager将DAO、 Service类中影响线程安全的所有“状态”统一抽取到该类中,并用 ThreadLocal进行替换,从此DAO(必须是基于模板类或资源获取工具类创建的DAO)和 Service(必须釆用 Spring事务管理机制)变成了线程安全的。
public abstract class TransactionSynchronizationManager {
// 用于保存每个事务线程对应的Connection或Session等类型的资源
private static final ThreadLocal<Map<Object, Object>> resources =
new NamedThreadLocal<Map<Object, Object>>("Transactional resources");
private static final ThreadLocal<Set<TransactionSynchronization>> synchronizations =
new NamedThreadLocal<Set<TransactionSynchronization>>("Transaction synchronizations");
// 用于保存每个事务线程对应事务的名称
private static final ThreadLocal<String> currentTransactionName =
new NamedThreadLocal<String>("Current transaction name");
// 用于保存每个事务线程对应事务的read-only状态
private static final ThreadLocal<Boolean> currentTransactionReadOnly =
new NamedThreadLocal<Boolean>("Current transaction read-only status");
// 用于保存每个事务线程对应事务的隔离级别
private static final ThreadLocal<Integer> currentTransactionIsolationLevel =
new NamedThreadLocal<Integer>("Current transaction isolation level");
// 用于保存每个事务线程对应事务的激活态
private static final ThreadLocal<Boolean> actualTransactionActive =
new NamedThreadLocal<Boolean>("Actual transaction active");
}
1.5 事务传播行为
当我们调用一个基于Spring的Service接口方法时,它将运行于Spring管理的事务环境中,Service接口方法可能会在内部调用其他的Service接口方法以共同完成一个完整的业务操作,因此就会产生服务接口方法嵌套调用的情况。Spring通过事务传播行为控制当前的事务如何传播到被嵌套调用的目标服务接口方法中。Spring在TransactionDefinition接口中规定了7种类型的事务传播行为,它们规定了事务方法和事务方法发生嵌套调用时事务如何进行传播,
事务传播行为类型 | 说明 |
---|---|
PROPAGATION REQUIRED | 如果当前没有事务,则新建一个事务;如果已经存在一个事务,则加入到这个事务中。这是最常见的选择 |
PROPAGATION SUPPORTS | 支持当前事务。如果当前没有事务,则以非事务方式执行 |
PROPAGATION MANDATORY | 使用当前的事务。如果当前没有事务,则抛出异常 |
PROPAGATION REQUIRES NEW | 新建事务。如果当前存在事务,则把当前事务挂起 |
PROPAGATION NOT SUPPORTED | 以非事务方式执行操作。如果当前存在事务,则把当前事务挂起 |
PROPAGATION NEVER | 以非事务方式执行。如果当前存在事务,则抛出异常 |
PROPAGATION NESTED | 如果当前存在事务,则在嵌套事务内执行;如果当前没有事务,则执行与PROPAGATION REQUIRED类似的操作 |
2 编程式的事务管理
Spring还是为编程式事务管理提供了模板类TransactionTemplate,它和那些持久化模板类一样是线程安全的,因此可以在多个业务类中共享一个TransactionTemplate实例进行事务管理。TransactionTemplate有两个主要的方法:
- void setTransactionManager:设置事务管理器。
- Object execute(TransactionCallback action):在TransactionTemplate回调接口中定义需要以事务方式组织的数据访问逻辑。
@Bean
@Autowired
public TransactionTemplate transactionTemplate(PlatformTransactionManager platformTransactionManager) {
// 设置事务管理器
TransactionTemplate transactionTemplate = new TransactionTemplate();
transactionTemplate.setTransactionManager(platformTransactionManager);
return transactionTemplate;
}
3 使用注解配置声明式事务
开启事务
使用注解@EnableTransactionManagement
在类或者方法上添加@Transactional
@Service
@Transactional
public class StudentService {
@Autowired
private StudentMapper studentMapper;
public void updateStudent(long id, String name, int sex) {
studentMapper.update(new StudentDomain(id, name, sex, null, null));
}
}
@Transactional的属性
属性名 | 说明 |
---|---|
propagation | 事务传播行为,通过枚举类Propagation提供合法值。默认PROPAGATION_REQUIRED。 |
isolation | 事务隔离级别,通过枚举类Isolation提供合法值。ISOLATION_DEFAULT。 |
readonly | 事务读写性,布尔型。 |
timeout | 超时时间,int型,以秒为单位。依赖于底层的事务系统的默认值 |
rollbackFor | 一组异常类,遇到时进行回滚,多个异常之间可用逗号分隔 |
rollbackForClassName | 一组异常类名,遇到时进行回滚,类型为String[] |
noRollbackFor | 一组异常类,遇到时不回滚 |
noRollbackForClassName | 一组异常类名,遇到时不回滚,类型为String。 |