• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

@Transactional注解失效的情况

武飞扬头像
网恋褙骗八万
帮助2

该篇博客主要介绍@Transactional注解失效的12种情况,我是看b站的一个up主进行搬运总结的,希望对我、对你都有一点一点的帮助。


1、访问权限的问题

private、default、protected、public,他们的权限从左到右,依次变大。

把某些事务方法,定义成错误的访问权限,就会导致事务功能出问题。即不是public类型就会失效

2、方法用final修饰

事务是通过AOP完成,即通过代理的方式完成,将一个方法定义成final意味着该方法不能被重写了,那么事务就失效了。

注意:如果某个方法是static时,同样无法通过动态代理,变成事务方法。

3、方法内部调用

在某个Service类的某个方法中,调用另外一个是事务方法,比如:

  1.  
    @Service
  2.  
    public class UserService{
  3.  
    @Autowired
  4.  
    private UserService userService;
  5.  
     
  6.  
    //@Transactional
  7.  
    public void add(UserModel userModel){
  8.  
    userMapper.insertUser(userModel);
  9.  
    updateStatus(userModel);
  10.  
    }
  11.  
     
  12.  
    @Transactional
  13.  
    public void updateStatus(UserModel userModel){
  14.  
    doSameThing();
  15.  
    }
  16.  
    }
学新通

以上方法中采用的是this对updateStatus()方法进行调用的,因此updateStatus不能生成事务。

解决以上问题可以将自身进行注入的方式

  1.  
    @Service
  2.  
    public class ServiceA{
  3.  
    @AutoWired
  4.  
    private ServiceA serviceA;
  5.  
     
  6.  
    public void save(User user){
  7.  
    queryData1();
  8.  
    queryData2();
  9.  
    serviceA.daSave(user);
  10.  
    }
  11.  
    @Transactional(rollbackFor=Exception.class)
  12.  
    public void daSave(User user){
  13.  
    addData1();
  14.  
    uodateData2();
  15.  
    }
  16.  
    }
学新通

4、未被spring管理

在开发过程中容易忽略一些细节问题,比如忘记了@Controller、@Service、@Component、@Repository等注解

没使用以上注解交给spring进行管理,那么事务就不会生效。

5、多线程调用

在实际项目开发中,多线程的使用场景还是挺多的。如果spring事务用在多线程场景中,会有什么问题

我们说的同一事物,其实是指同一个数据库连接,只有拥有同一个数据库连接才能同时提交和回滚。如果在不同的线程,拿到的数据库连接肯定是不一样的,所以是不同的事务。

6、表不支持事务

mysql5之前,默认的数据库引擎是MYISAM,它的好处是:索引文件和数据文件是分开存储的,对于查多写少的单表操作,性能比innodb更好。

7、未开启事务

如果你使用的是springboot项目,那么springboot通过DataSourceTransactionaManagerAutoConfiguration类,已经默默地帮你开启了事务,你只需要配置spring.datasource相关参数即可。

如果使用的还是传统的spring项目,那么你需要在applicationContext.xml文件中,手动配置事务相关参数。如果忘了配置,事务肯定是不会生效的。

二、事务不回滚

8、错误的传播特性

其实,在使用@Transactional注解时,是可以指定propagation参数的。

该参数得作用是指定事务的传播特性,spring目前支持7中传播特性:

REQUIRED

SUPPORTS

MANDATORY

REQUIRES_NEW

NOT_SUPPORTED

NEVER

NESTED

如果事务设置成Propagation.NEVER,这种类型的传播特性不支持事务,如果有事务则抛出异常。

9、自己吞了异常

事物不会回滚,最常见的问题是:开发者在代码中手动try...catch了异常。比如

  1.  
    @slf4j
  2.  
    @Service
  3.  
    public class UserService{
  4.  
    @Transactional
  5.  
    public void add(UserModel userModel){
  6.  
    try{
  7.  
    saveData(userModel);
  8.  
    updateData(userModel);
  9.  
    }catch(Exception e){
  10.  
    log.error(e.getMessage,e);
  11.  
    }
  12.  
    }
  13.  
    }

这种情况下spring事务当然不会回滚,因为开发者自己捕获了异常,又没有手动抛出,换句话说就是把异常吞掉了。

