@Transactional注解失效的解決方案


一、前言

  開發中我們經常使用 @Transactional注解來啟用Spring事務管理,但是如果使用方法不當,會遇到注解不生效該事務回滾的地方卻沒有回滾的問題。
總結下一般是以下幾個原因:

  1. @Transactional 注解只能應用到 public 可見度的方法上。 如果應用在protected、private或者 package可見度的方法上,也不會報錯,不過事務設置不會起作用。
  2. 默認情況下,spring會對unchecked異常進行事務回滾;如果是checked異常則不回滾。針對這種情況,可以try catch checked異常后進行手動事務回滾。
  3. 數據庫引擎要支持事務,如果是mysql,注意表要使用支持事務的引擎,比如InnoDB,如果是MyISAM,事務是不起作用的。
  4. 同一個類中, 一個no-transactional的方法去調用transactional的方法, 事務會失效。

本文主要講第四種情況如何處理。

二、示例

 

事務失效示例1
事務失效示例1

 

 

事務失效示例2
事務失效示例2

 

執行add() 方法后數據庫插入了兩條數據,也就說明以上兩段代碼中,doAdd()方法的事務增強都不會執行,具體原因可以參考這篇文章

那么如果想讓doAdd()方法在發生異常時數據庫事務回滾,有什么解決辦法嗎?

第一步開啟配置expose-proxy

如果使用的xml配置文件的方式,則添加如下內容:

<aop:aspectj-autoproxy expose-proxy="true" />

如果用的SpringBoot框架,則直接在啟動類上添加如下注解即可。

@EnableAspectJAutoProxy(proxyTargetClass = true,exposeProxy = true)

第二步修改代碼
將原來的調用代碼this.doAdd(person); 改為 ((PersonService)AopContext.currentProxy()).doAdd(person);

 

enter description here
enter description here

 

再進行測試發現數據庫只插入了一條數據(person2),說明doAdd() 方法里的事務增強生效了。

三、源碼分析

點進 @EnableAspectJAutoProxy 注解代碼如下:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {

    boolean proxyTargetClass() default false;

    boolean exposeProxy() default false;

}

發現它注入了一個Bean AspectJAutoProxyRegistrar,再看看這個AspectJAutoProxyRegistrar是什么。

class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {

    public void registerBeanDefinitions( AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

        AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
        // 解析注解EnableAspectJAutoProxy
        AnnotationAttributes enableAspectJAutoProxy =
                AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
        if (enableAspectJAutoProxy != null) {
            if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
                AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
            }
            if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
                // 如果設置了exposeProxy=true就強制使用
                AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
            }
        }
    }

}

AopConfigUtils#forceAutoProxyCreatorToExposeProxy

// 強制使用的過程其實也是一個屬性設置的過程
    public static void forceAutoProxyCreatorToExposeProxy(BeanDefinitionRegistry registry) {
        if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
            BeanDefinition definition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
            definition.getPropertyValues().add("exposeProxy", Boolean.TRUE);
        }
    }
    
    /** * The bean name of the internally managed auto-proxy creator. */
    public static final String AUTO_PROXY_CREATOR_BEAN_NAME =
            "org.springframework.aop.config.internalAutoProxyCreator";


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM