当前位置: 代码网 > it编程>编程语言>Java > spring中@Transactional 注解失效的原因及解决办法

spring中@Transactional 注解失效的原因及解决办法

2024年06月10日 Java 我要评论
前言面试中经常会被问到事务失效的场景有哪些,其实在开发中,若是不了解事务失效的场景,当你觉得加了事务,就会回滚,就大错特错了,今天就来了解一下吧。一、@transactional 属性介绍1.事务的传

前言

面试中经常会被问到事务失效的场景有哪些,其实在开发中,若是不了解事务失效的场景,当你觉得加了事务,就会回滚,就大错特错了,今天就来了解一下吧。

一、@transactional 属性介绍

1.事务的传播行为:propagation

这就是我们常说的事务的七种传播行为,默认值:propagation.required

属性解释
propagation.required如果当前存在事务,则加入该事务,如果当前不存在事务,则创建一个新的事务。
propagation.supports如果当前存在事务,则加入该事务;如果当前不存在事务,则以非事务的方式继续运行。
propagation.mandatory如果当前存在事务,则加入该事务;如果当前不存在事务,则抛出异常。
propagation.requires_new重新创建一个新的事务,如果当前存在事务,暂停当前的事务。
propagation.not_supported以非事务的方式运行,如果当前存在事务,暂停当前的事务。
propagation.never以非事务的方式运行,如果当前存在事务,则抛出异常。
propagation.nested嵌套事务。

2.事务的隔离级别:isolation

这就是我们常说的事务的隔离级别,默认值:isolation.default

属性解释
isolation.default使用底层数据库默认的隔离级别
isolation.read_uncommitted读未提交
isolation.read_committed读已提交
isolation.repeatable_read可重复读
isolation.serializable串行化

3.事务的超时时间:timeout

  • timeout :事务的超时时间,默认值为 -1。如果超过该时间限制但事务还没有完成,则自动回滚事务。

4.事务的回滚类型:rollbackfor

  • rollbackfor :用于指定能够触发事务回滚的异常类型,可以指定多个异常类型。

二、@transactional 失效场景

1.同一个类中方法调用,注解失效

场景重现:当调用 savecabinet 方法时,save 方法中出异常不会回滚,如下代码

@service
public class cabinetserviceimpl implements cabinetservice {
    @autowired
    private cabinetmapper cabinetmapper;
    @override
    public void savecabinet(cabinet cabinet) {
        save(cabinet);
    }
    @transactional(rollbackfor = exception.class)
    public void save(cabinet cabinet) {
        cabinetmapper.insert(cabinet);
        int i = 1/0;	// 模拟出错
    }
}

原因分析:spring 中采用动态代理来实现事务,当同一个类调用 save 时,不是代理类在调用,所以扫描不到事务注解

解决方案1:将 save 方法提到另一个类中,savecabinet 调用另一个类的 save 方法(推荐使用)

// 当前类
@service
public class cabinetserviceimpl implements cabinetservice {
    @autowired
    private methodtest methodtest;
    @override
    public void savecabinet(cabinet cabinet) {
        methodtest.save(cabinet);
    }
}
// 另一个类
@service
public class methodtest {
    @autowired
    private cabinetmapper cabinetmapper;
    
    @transactional(rollbackfor = exception.class)
    public void save(cabinet cabinet) {
        cabinetmapper.insert(cabinet);
        int i = 1/0; 	// 模拟出错
    }
}

解决方案2:在 cabinetserviceimpl 中自己注入自己,用自己的代理类调用 save 方法,即 cabinetserviceimpl.save()

@service
public class cabinetserviceimpl implements cabinetservice {
    @autowired
    private cabinetmapper cabinetmapper;
    
    @autowired
    private cabinetserviceimpl cabinetserviceimpl;
    
    @override
    public void savecabinet(cabinet cabinet) {
        cabinetserviceimpl.save(cabinet);
    }
    
    @transactional(rollbackfor = exception.class)
    public void save(cabinet cabinet) {
        cabinetmapper.insert(cabinet);
        int i = 1/0;	// 模拟出错
    }
}

2.异常被 catch “吃了”,注解失效

场景重现:当调用 savecabinet 方法时,方法体被 try-catch 了,不会回滚,如下代码

@service
public class cabinetserviceimpl implements cabinetservice {
    @autowired
    private cabinetmapper cabinetmapper;
    
    @override
    @transactional(rollbackfor = exception.class)
    public void savecabinet(cabinet cabinet) {
        try {
            cabinetmapper.insert(cabinet);
            int i = 1/0;	// 模拟出错
        } catch (exception e) {
            e.printstacktrace();
        }
    }
}

原因分析:异常直接捕获没有向上抛出,而是自己处理了,注解捕获不到异常,所以失效

解决方案:将 catch 到的异常向上抛出 throw new exception(e);,异常交给注解处理

@service
public class cabinetserviceimpl implements cabinetservice {
    @autowired
    private cabinetmapper cabinetmapper;
    
    @override
    @transactional(rollbackfor = exception.class)
    public void savecabinet(cabinet cabinet) throws exception {
        try {
            cabinetmapper.insert(cabinet);
            int i = 1/0;	// 模拟出错
        } catch (exception e) {
            e.printstacktrace();
            throw new exception(e);
        }
    }
}

3.@transactional 应用在非 public 修饰的方法上

  • 场景重现:当调用的方法不是 public 修饰时,不会回滚,事务失效
  • 原因分析:在 spring aop 代理时,事务拦截器会在目标方法的执行前后进行拦截,其中的方法会去获取 transactional 注解的一些信息,而其中的方法会去检查目标方法是否被 public 修饰,不是的话获取不到注解信息
  • 解决方案:将调用的方法访问修饰符改为 public

4.@transactional 注解属性 rollbackfor 设置错误

场景重现:当注解没有指定 rollbackfor 属性时,如下若抛出 filenotfoundexception 异常则不会回滚

@service
public class cabinetserviceimpl implements cabinetservice {
    @autowired
    private cabinetmapper cabinetmapper;
    
    @override
    @transactional
    public void savecabinet(cabinet cabinet) throws ioexception {
        cabinetmapper.insert(cabinet);
        // 模拟出错
        throw new filenotfoundexception();
    }
}

原因分析:spring 默认抛出未检查异常,继承 runtimeexception 或者 error 才会回滚,其他异常如 filenotfoundexception 则不会回滚

解决方案:指定 rollbackfor 属性,一般指定 @transactional(rollbackfor = exception.class) 即可,也可以指定自定义异常

@service
public class cabinetserviceimpl implements cabinetservice {
    @autowired
    private cabinetmapper cabinetmapper;
    
    @override
    @transactional(rollbackfor = exception.class)
    public void savecabinet(cabinet cabinet) throws ioexception {
        cabinetmapper.insert(cabinet);
        // 模拟出错
        throw new filenotfoundexception();
    }
}

5.@transactional 注解属性 propagation 设置错误

  • 使用了不支持事务的传播属性

6.数据库引擎不支持事务

  • 数据库不支持事务

总结

到此这篇关于spring中@transactional 注解失效的原因及解决办法的文章就介绍到这了,更多相关spring @transactional 失效内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!

(0)

相关文章:

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

发表评论

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