@Transactional的10种失效情况

本文详细列举了在使用Spring事务管理时可能出现的问题,包括错误的访问权限、final方法、内部方法调用、实体未被管理、错误的事务传播特性、数据库不支持事务、异常处理不当、多线程调用以及嵌套事务的回滚规则。理解这些问题有助于避免事务失效,确保数据一致性。
摘要由CSDN通过智能技术生成

1.错误的访问权限

@Service
public class UserService {

    @Autowired
    private UserMapper userMapper;
    
    @Transactional
    private void add(UserModel userModel) {
        userMapper.insertUser(userModel);
    }
}

因为@Transactional生效要走AOP代理,而切面是切不到私有方法的


2.方法被定义成final的

@Servicepublic class UserService {
    @Autowired    
    private UserMapper userMapper;
    
    @Transactional    
    public final void add(UserModel userModel) {        
        userMapper.insertUser(userModel);   
    }
 }

可以看到add方法被定义成了final的,这样会导致spring aop生成的代理对象不能复写该方法,而让事务失效。

3.方法内部调用

没走代理

4.当前实体没有被spring管理

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

可以看到UserService类没有定义@Service注解,即没有交给spring管理bean实例,所以它的add方法也不会生成事务。

5.错误的spring事务传播特性

@Servicepublic class UserService {
    @Autowired    
    private UserMapper userMapper;
    
    @Transactional(propagation = Propagation.NEVER)    
    public void add(UserModel userModel) {        
    userMapper.insertUser(userModel);    
    }
}

可以看到add方法的事务传播特性定义成了Propagation.NEVER,这种类型的传播特性不支持事务,如果有事务则会抛异常。只有这三种传播特性才会创建新事务:PROPAGATION_REQUIRED,PROPAGATION_REQUIRES_NEW,PROPAGATION_NESTED。

6.数据库不支持事务


7.自己吞掉了异常

@Slf4j
@Service
public class UserService {
    @Autowired    
    private UserMapper userMapper;    
        
    @Transactional    
    public void add(UserModel userModel) throws Exception {        
        try {            
            userMapper.insertUser(userModel);        
        } catch (Exception e) {            
            log.error(e.getMessage(), e);               
        }    
    }
}

8.抛出的异常不正确

@Slf4j
@Service
public class UserService {
    @Autowired    
    private UserMapper userMapper;    
        
    @Transactional    
    public void add(UserModel userModel) throws Exception {        
        try {            
            userMapper.insertUser(userModel);        
        } catch (Exception e) {            
            log.error(e.getMessage(), e);            
            throw new Exception(e);        
        }    
    }
}

spring事务,默认情况下只会回滚RuntimeException(运行时异常)和Error(错误),不会回滚Exception。

9.多线程调用

@Slf4j
@Service
public class UserService {

    @Autowired
    private UserMapper userMapper;
    @Autowired
    private RoleService roleService;

    @Transactional
    public void add(UserModel userModel) throws Exception {
        userMapper.insertUser(userModel);
        new Thread(() -> {
            roleService.doOtherThing();
        }).start();
    }
}

@Service
public class RoleService {

    @Transactional
    public void doOtherThing() {
        System.out.println("保存role表数据");
    }
}

可以看到事务方法add中,调用了事务方法doOtherThing,但是事务方法doOtherThing是在另外一个线程中调用的,这样会导致两个事务方法不在同一个线程中,获取到的数据库连接不一样,从而是两个不同的事务。如果想doOtherThing方法中抛了异常,add方法也回滚是不可能的。

如果看过spring事务源码的朋友,可能会知道spring的事务是通过数据库连接来实现的。当前线程中保存了一个map,key是数据源,value是数据库连接。
private static final ThreadLocal<Map<Object, Object>> resources = new NamedThreadLocal<>(“Transactional resources”);

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

10.嵌套事务多回滚了

ServiceA.methodA的事务级别为PROPAGATION_REQUIRED,ServiceB.methodB的事务级别为PROPAGATION_NESTED,
调用ServiceB.methodB的时候,ServiceA.methodA所在的事务就会挂起,ServiceB.methodB会起一个新的子事务并设置savepoint
1、如果ServiceB.methodB已经提交,那么ServiceA.methodA失败回滚,ServiceB.methodB也将回滚。
2、如果ServiceB.methodB失败回滚,如果他抛出的异常被ServiceA.methodA的try…catch捕获并处理,ServiceA.methodA事务仍然可能提交;如果他抛出的异常未被ServiceA.methodA捕获处理,ServiceA.methodA事务将回滚。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值