@Transactional注解失效的情况

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


1、访问权限的问题

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

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

2、方法用final修饰

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

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

3、方法内部调用

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

@Service
public class UserService{
    @Autowired
    private UserService userService;
    
    //@Transactional
    public void add(UserModel userModel){
            userMapper.insertUser(userModel);
            updateStatus(userModel);
    }
    
     @Transactional
     public void updateStatus(UserModel userModel){
         doSameThing();     
     }    
}

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

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

@Service
public class ServiceA{
    @AutoWired
    private ServiceA serviceA;
    
    public void save(User user){
        queryData1();
        queryData2();
        serviceA.daSave(user);    
    }
    @Transactional(rollbackFor=Exception.class)
    public void daSave(User user){
        addData1();
        uodateData2();    
    }
}

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了异常。比如

@slf4j
@Service
public class UserService{
    @Transactional
    public void add(UserModel userModel){
            try{
                saveData(userModel);
                updateData(userModel);            
            }catch(Exception e){
                log.error(e.getMessage,e);            
            }
    }
}

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

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

10、手动抛出了别的异常

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

@slf4j
@Service
public class UserService{
    @Transactional
    public void add(UserModel userModel) throws Exception{
            try{
                saveData(userModel);
                updateData(userModel);            
            }catch(Exception e){
                log.error(e.getMessage,e);
                throw new Exception(e);            
            }
    }
}

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

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

11、自定了回滚异常

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

@slf4j
@Service
public class UserService{
    @Transactional(rollbackFor = BusinessException.class)
    public void add(UserModel userModel) throws Exception{
         saveData(userModel);
         updateData(userModel);            
    }
}

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

12、嵌套事务回滚多了

public class UserService{
    @AutoWired
    private UserMapper userMapper;
    
    @AutoWired
    private RoleService reloService;
    
    @Transactional
    public void add(UserModel userModel) throws Exception{
        userMapper.insertUser(userModel);
        roleService.doOtherThing();    
    }
}

@Service
public class RoleService{
    @Transactional(propagation = Propagation.NESTED)
    public void doOtherThing(){
        System.out.println("保存role表数据");    
    }
}

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

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

public class UserService{
    @AutoWired
    private UserMapper userMapper;
    
    @AutoWired
    private RoleService reloService;
    
    @Transactional
    public void add(UserModel userModel) throws Exception{
        userMapper.insertUser(userModel);
        try{
            roleService.doOtherThing();
        }catch(Exception e){
            log.error(e.getMessage(),e);          
        }            
    }
}

@Service
public class RoleService{
    @Transactional(propagation = Propagation.NESTED)
    public void doOtherThing(){
        System.out.println("保存role表数据");    
    }
}

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

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

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

猜你喜欢

转载自blog.csdn.net/qq_40834643/article/details/128535431