作者:微信小助手
发布时间:2021-10-21T12:05:51
对于从事java开发工作的同学来说,spring的事务肯定再熟悉不过了。 在某些业务场景下,如果一个请求中,需要同时写入多张表的数据。为了保证操作的原子性(要么同时成功,要么同时失败),避免数据不一致的情况,我们一般都会用到spring事务。 确实,spring事务用起来贼爽,就用一个简单的注解: 但如果你使用不当,它也会坑你于无形。 今天我们就一起聊聊,事务失效的一些场景,说不定你已经中招了。不信,让我们一起看看。 众所周知,java的访问权限主要有四种:private、default、protected、public,它们的权限从左到右,依次变大。 但如果我们在开发过程中,把有某些事务方法,定义了错误的访问权限,就会导致事务功能出问题,例如: 我们可以看到add方法的访问权限被定义成了 说白了,在 也就是说,如果我们自定义的事务方法(即目标方法),它的访问权限不是 有时候,某个方法不想被子类重新,这时可以将该方法定义成final的。普通方法这样定义是没问题的,但如果将事务方法定义成final,例如: 我们可以看到add方法被定义成了 为什么? 如果你看过spring事务的源码,可能会知道spring事务底层使用了aop,也就是通过jdk动态代理或者cglib,帮我们生成了代理类,在代理类中实现的事务功能。 但如果某个方法用final修饰了,那么在它的代理类中,就无法重写该方法,而添加事务功能。 注意:如果某个方法是static的,同样无法通过动态代理,变成事务方法。 有时候我们需要在某个Service类的某个方法中,调用另外一个事务方法,比如: 我们看到在事务方法add中,直接调用事务方法updateStatus。从前面介绍的内容可以知道,updateStatus方法拥有事务的能力是因为spring aop生成代理了对象,但是这种方法直接调用了this对象的方法,所以updateStatus方法不会生成事务。 由此可见,在同一个类中的方法直接内部调用,会导致事务失效。 那么问题来了,如果有些场景,确实想在同一个类的某个方法中,调用它自己的另外一个方法,该怎么办呢? 这个方法非常简单,只需要新加一个Service方法,把前言
@Transactional
,就能轻松搞定事务。我猜大部分小伙伴也是这样用的,而且一直用一直爽。
一 事务不生效
1.访问权限问题
@Service
public class UserService {
@Transactional
private void add(UserModel userModel) {
saveData(userModel);
updateData(userModel);
}
}private
,这样会导致事务失效,spring要求被代理方法必须是 public
的。AbstractFallbackTransactionAttributeSource
类的 computeTransactionAttribute
方法中有个判断,如果目标方法不是public,则 TransactionAttribute
返回null,即不支持事务。protected TransactionAttribute computeTransactionAttribute(Method method, @Nullable Class<?> targetClass) {
// Don't allow no-public methods as required.
if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
return null;
}
// The method may be on an interface, but we need attributes from the target class.
// If the target class is null, the method will be unchanged.
Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass);
// First try is the method in the target class.
TransactionAttribute txAttr = findTransactionAttribute(specificMethod);
if (txAttr != null) {
return txAttr;
}
// Second try is the transaction attribute on the target class.
txAttr = findTransactionAttribute(specificMethod.getDeclaringClass());
if (txAttr != null && ClassUtils.isUserLevelMethod(method)) {
return txAttr;
}
if (specificMethod != method) {
// Fallback is to look at the original method.
txAttr = findTransactionAttribute(method);
if (txAttr != null) {
return txAttr;
}
// Last fallback is the class of the original method.
txAttr = findTransactionAttribute(method.getDeclaringClass());
if (txAttr != null && ClassUtils.isUserLevelMethod(method)) {
return txAttr;
}
}
return null;
}public
,而是private、default或protected的话,spring则不会提供事务功能。2. 方法用final修饰
@Service
public class UserService {
@Transactional
public final void add(UserModel userModel){
saveData(userModel);
updateData(userModel);
}
}final
的,这样会导致事务失效。
“
3.方法内部调用
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
public void add(UserModel userModel) {
userMapper.insertUser(userModel);
updateStatus(userModel);
}
@Transactional
public void updateStatus(UserModel userModel) {
doSameThing();
}
}3.1 新加一个Service方法
@Transactional
注解加到新Service方法上,把需要事务执行的代码移到新方法中。具体代码如下:@Servcie
public class ServiceA {
@Autowired
prvate ServiceB serviceB;
public void save(User user) {
queryData1();
queryData2();
serviceB.doSave(user);
}
}
@Servcie
public class ServiceB {