@Transactional 注解失效情况及解决办法
2022-10-13 本文已影响0人
瞎胡扯1
一、@Transactional 注解在了非 public 方法上
- 如下所示
@Transactional
修饰在了非public
方法上
@Service
public class TestServiceImpl {
@Resource
private Test1Mapper test1Mapper;
@Transactional
protected Long save(Long seq){
Long aLong = Optional.ofNullable(seq).orElse(System.currentTimeMillis());
Test1Entity entity = new Test1Entity().setSeq(seq);
test1Mapper.insert(entity);
long l = entity.getId() / 0;
return entity.getId();
}
}
-
失效原因
Spring
中是通过动态代理来实现注解了@Transactional
的方法的事务处理操作,而在处理@Transactional
的注解时是只对public
方法才有效。
Spring
在扫描切点时,是根据注解进行判断是否创建切点。AopUtils#canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions)
public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) { // ..... for (Class<?> clazz : classes) { Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz); for (Method method : methods) { if (introductionAwareMethodMatcher != null ? introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions) : // 此处判断是否匹配生成切面 methodMatcher.matches(method, targetClass)) { return true; } } } return false; }
// TransactionAttributeSourcePointcut# matches(Method method, Class<?> targetClass) public boolean matches(Method method, Class<?> targetClass) { TransactionAttributeSource tas = getTransactionAttributeSource(); // 获取 注解属性 return (tas == null || tas.getTransactionAttribute(method, targetClass) != null); }
// AnnotationTransactionAttributeSource#getTransactionAttribute // AnnotationTransactionAttributeSource#computeTransactionAttribute @Nullable protected TransactionAttribute computeTransactionAttribute(Method method, @Nullable Class<?> targetClass) { // Don't allow no-public methods as required. // 判断是否只允许 public 方法 if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) { return null; } // 其他代码 ... return null; }
-
解决方法
- 方法一:把需要支持事务的方法统一定义为
public
修饰的方法 - 方法二:创建一个全为
public
修饰的方法的门面类,在此类中添加@Transactional
二、在类内部调用添加 @Transactional 的方法
- 实例
@Service
public class TestServiceImpl {
@Resource
private Test1Mapper test1Mapper;
@Transactional
public void save(Long seq){
Long aLong = Optional.ofNullable(seq).orElse(System.currentTimeMillis());
test1Mapper.insert(new Test1Entity(null, seq));
long l = aLong / 0;
}
// 调用内部方法
public void innerInvoke(){
this.save(100L);
}
}
- 失效原因
此种情况跟情况一的原因类似,在Spring
启动时会扫描所有添加了@Transactional
注解的类,创建的切面,当在同一个类中调用是,切面失去了作用。
- 解决方法
既然事务管理是基于动态代理对象的代理逻辑实现的,那么如果在类内部调用类内部的事务方法,这个调用事务方法的过程并不是通过代理对象来调用的,而是直接通过this对象来调用方法,绕过的代理对象,肯定就是没有代理逻辑了。
如下所示:
@Service
public class TestServiceImpl {
@Resource
private Test1Mapper test1Mapper;
// 第一个一个自己作为属性
@Resource
@Lazy
private TestServiceImpl test1Service;
@Transactional
public void save(Long seq){
Long aLong = Optional.ofNullable(seq).orElse(System.currentTimeMillis());
test1Mapper.insert(new Test1Entity(null, seq));
long l = aLong / 0;
}
// 调用内部方法
public void innerInvoke(){
//内部调用事务方法
test1Service.save(100L);
}
}
三、捕获异常未抛出
- 实例
@Service
public class TestServiceImpl {
@Resource
private Test1Mapper test1Mapper;
@Transactional
public void save(Long seq){
try {
Long aLong = Optional.ofNullable(seq).orElse(System.currentTimeMillis());
test1Mapper.insert(new Test1Entity(null, seq));
long l = aLong / 0;
} catch (Exception e) {
e.printStackTrace();
}
}
}
- 失效原因
事务方法内部捕捉了异常,没有抛出新的异常,导致事务操作不会进行回滚。
这种的话,可能我们比较常见,问题就出在代理逻辑中,我们先看看源码里卖弄动态代理逻辑是如何为我们管理事务的。
TransactionAspectSupport#invokeWithinTransaction
protected Object invokeWithinTransaction(Method method, Class<?> targetClass, final InvocationCallback invocation)
throws Throwable {
// If the transaction attribute is null, the method is non-transactional.
final TransactionAttribute txAttr = getTransactionAttributeSource().getTransactionAttribute(method, targetClass);
final PlatformTransactionManager tm = determineTransactionManager(txAttr);
final String joinpointIdentification = methodIdentification(method, targetClass);
if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
// Standard transaction demarcation with getTransaction and commit/rollback calls.
//开启事务
TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
Object retVal = null;
try {
// This is an around advice: Invoke the next interceptor in the chain.
// This will normally result in a target object being invoked.
//反射调用业务方法
retVal = invocation.proceedWithInvocation();
}
catch (Throwable ex) {
// target invocation exception
//异常时,在catch逻辑中回滚事务
completeTransactionAfterThrowing(txInfo, ex);
throw ex;
}
finally {
cleanupTransactionInfo(txInfo);
}
//提交事务
commitTransactionAfterReturning(txInfo);
return retVal;
}
else {
//....................
}
}
- 解决方法
- 方法一:在方法中不对异常进行捕获
- 方法二:若必须捕获,在捕获处理后在重新抛出异常。