文章目录
- 前言
- 一、事务是什么?
- 二、使用步骤
- 开始验证
- 1.验证REQUIRED
- 2.验证 REQUIRES_NEW
- 3.同一个类中的两个方法
- 总结
前言
事务传播行为: 指的是在项目中开启多个事务后,他们之间的影响关系;
一、事务是什么?
- 逻辑上是一组操作,要么执行,要不都不执行。在平时开发中,主要是针对数据库而言的,比如说 MySQL的innodb引擎;
- 事务能否生效,主要取决于数据库引擎是否支持事务,MySQL 的 InnoDB 引擎是支持事务的,但 MyISAM 就不支持。其次是 是否正确开启了事务;
二、使用步骤
事务可以保证在dml操作之后,如果发生异常,可以将之前的dml操作进行回滚,保证原子性
这里重点介绍 REQUIRES_NEW 和 REQUIRED , REQUIRED是事务的默认传播值(不设置默认为REQUIRED)
- REQUIRES_NEW 意思是开启一个新事务
- 创建一个新的事务,如果当前存在事务,则把当前事务挂起。也就是说不管外部方法是否开启事务,Propagation.REQUIRES_NEW 修饰的内部方法都会开启自己的事务,且开启的事务与外部的事务相互独立,互不干扰。
- REQUIRED 意思是
- 如果外部方法没有开启事务的话,Propagation.REQUIRED 修饰的内部方法会开启自己的事务,且开启的事务相互独立,互不干扰。
- 如果外部方法开启事务并且是 Propagation.REQUIRED 的话,所有 Propagation.REQUIRED 修饰的内部方法和外部方法均属于同一事务 ,只要一个方法回滚,整个事务都需要回滚。
开始验证
代码如下:
第一个service
@Service
public class TestService {
@Autowired
JdbcTemplate jdbcTemplate;
@Autowired
Test1Service test1Service;
@SneakyThrows
@Transactional(propagation = Propagation.REQUIRED)
public void test() {
jdbcTemplate.execute("INSERT INTO `yu_team` (\n" +
" `name`,\n" +
" `type`,\n" +
" `space_id`,\n" +
" `remark`,\n" +
" `create_time`,\n" +
" `update_time`\n" +
")\n" +
"VALUES\n" +
" (\n" +
" 'name',\n" +
" 0,\n" +
" 2,\n" +
" 'remark',\n" +
" '2022-10-19 07:04:46',\n" +
" '2022-10-19 07:04:46'\n" +
" );\n");
test1Service.insert();
//insert();
System.out.println(4/0);
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void insert() {
jdbcTemplate.execute("INSERT INTO `yu_team` (\n" +
" `name`,\n" +
" `type`,\n" +
" `space_id`,\n" +
" `remark`,\n" +
" `create_time`,\n" +
" `update_time`\n" +
")\n" +
"VALUES\n" +
" (\n" +
" '同一个方法内',\n" +
" 0,\n" +
" 2,\n" +
" 'remark',\n" +
" '2022-10-19 07:04:46',\n" +
" '2022-10-19 07:04:46'\n" +
" );\n");
}
}
第二个service
@Service
public class Test1Service {
@Autowired
JdbcTemplate jdbcTemplate;
@Transactional(propagation = Propagation.REQUIRED)
public void insert() {
jdbcTemplate.execute("INSERT INTO `yu_team` (\n" +
" `name`,\n" +
" `type`,\n" +
" `space_id`,\n" +
" `remark`,\n" +
" `create_time`,\n" +
" `update_time`\n" +
")\n" +
"VALUES\n" +
" (\n" +
" '非同一个类的方法',\n" +
" 0,\n" +
" 2,\n" +
" 'remark',\n" +
" '2022-10-19 07:04:46',\n" +
" '2022-10-19 07:04:46'\n" +
" );\n");
}
}
1.验证REQUIRED
- 开启两个事务: 都设置为REQUIRED(或者啥都不设置)
- TestService 和 Test1Service 分别设置为 REQUIRED
- 由于 System.out.println(4/0); 所以会报异常,而由于传播特性,两个方法应该所属于同一个事务,所以都应该回滚
结果: 两个都未成功插入到数据库,都回滚了
2.验证 REQUIRES_NEW
- 将 Test1Service中的事务传播设置为 REQUIRES_NEW
- 由于REQUIRES_NEW 相当于新开启了一个事务
- 即使 System.out.println(4/0); 第一个方法中会报异常,但是 Test1Service中相当于开启了新的事务,所以第一个方法会回滚,但是 Test1Service 中的应该不会回滚;
- 数据库中已经插入了一条数据
佐证了我们的猜想,说明第一个回滚了,但是第二个正常执行插入了,且没有回滚;
3.同一个类中的两个方法
- 细心的小伙伴会发现,整体验证中,其实 TestService中有一个方法 insert() ,但是被注释掉了;
- 那么当它开启事务传播特性REQUIRES_NEW 与 Test1Service中开启 的 Test1Service 一样嘛??
直接上答案:
答案是: 不一样,无论改成什么,它都会跟着public void test()方法中的报错一起回滚!! 什么原因呢?
应该是: @Transactional 是基于注解的, 也就是基于aop实现的,那么当内部调用的时候,是无法走代理的,由于spring 默认的代理为基于接口的动态代理,目标需要为一个接口的实现类,才能被代理,那么内部调用就不会有代理,也就无法使添加的注解生效
总结
虽然事务的传播枚举还有很多,但是其实,只要掌握这两个就够了,其他的了解即可;
03、PROPAGATION_NESTED
如果当前存在事务,就在当前事务内执行;否则,就执行与 PROPAGATION_REQUIRED 类似的操作。
04、PROPAGATION_MANDATORY
如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。
05、PROPAGATION_SUPPORTS
如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
06、PROPAGATION_NOT_SUPPORTED
以非事务方式运行,如果当前存在事务,则把当前事务挂起。
07、PROPAGATION_NEVER
以非事务方式运行,如果当前存在事务,则抛出异常。