事务传播行为 @Transactional

84 阅读5分钟

文章目录


前言

事务传播行为: 指的是在项目中开启多个事务后,他们之间的影响关系;


一、事务是什么?

  • 逻辑上是一组操作,要么执行,要不都不执行。在平时开发中,主要是针对数据库而言的,比如说 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

以非事务方式运行,如果当前存在事务,则抛出异常。

本文转自 jimolvxing.blog.csdn.net/article/det…,如有侵权,请联系删除。