@Transactional 注解失效情況及解決辦法

一、@Transactional 注解在了非 public 方法上

  1. 如下所示@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();
    }
}

  1. 失效原因

    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;
    }
    
    
  2. 解決方法

  • 方法一:把需要支持事務(wù)的方法統(tǒng)一定義為 public 修飾的方法
  • 方法二:創(chuàng)建一個(gè)全為public修飾的方法的門面類,在此類中添加@Transactional

二、在類內(nèi)部調(diào)用添加 @Transactional 的方法

  1. 實(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);
   }
}

  1. 失效原因

此種情況跟情況一的原因類似,在Spring啟動(dòng)時(shí)會(huì)掃描所有添加了@Transactional注解的類,創(chuàng)建的切面,當(dāng)在同一個(gè)類中調(diào)用是,切面失去了作用。

  1. 解決方法

既然事務(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);
    }
}

三、捕獲異常未拋出

  1. 實(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();
        }
    }
}

  1. 失效原因

事務(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 {  
     //....................  
   }  
} 

  1. 解決方法
  • 方法一:在方法中不對(duì)異常進(jìn)行捕獲
  • 方法二:若必須捕獲,在捕獲處理后在重新拋出異常。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容