Spring Boot 事物回滚实验

1.开启事物

通过注解开启

@EnableTransactionManagement 

提示: Spring Boot  以Spring 5.0为基础的版本无需注解自动开启事物

2.测试事物

首先查询数据库中的表
默认表
默认表中有一条数据;

2.1默认不用事物

首先是不开启,创建两个数据代码如下:

public void test() {
        SystemMenu m1=new SystemMenu();
        m1.setName("test2");
        m1.setTerminal("00");
        menuExtMapper.insertSelective(m1);
        SystemMenu m2=new SystemMenu();
        m2.setId(1);//重复ID 数据库会报错
        m2.setName("test3");
        m2.setTerminal("00");
    }

执行上面的代码:
执行错误
通过控制台我们可以看到已经输出了 主键冲突的异常,我们再看看数据库:
执行结果数据库查询
通过数据库查询可以看到,test2数据已经存入数据库,test3肯定就是失败了,同时证明了默认没开启事物,遇到错误并不会回滚。

2.2通过注解@Transactional开启事物

首先我们将数据库还原,删除掉test2数据,如下:
Spring Boot 事物回滚实验
程序代码如下:

@Transactional
    public void test() {
        SystemMenu m1=new SystemMenu();
        m1.setName("test2");
        m1.setTerminal("00");
        menuExtMapper.insertSelective(m1);
        SystemMenu m2=new SystemMenu();
        m2.setId(1);//重复ID 数据库会报错
        m2.setName("test3");
        m2.setTerminal("00");
        menuExtMapper.insertSelective(m2);
    }

执行上方代码:
同样的执行错误

从控制台看,同样输出了主键冲突的错误,我们再看看数据库中的数据:
数据库数据

从数据可以看到,并没有数据存入。这里也就证明了事物开启成功,遇到错误就会同一回滚保证了数据的一致性。

提示:默认情况下,@Transactional注解只对RuntimeException及其子类异常有效。

证明上方的观点,下面以代码示例:

@Transactional
    public void test() throws Exception {
        SystemMenu m1=new SystemMenu();
        m1.setName("test2");
        m1.setTerminal("00");
        menuExtMapper.insertSelective(m1);

        if (1==1){
            throw new Exception("EEEEEE");
        }
        SystemMenu m2=new SystemMenu();
        m2.setId(1);//重复ID 数据库会报错
        m2.setName("test3");
        m2.setTerminal("00");
        menuExtMapper.insertSelective(m2);
    }

上方代码,在处理数据中间主动抛出了Exception异常,执行代码观察控制台以及数据库:

控制台
控制台

 

数据库
数据库

通过上方我们可以看到,抛出Exception的情况下事物并没有生效,test2已经存入数据库中。

提示:
可以通过注解@Transactional中的rollbackFor 来指定事物遇到那些异常会启用,同样也可以通过noRollbackFor指定那些异常不会滚
例如下方的配置事物遇到所有的Exception都会回滚
@Transactional(rollbackFor = {Exception.class})

2.3 启用事物的方法内部调用其他方法出现异常会回滚吗

首先我们蒋数据库恢复如下:
恢复数据
编写测试代码如下:

@Transactional(rollbackFor = {Exception.class})
    public void test() throws Exception {
        SystemMenu m1=new SystemMenu();
        m1.setName("test2");
        m1.setTerminal("00");
        menuExtMapper.insertSelective(m1);
        oo();
    }

    public void oo(){
        SystemMenu m2=new SystemMenu();
        m2.setId(1);
        m2.setName("test2");
        m2.setTerminal("00");
        menuExtMapper.insertSelective(m2);
    }

执行代码,观察控制台和数据库:

控制台
控制台
数据库
数据库

通过上方实验,我们可以看到事物生效了。所以内部调用其他方法出现异常同样会触发事物

提示:
调用的方法需没有标注注解@Transactional,如果标注注解会有几种情况,后方说明。

2.4 启用事物的方法内部调用其他启用事物的方法

编写测试代码1:

    @Transactional(rollbackFor = {Exception.class})
    public void test() throws Exception {
        oo();

        SystemMenu m1=new SystemMenu();
        m1.setId(1);
        m1.setName("test2");
        m1.setTerminal("00");
        menuExtMapper.insertSelective(m1);

    }

    @Transactional
    public void oo(){
        SystemMenu m2=new SystemMenu();
        m2.setName("test3");
        m2.setTerminal("00");
        menuExtMapper.insertSelective(m2);
    }

执行测试代码并观察控制台和数据库:

控制台
控制台

 

数据库
数据库

通过上方实验,我们可以知道默认情况下,@Transactional注解的方法调用@Transactional注解的方法也是可以事物统一回滚的。

注意:

  • 调用的方法上指定的Exception范围必须包含被调用方抛出异常的范围,否则可能出现不一致
  • 上方还涉及到一个知识点,事物的传播级别,默认情况下事物的传播级别为"REQUIRED",即:如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。

所以上方两个方法的事物会被整合成一个事物处理。@Transactional注解也可以通过propagation 来指定传播级别,如下:
@Transactional(propagation = Propagation.REQUIRED)

事物传播的几个级别分别为:

  • REQUIRED(@Transactional注解默认值): 如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。
  • SUPPORTS:前方法不需要事务上下文,但是如果存在当前事务的话,那么这个方法会在这个事务中运行。
  • MANDATORY:该方法必须在事务中运行,如果当前事务不存在,则会抛出一个异常。不会主动开启一个事务。
  • REQUIRES_NEW:前方法必须运行在它自己的事务中。一个新的事务将被启动,如果存在当前事务,在该方法执行期间,当前事务会被挂起(如果一个事务已经存在,则先将这个存在的事务挂起)。如果使用JTATransactionManager的话,则需要访问TransactionManager。
  • NOT_SUPPORTED:该方法不应该运行在事务中,如果存在当前事务,在该方法运行期间,当前事务将被挂起。如果使用JTATransactionManager的话,则需要访问TransactionManager。
  • NEVER:表示当前方法不应该运行在事务上下文中,如果当前正有一个事务在运行,则会抛出异常。
  • NESTED:如果当前已经存在一个事务,那么该方法将会在嵌套事务中运行。嵌套的事务可以独立于当前事务进行单独地提交或回滚。如果当前事务不存在,那么其行为与REQUIRED一样。嵌套事务一个非常重要的概念就是内层事务依赖于外层事务。外层事务失败时,会回滚内层事务所做的动作。而内层事务操作失败并不会引起外层事务的回滚。

(未完待续)后续继续更新实验….

 

原创文章,作者:kepupublish,如若转载,请注明出处:https://blog.ytso.com/243756.html

(0)
上一篇 2022年4月11日
下一篇 2022年4月11日

相关推荐

发表回复

登录后才能评论