Spring Boot 提供了声明式事务管理。
@Transactional 注解
@Transactional 可以用于方法和类。
@Service
public class UserService {
@Transactional
public Long registerUser(User user) {
// 执行数据库操作
return id;
}
}
使用方法
- 配置启用
@EnableTransactionManagement - 在 Spring 配置中指定事务管理器
- 公共方法上添加
@Transactional注解
声明式事务
事务的四个属性的特性 ACID:
- Atomic
- Consistent
- Isolation
- Duration
事务隔离级别
- 脏读,事务 A 读取了事务 B 未提交的数据并做了修改,如果 B 事务回滚,事务 A 读取的数据就是错误的,产生脏读
- 不可重复读,同一个事务相同的查询得到不同的结果,读取到了已经提交事物的更改数据
- 幻读,事务 A 读取了事务 B 提交的新数据
MySQL 的默认事务隔离级别:可重复读
Spring 事务管理委托给底层的持久化框架完成。
Spring 为不同的持久化框架提供了不同的 PlatformTransactionManager 接口。
- org.springframework.jdbc.datasource.DataSourceTransactionManager 提供对单个
javax.sql.DataSource事务的管理,用于 Spring JDBC ,iBATIS 或 MyBatis 框架事务管理 - org.springframework.orm.jpa.JpaTransactionManager 对 javax.persistence.EntityManagerFactory 事务支持,集成 JPA 实现框架事务
- org.springframework.transaction.jta.JtaTransactionManager 提供分布式事务管理支持,事务管理委托 Java EE 应用服务器事务管理
基于 @Transactional 注解的声明式事务
在方法前后进行拦截,在目标方法开始之前创建或加入事务,执行后根据情况提交或回滚事务。
无需在逻辑代码中混入事务管理的代码,通过配置或注解完成。
<?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:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!--开启tx注解-->
<tx:annotation-driven transaction-manager="transactionManager"/>
<!--事物管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--数据源-->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/my_test?useSSL=false&useUnicode=true&characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="chenhao1991@"/>
</bean>
<!--jdbcTemplate-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--业务bean-->
<bean id="accountService" class="com.chenhao.aop.AccountServiceImpl">
<property name="jdbcTemplate" ref="jdbcTemplate"/>
</bean>
</beans>
Transactional 注解工作原理
- 传播(propagation) 隔离(isolation)
- 如何避免 pitfall
Spring 的声明式注解底层使用了 AOP,Spring 为添加了 @Transactional 的类增加代理类,这一层代理在运行时几乎时隐形的,用户不可见。这个方式使得 Spring 可以在方法的前后来增加额外的行为。事务管理就是利用这个原理来实现的。
拦截方法 TransactionInterceptor
TransactionInterceptor 实现了 MethodInterceptor,通过该拦截器拦截被注解 @Transactional 的方法,然后实现了 invoke 方法:
@Override
public Object invoke(final MethodInvocation invocation) throws Throwable {
// Work out the target class: may be {@code null}.
// The TransactionAttributeSource should be passed the target class
// as well as the method, which may be from an interface.
Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);
// Adapt to TransactionAspectSupport's invokeWithinTransaction...
return invokeWithinTransaction(invocation.getMethod(), targetClass, new InvocationCallback() {
@Override
public Object proceedWithInvocation() throws Throwable {
return invocation.proceed();
}
});
}
TransactionAttributeSourcePointcut 定义 Spring Transaction 的拦截规则
PlatformTransactionManager 事务管理器
如果使用 JPA(Java Persistence API) ,则需要自己手工实现:
UserTransaction utx = entityManager.getTransaction();
try {
utx.begin();
businessLogic();
utx.commit();
} catch(Exception ex) {
utx.rollback();
throw ex;
}
这种方式的实现代码很清晰,但是引入了很多缺点:
- 很罗嗦并且容易出错
- 任何出错可能造成错误
- 难以调试和重现问题
- 降低了代码的可读性
- 如何处理一个方法调用另一个事务方法的问题
@Transactional
public void businessLogic() {
... use entity manager inside a transaction ...
}
通过使用 @Transactional 注解,一方面提高了代码可读性,另一方面也提高了扩展性。
如果其他事务方法调用 businessLogic() 方法,这个方法会自动加入当前的事务。
The Transaction Manager 处理两件事情:
- Entity Manager 是否应该创建
- Database transaction 是否应该开始
在 Before 中需要决定:
- 事务是否已经开始了
- 事务的 propagation 属性,比如
REQUIRES_NEW会立即开始一个新的事务
如果事务管理决定开始一个新的事务:
- 创建一个新的 entity manager
- 将 entity manager 绑定到当前线程
- 从 DB 连接池中获取一个链接
- 将链接绑定到当前线程
编程式事务管理
主要通过 TransactionTemplate 或直接使用 PlatformTransactionManager 实现
@Service
public class UserService {
@Autowired
private TransactionTemplate template;
public Long registerUser(User user) {
return template.execute(status -> {
// 执行数据库操作
return id;
});
}
}
事务管理器
Spring Boot 使用事务管理器来处理事务状态(开启、提交、回滚)
DataSourceTransactionManager:用于 JDBC 事务JpaTransactionManager:用于 JPA 事务HibernateTransactionManager:用于 Hibernate 事务
传播行为
@Transactional 注解支持配置事务的传播行为,例如:
@Transactional(propagation = Propagation.REQUIRED)
public void someMethod() {
// 方法体
}
隔离级别
可以通过 @Transactional 注解设置事务的隔离级别:
@Transactional(isolation = Isolation.READ_COMMITTED)
public void someMethod() {
// 方法体
}