zoukankan      html  css  js  c++  java
  • @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";
    
  • 相关阅读:
    JS变量的作用域
    使用jquery修改css中带有!important的样式属性
    js异步加载的三种解决方案
    js 停止事件冒泡 阻止浏览器的默认行为(阻止超连接 # )
    【翻译】MongoDB指南/CRUD操作(二)
    【翻译】MongoDB指南/CRUD操作(一)
    【翻译】MongoDB指南/引言
    深入浅出UML类图(一)
    让thinkphp 5 支持pathinfo 的 nginx ,去掉index.php
    linux 常用命令
  • 原文地址:https://www.cnblogs.com/2YSP/p/11748389.html
Copyright © 2011-2022 走看看