当前位置: 代码网 > it编程>编程语言>Java > Spring的@Transactional嵌套解读

Spring的@Transactional嵌套解读

2024年11月09日 Java 我要评论
事务嵌套和局部回滚的问题,很是费解。本文将做一个详细的测试,加强对spring的@transactional 理解和使用1、两个单独不干扰事务@requestmapping("/test") p

事务嵌套局部回滚的问题,很是费解。

本文将做一个详细的测试,加强对spring的@transactional 理解和使用

1、两个单独不干扰事务

	@requestmapping("/test")
    public void test() {
        lovefile test1 = new lovefile();
        test1.setfileuuid(get32uuid());
        test1.setfilename("test1");
        lovefileservice.test1(test1);
        lovefile test2 = new lovefile();
        test2.setfileuuid(null);
        test2.setfilename("test2");
        lovefileservice.test2(test2);
    }
    @override
    @transactional
    public void test1(lovefile lovefile) {
        mapper.insertselective(lovefile);

    }
    @override
    @transactional
    public void test2(lovefile lovefile) {
        mapper.insertselective(lovefile);
    }

由于我给第二个test2插入主键为空,报错。因此库里只有一条

总结:两个单独事务互不干扰。错了就是错了,很好理解

2、普通嵌套事务

	@requestmapping("/test")
    @transactional
    public void test() {
        lovefile test1 = new lovefile();
        test1.setfileuuid(get32uuid());
        test1.setfilename("test1");
        lovefileservice.test1(test1);
        lovefile test2 = new lovefile();
        test2.setfileuuid(null);
        test2.setfilename("test2");
        lovefileservice.test2(test2);
    }

子事务不变,删除刚才的数据后,测试。

库里空空。

总结:加在外层的事务起了作用,在test2报错时回滚了test1

3、嵌套事务、三个子事务中间一个加(propagation = propagation.requires_new)

propagation取值:

  • required(默认值):在有transaction状态下执行;如当前没有transaction,则创建新的transaction;
  • supports:如当前有transaction,则在transaction状态下执行;如果当前没有transaction,在无transaction状态下执行;
  • mandatory:必须在有transaction状态下执行,如果当前没有transaction,则抛出异常illegaltransactionstateexception;
  • requires_new:创建新的transaction并执行;如果当前已有transaction,则将当前transaction挂起;
  • not_supported:在无transaction状态下执行;如果当前已有transaction,则将当前transaction挂起;
  • never:在无transaction状态下执行;如果当前已有transaction,则抛出异常illegaltransactionstateexception。

本文重点研究 requires_new

@requestmapping("/test")
    @transactional
    public void test() {
        lovefile test1 = new lovefile();
        test1.setfileuuid(get32uuid());
        test1.setfilename("test1");
        lovefileservice.test1(test1);
        lovefile test3 = new lovefile();
        test3.setfileuuid(get32uuid());
        test3.setfilename("test3");
        lovefileservice.test3(test3);
        lovefile test2 = new lovefile();
        test2.setfileuuid(null);
        test2.setfilename("test2");
        lovefileservice.test2(test2);
    }
@override
    @transactional
    public void test1(lovefile lovefile) {
        mapper.insertselective(lovefile);
    }
    @override
    @transactional
    public void test2(lovefile lovefile) {
        mapper.insertselective(lovefile);
    }
    @override
    @transactional(propagation = propagation.requires_new)
    public void test3(lovefile lovefile) {
        mapper.insertselective(lovefile);
    }

requires_new官方文档解释:

create a new transaction, and suspend the current transaction if one exists.

意思是,创建一个新事务,如果当前存在事务,将这个事务挂起。

也就是说test3,是独立于外层事务的单独的事务,他已经挂起的外层事务,他要把自己的问题处理完再去管别人。

因此结果是

总结:不给子事务加(propagation = propagation.requires_new),相当于加入了外层事务。加了则当做新的子事务。

test2报错了,影响了test1 ,却不能影响test3,已经说明了此问题

