一、@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中是通過動(dòng)態(tài)代理來(lái)實(shí)現(xiàn)注解了@Transactional的方法的事務(wù)處理操作,而在處理@Transactional的注解時(shí)是只對(duì)public方法才有效。
Spring在掃描切點(diǎn)時(shí),是根據(jù)注解進(jìn)行判斷是否創(chuàng)建切點(diǎn)。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; } 解決方法
- 方法一:把需要支持事務(wù)的方法統(tǒng)一定義為
public修飾的方法 - 方法二:創(chuàng)建一個(gè)全為
public修飾的方法的門面類,在此類中添加@Transactional
二、在類內(nèi)部調(diào)用添加 @Transactional 的方法
- 實(shí)例
@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;
}
// 調(diào)用內(nèi)部方法
public void innerInvoke(){
this.save(100L);
}
}
- 失效原因
此種情況跟情況一的原因類似,在Spring啟動(dòng)時(shí)會(huì)掃描所有添加了@Transactional注解的類,創(chuàng)建的切面,當(dāng)在同一個(gè)類中調(diào)用是,切面失去了作用。
- 解決方法
既然事務(wù)管理是基于動(dòng)態(tài)代理對(duì)象的代理邏輯實(shí)現(xiàn)的,那么如果在類內(nèi)部調(diào)用類內(nèi)部的事務(wù)方法,這個(gè)調(diào)用事務(wù)方法的過程并不是通過代理對(duì)象來(lái)調(diào)用的,而是直接通過this對(duì)象來(lái)調(diào)用方法,繞過的代理對(duì)象,肯定就是沒有代理邏輯了。
如下所示:
@Service
public class TestServiceImpl {
@Resource
private Test1Mapper test1Mapper;
// 第一個(gè)一個(gè)自己作為屬性
@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;
}
// 調(diào)用內(nèi)部方法
public void innerInvoke(){
//內(nèi)部調(diào)用事務(wù)方法
test1Service.save(100L);
}
}
三、捕獲異常未拋出
- 實(shí)例
@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();
}
}
}
- 失效原因
事務(wù)方法內(nèi)部捕捉了異常,沒有拋出新的異常,導(dǎo)致事務(wù)操作不會(huì)進(jìn)行回滾。
這種的話,可能我們比較常見,問題就出在代理邏輯中,我們先看看源碼里賣弄?jiǎng)討B(tài)代理邏輯是如何為我們管理事務(wù)的。
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.
//開啟事務(wù)
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.
//反射調(diào)用業(yè)務(wù)方法
retVal = invocation.proceedWithInvocation();
}
catch (Throwable ex) {
// target invocation exception
//異常時(shí),在catch邏輯中回滾事務(wù)
completeTransactionAfterThrowing(txInfo, ex);
throw ex;
}
finally {
cleanupTransactionInfo(txInfo);
}
//提交事務(wù)
commitTransactionAfterReturning(txInfo);
return retVal;
}
else {
//....................
}
}
- 解決方法
- 方法一:在方法中不對(duì)異常進(jìn)行捕獲
- 方法二:若必須捕獲,在捕獲處理后在重新拋出異常。