如果想要spring事务能够正常回滚,必须抛出它能够处理的异常,如果没有抛出异常,则spring认为程序是正常的。

10、手动抛出了别的异常

即使开发者没有手动捕获异常,但是抛出的异常不正确,spring事务也不会回滚。

  1.  
    @slf4j
  2.  
    @Service
  3.  
    public class UserService{
  4.  
    @Transactional
  5.  
    public void add(UserModel userModel) throws Exception{
  6.  
    try{
  7.  
    saveData(userModel);
  8.  
    updateData(userModel);
  9.  
    }catch(Exception e){
  10.  
    log.error(e.getMessage,e);
  11.  
    throw new Exception(e);
  12.  
    }
  13.  
    }
  14.  
    }

上面的情况,开发人员自己捕获了异常:Exception,事务同样不会回滚。

因为spring事务,默认情况下只会回滚RuntimeException(运行时异常)和Error(错误),对于普通的Exception(非运行时异常),它不会回滚。

11、自定了回滚异常

在使用@Transactional注解声明事务时,有时我们想自定义回滚异常,spring也是支持的。可以通过设置rollbackFor参数,来完成这个功能。

  1.  
    @slf4j
  2.  
    @Service
  3.  
    public class UserService{
  4.  
    @Transactional(rollbackFor = BusinessException.class)
  5.  
    public void add(UserModel userModel) throws Exception{
  6.  
    saveData(userModel);
  7.  
    updateData(userModel);
  8.  
    }
  9.  
    }

如果在执行上面这段代码,保存和更新数据时,程序报错了,抛出了SqlException、DuplicateKeyException等异常。而BusinessException是我们自定义的异常,报错的异常不属于BusinessException,所以事务也不会回滚。

12、嵌套事务回滚多了

  1.  
    public class UserService{
  2.  
    @AutoWired
  3.  
    private UserMapper userMapper;
  4.  
     
  5.  
    @AutoWired
  6.  
    private RoleService reloService;
  7.  
     
  8.  
    @Transactional
  9.  
    public void add(UserModel userModel) throws Exception{
  10.  
    userMapper.insertUser(userModel);
  11.  
    roleService.doOtherThing();
  12.  
    }
  13.  
    }
  14.  
     
  15.  
    @Service
  16.  
    public class RoleService{
  17.  
    @Transactional(propagation = Propagation.NESTED)
  18.  
    public void doOtherThing(){
  19.  
    System.out.println("保存role表数据");
  20.  
    }
  21.  
    }
学新通

这种情况使用了嵌套的内部事务,原本是希望roleService.doOtherThing方法时,如果出现了异常,只回滚doOtherThing方法里的内容,不回滚userMapper.insertUser里的内容,即回滚保存点,但事实是,insertUser也回滚了。

可以使用下面的方式进行事务的回滚。

  1.  
    public class UserService{
  2.  
    @AutoWired
  3.  
    private UserMapper userMapper;
  4.  
     
  5.  
    @AutoWired
  6.  
    private RoleService reloService;
  7.  
     
  8.  
    @Transactional
  9.  
    public void add(UserModel userModel) throws Exception{
  10.  
    userMapper.insertUser(userModel);
  11.  
    try{
  12.  
    roleService.doOtherThing();
  13.  
    }catch(Exception e){
  14.  
    log.error(e.getMessage(),e);
  15.  
    }
  16.  
    }
  17.  
    }
  18.  
     
  19.  
    @Service
  20.  
    public class RoleService{
  21.  
    @Transactional(propagation = Propagation.NESTED)
  22.  
    public void doOtherThing(){
  23.  
    System.out.println("保存role表数据");
  24.  
    }
  25.  
    }
学新通

学习之所以会想睡觉,是因为那是梦开始的地方。

ଘ(੭ˊᵕˋ)੭ (开心) ଘ(੭ˊᵕˋ)੭ (开心)ଘ(੭ˊᵕˋ)੭ (开心)ଘ(੭ˊᵕˋ)੭ (开心)ଘ(੭ˊᵕˋ)੭ (开心)

------不写代码不会凸的小刘

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhfhhffg
系列文章
更多 icon
同类精品
更多 icon
继续加载