4、嵌套事务、三个子事务中间一个加(propagation = propagation.requires_new),偏偏出错了

	@requestmapping("/test")
    @transactional
    public void test() {
        lovefile test1 = new lovefile();
        test1.setfileuuid(get32uuid());
        test1.setfilename("test1");
        lovefileservice.test1(test1);
        lovefile test3 = new lovefile();
        test3.setfileuuid(null);
        test3.setfilename("test3");
        lovefileservice.test3(test3);
        lovefile test2 = new lovefile();
        test2.setfileuuid(get32uuid());
        test2.setfilename("test2");
        lovefileservice.test2(test2);
    }

我把test3主键为空,因此他会有sqlexception。来研究下,他会不会影响整体。

总结: 虽然他是开辟的新事物,但是出错了,还是会牵连整体的

至此,(propagation = propagation.requires_new) 已经解释清楚了。

我觉得子事务,不加这个东西的话不叫子事务。不加的话直接加入了外层事务。那还要个单单 @transactional何用?

建议大家对子事务都加上这条件。

rollbackfor官方文档解释:

在@transactional注解中如果不配置rollbackfor属性,那么事物只会在遇到runtimeexception的时候才会回滚,加上rollbackfor=exception.class,可以让事物在遇到非运行时异常时也回滚

5、rollbackfor了一个异常,却会报另外异常

@requestmapping("/test")
    @transactional
    public void test() {
        lovefile test1 = new lovefile();
        test1.setfileuuid(get32uuid());
        test1.setfilename("test1");
        lovefileservice.test1(test1);
        lovefile test3 = new lovefile();
        test3.setfileuuid(null);
        test3.setfilename("test3");
        lovefileservice.test3(test3);
        lovefile test2 = new lovefile();
        test2.setfileuuid(get32uuid());
        test2.setfilename("test2");
        lovefileservice.test2(test2);
    }
 @override
    @transactional
    public void test1(lovefile lovefile) {
        mapper.insertselective(lovefile);
    }
    @override
    @transactional
    public void test2(lovefile lovefile) {
        mapper.insertselective(lovefile);
    }
    @override
    @transactional(rollbackfor=sqlexception.class,propagation = propagation.requires_new)
    public void test3(lovefile lovefile) {
        int  a = 1/0;
       // mapper.insertselective(lovefile);
    }

这个test3,一定会报 by zero,他会报错,我来测试下,他会不会回滚,影响他人。

如此是影响了。

因为外层事务没有声明异常,他看到一个内部报by zero 了,而自己是runtimeexception,包含在内。因此自己回滚了。

test1 是直接加入整体的。没加出数据,就说明了此问题。

我其实是像测试,test3是否回滚,这个测试案例不合适。

@requestmapping("/test")
    @transactional
    public void test() {
        lovefile test1 = new lovefile();
        test1.setfileuuid(get32uuid());
        test1.setfilename("test1");
        lovefileservice.test1(test1);
        lovefile test3 = new lovefile();
        test3.setfileuuid(get32uuid());
        test3.setfilename("test3");
        lovefileservice.test3(test3);
        lovefile test2 = new lovefile();
        test2.setfileuuid(get32uuid());
        test2.setfilename("test2");
        lovefileservice.test2(test2);
    }
   @override
    @transactional
    public void test1(lovefile lovefile) {
        mapper.insertselective(lovefile);
    }
    @override
    @transactional
    public void test2(lovefile lovefile) {
        mapper.insertselective(lovefile);
    }
    @override
    @transactional(rollbackfor=sqlexception.class,propagation = propagation.requires_new)
    public void test3(lovefile lovefile) {
        mapper.insertselective(lovefile);
        int  a = 1/0;
    }

我给了test3 正常的主键,让他添加成功,却在后面 by zero .因为我认为他不会回滚,这次应该是test3加进去了,test1和test2被外层事务回滚。

但是结果是:

我又做了一些测试,发现rollbackfor=sqlexception.class 这样的声明一个特定的异常没有任何效果。

rollbackfor 用途不大,就是声明rollbackfor=exception.class 时比 runtimeexception 广一些。

其他时用途不大。

如果想让特定异常不回滚,还不如用 try catch。只要异常被catch住,不被方法知道,就不会出现error ,也就不会回滚。

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com