一、实现原理
Spring事务的实现原理是动态代理,当调用添加@Transactional注解的方法时,代理对象会先开启事务,再调用方法,最后再提交/回滚事务。
二、常见的事务失效原因
1.修饰方法为非public
Spring事务的动态代理只会对public方法生效(jdk代理只对public方法生效,cglib代理虽然可以代理非public方法,但是在Spring中做了限制,只对public方法进行事务代理)
示例:
java
@Service
public class UserService {
// private修饰 → 事务失效
@Transactional
private void updateUserName(Long id, String name) {
userMapper.updateName(id, name);
}
}
2.内部方法直接调用
如果通过一个普通方法直接调用添加@Transactional的方法则会导致不走代理对象,那么也不会实现事务管理。
示例:
java
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
// 普通方法(无事务)
public void updateUserInfo(Long id, String name) {
// 内部调用事务方法,不走代理 → 事务失效
this.updateUserName(id, name);
}
// 事务方法
@Transactional
public void updateUserName(Long id, String name) {
userMapper.updateName(id, name);
int i = 1 / 0; // 异常,但事务不会回滚
}
}
3.异常类型不匹配
Spring事务只会对RuntimException和Error异常进行回滚,对检查型异常则不会触发事务回滚。
解决方法:
1.扩大异常捕获范围
java
//捕获所有类型异常回滚
@Transactional(rollbackFor = Exception.class)
//或指定具体异常
@Transactional(rollbackFor = SQLException.class)
2.将检查型异常包装成运行时异常
java
throw new RuntimeException(new SQLException("数据库异常"));
4.手动捕获异常且未手动回滚
手动捕获异常后,Spring无法感知到异常
java
@Transactional
public void updateUserName(Long id, String name) {
try {
userMapper.updateName(id, name);
int i = 1 / 0; // 抛出运行时异常
} catch (Exception e) {
// 捕获异常但未处理 → 事务提交,数据不会回滚
log.error("更新失败", e);
}
}
解决方法
1.在catch中触发手动回滚
java
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
2。显示的抛出运行时异常
java
throw new RuntimeException(e);