1.pom依赖
当引入 jdbc 依赖之后,Spring Boot会自动默认分别注入 DataSourceTransactionManager 或JpaTransactionManager,所以我们不需要任何额外配置就可以用 @Transactional 注解进行事务的使用。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
2.事务传播行为
public enum Propagation {
REQUIRED(0),
SUPPORTS(1),
MANDATORY(2),
REQUIRES_NEW(3),
NOT_SUPPORTED(4),
NEVER(5),
NESTED(6);
}
支持当前事务的情况:
-
REQUIRED: (默认行为) 如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。
-
SUPPORTS: 如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
-
MANDATORY: 如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。(mandatory:强制性)。
不支持当前事务的情况:
-
REQUIRES_NEW: 创建一个新的事务运行,如果当前存在事务,则把当前事务挂起。
-
NOT_SUPPORTED: 以非事务方式运行,如果当前存在事务,则把当前事务挂起。
-
NEVER: 以非事务方式运行,如果当前存在事务,则抛出异常。
其他情况:
- NESTED: 如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则创建一个新的事务运行。(nested:嵌套的)
3.事务传播行为各种场景
@Service
public class T1Service {
@Autowired
private JdbcTemplate jdbcTemplate;
@Autowired
private T2Service t2Service;
@Transactional(propagation = Propagation.REQUIRED) // 默认行为加不加 propagation 属性一样
public void t1() {
jdbcTemplate.update("insert into user1(name) values(?)", "t1_user");
t2Service.t2();
// int i = 10 / 0;
}
}
@Service
public class T2Service {
@Autowired
private JdbcTemplate jdbcTemplate;
public void t2() {
jdbcTemplate.update("insert into user2(name) values(?)", "t2_user");
// int i = 10 / 0;
}
}
1、只在t1上加默认事务REQUIRED
-
t1 异常或 t2 异常,事务回滚,两表数据插入失败
-
t2 异常,t1 捕获 t2 异常 try-catch
try { t2Service.t2(); } catch (Exception e) { e.printStackTrace(); }
t1 没有向外抛出异常,t1、t2数据插入成功
- t1、t2 都异常,t1 捕获 t2 异常,都插入失败
结论:开启事务的方法没有向外抛出异常,不管被调用者结果如何,数据都会执行成功;若开启事务的方法向外抛出异常,所有对数据的操作都会回滚
2、t1、t2 都加REQUIRED
@Transactional
public void t2() {
jdbcTemplate.update("insert into user2(name) values(?)", "t2_user");
// int i = 10 / 0;
}
- t1 异常或 t2 异常,数据都插入失败
- t2 异常,t1 捕获 t2 异常,数据都插入失败
结论:只要在方法上加事务,表示此方法异常与否都会交给事务,t2 抛出异常都会给事务,只是此事务传播行为是 t1 的事务
3、t1 REQUIRED、t2 REQUIRES_NEW
- 只有 t1 异常,t1 插入失败,t2 插入成功
- 只有 t2 异常,都插入失败
- t2异常,t1捕获异常,t2失败,t1成功
结论:t1、t2为两个事务,t2 不支持当前事务,开启新事务;t1 调用 t2,t2 异常会影响 t1
4、t1 REQUIRES_NEW、t2 REQUIRED
此结果和第2中场景一样,t1、t2 为同一个事务
4.总结
-
外部方法事务REQUIRED或REQUIRES_NEW,内部调用方法事务为 REQUIRED、SUPPORTS、MANDATORY 效果一样,即它们都使用同一个事务,只是此事务监听两个方法
-
只有t1、t2 方法都不报错,数据插入成功
-
其中一个报错,数据都会回滚
-
-
外部方法事务REQUIRED,内部调用
- REQUIRES_NEW:两个事务,t1 调用 t2,t2 异常会影响 t1,t2 数据是否回滚有当前 t2 方法是否异常决定
- NOT_SUPPORTED:非事务,t2 异常不会回滚,t2 异常会影响 t1是否回滚
- NEVER:报错,都会插入失败,只有外部不开启事务才会成功
- NESTED:类似 REQUIRES_NEW 功能