该枚举类定义了spring的事务传播行为,在TransactionDefinition接口中也有定义,其实引用的就是该接口的属性
为了方便测试,需要创建三个类,由于我使用的是hikari数据源,恰好spring boot中有,所以直接使用spring boot的,引入pom文件,即可使用HikarCP,当然也可以去maven仓库搜索HikarCP
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
第一个是主函数类,代码如下
@Configuration
@EnableTransactionManagement
public class TestMain {
@Bean
public DataSource dataSource() {
HikariDataSource ds = new HikariDataSource();
ds.setJdbcUrl("jdbc:mysql://localhost:3306/swttest?serverTimezone=UTC&&useSSL=false");
ds.setUsername("root");
ds.setPassword("Aa111111!");
return ds;
}
@Bean
public JdbcTemplate jdbcTemplate() {
JdbcTemplate jdbc = new JdbcTemplate();
jdbc.setDataSource(dataSource());
return jdbc;
}
@Bean
public PlatformTransactionManager platformTransactionManager() {
DataSourceTransactionManager manager = new DataSourceTransactionManager(dataSource());
return manager;
}
public static void main(String[] args) throws Throwable {
// 依赖容器的话必须要开启@EnableTransactionManagement注解,表示告诉spring我要使用事务
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(
"TestMain所在的包名");
Creater1 tt = ctx.getBean(Creater1.class);
tt.create();
}
第二个是事务类1(类名叫Creater1)
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
@Component
public class Creater1 {
@Autowired
JdbcTemplate jdbcTemplate;
@Autowired
Creater2 creater2;
@Transactional(rollbackFor = Exception.class)
public void create() {
jdbcTemplate.update("insert into t1(value) values ('create1')");
creater2.create();
}
}
第三个是事务类2
@Component
public class Creater2 {
@Autowired
JdbcTemplate jdbcTemplate;
@Transactional(rollbackFor = Exception.class)
public void create() {
jdbcTemplate.update("insert into t1(value) values ('create2')");
}
}
接下来我将通过修改事务类2(Creater2)来演示不同的传播行为
1.NEVER
表示创建执行的时候,不允许存在其他事务,否则报错,将Creater2做如下修改
@Transactional(rollbackFor = Exception.class)
改为
@Transactional(rollbackFor = Exception.class, propagation = Propagation.NEVER)
运行TestMain,会发现系统报错
2.REQUIRED
默认的模式,不多阐述
3.NESTED
内层事务报错回滚,不会影响到外层事务正常提交,但是要注意只有外部事务结束后它才会被提交,或者回滚,所以写法要注意
1)外层事务应该try住,确保外层事务执行完毕,否则内层事务报错,会抛出来,导致外层也报错
2)不需要设置额外的savepoint,内部事务会自动回滚到事务开始处
将Creater1做如下修改
jdbcTemplate.update("insert into t1(value) values ('create1')");
creater2.create();
-------------改为-------------
try {
jdbcTemplate.update("insert into t1(value) values ('create1')");
creater2.create();
} catch (Exception e) {
}
将Creater2代码变成下面这样
@Transactional(rollbackFor = Exception.class, propagation = Propagation.NESTED)
public void create() {
jdbcTemplate.update("insert into t1(value) values ('create2')");
throw new RuntimeException("MYS");//手动抛异常
}
运行TestMain,会发现create1插入到数据库,而create2没有入库
日记:NESTED行为下,外层报错,会连同内层一起回滚,但是本文没有演示这种情况
4.REQUIRES_NEW
将Creater1代码变成下面这样
@Transactional(rollbackFor = Exception.class)
public void create() {
jdbcTemplate.update("insert into t1(value) values ('create1')");
creater2.create();
throw new RuntimeException("MYS");
}
将Creater2代码变成下面这样
@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
public void create() {
jdbcTemplate.update("insert into t1(value) values ('create2')");
}
运行TestMain,会发现create2插入到数据库,而create1没有入库
日记:与NESTED的区别及共同点
内层事务如果是NESTED:外层异常,内外两层都回滚
内层事务如果是REQUIRES_NEW:外层异常,外层回滚,内层不回滚
共同点:内层异常,外层try住之后,内层回滚,外层不回滚
还有另外三种传播行为,但是实在是没有必要再去阐述,因为相对来说较为简单,理解了上面四种,另外三种甚至连测试都不需要,脑补就可以