Spring事务不能回滚的深层次原因

简介: Spring事务不能回滚的深层次原因

开头总述

Spring在同一个类中调用function,事务会失效。

Spring事务是基于AOP代理来实现的。而AOP是使用JDK动态代理来实现的。

第一次试验

    /**
     * 父类调用子类
     * 子类失败,不能影响父类
     * 
     * 预期效果:child回滚,parent插入成功
     * 第一次试验 真实效果:都插入成功,child方法因为try catch导致事务未起作用。
     */  
    @Override
    @Transactional
    public void insertParent(){
        try {
            insertChild();
        } catch (Exception e) {
            // TODO: handle exception
        }
        user.setName("parent_5");
        user.setAge((byte)1);
        userMapper.insertSelective(user);
    }
    @Override
    @Transactional(propagation=Propagation.REQUIRES_NEW)
    public void insertChild(){
        user.setName("child_5");
        user.setAge((byte)1);
        userMapper.insertSelective(user);
        int i=1/0;
    }

执行结果

原因分析

   child方法被try catch住了,同时事务未生效。

  在SpringIoC容器中返回的调用的对象是代理对象而不是真实的对象,只有被动态代理直接调用的才会产生事务。this调用并非代理对象。this.insertChild走的是真实对象(真实对象)。导致child上的事务无效,先执行insert语句,执行成功以后才抛出异常。异常被parent中的try catch捕获到。不影响parent方法的执行。

第二次试验

 

   /**
    * 预期效果:child回滚,parent插入成功
    * 第二试验 真实效果:child插入成功,parent插入回滚
    */
    @Override
    @Transactional
    public void insertParent(){        
        user.setName("parent_7");
        user.setAge((byte)1);
        userMapper.insertSelective(user);
        insertChild();        
    }
    @Override
    @Transactional(propagation=Propagation.REQUIRES_NEW)
    public void insertChild(){
        user.setName("child_7");
        user.setAge((byte)1);
        userMapper.insertSelective(user);
        int i=1/0;
    }

执行结果

   

原因分析

   去掉try catch,child方法抛出异常,导致回滚。同时异常向上抛,导致parent方法也回滚。

   child方法的事务失效,相当于child方法中的code挪到了parent方法中,也类似于在parent中抛出异常。

解决方案

   AOP需要先暴露出来。

<!-- 开启切面-->

<!-- 暴露AOP代理到ThreadLocal -->

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

获取AOP代理需要从AOP的上下文来获取。得到当前AopProxy,然后通过AOP代理来调用child方法。

 

   /**
    * 预期效果:child回滚,parent插入成功
    * 第三试验真实效果:child回滚,parent插入成功
    */   
    @Override
    @Transactional
    public void insertParent(){        
        try {
            /**
             * AopContext.currentProxy()就类似于JDK代理中的
             * Proxy.newInstance得到的Proxy对象
             * UserService proxy=(UserService) AopContext.currentProxy();
             * proxy.insertChild();
             */
            ((UserService)AopContext.currentProxy()).insertChild();
        } catch (Exception e) {
            // TODO: handle exception
        }        
        user.setName("parent_7");
        user.setAge((byte)1);
        userMapper.insertSelective(user);    
    }
    @Override
    @Transactional(propagation=Propagation.REQUIRES_NEW)
    public void insertChild(){
        user.setName("child_7");
        user.setAge((byte)1);
        userMapper.insertSelective(user);
        int i=1/0;
    }

执行结果

论:非PROPAGATION_NESTED的标注的嵌套事务不使用动态代理的方式来调用会导致子方法里的事务失效


相关文章
|
2月前
|
监控 Java 数据处理
【Spring云原生】Spring Batch:海量数据高并发任务处理!数据处理纵享新丝滑!事务管理机制+并行处理+实例应用讲解
【Spring云原生】Spring Batch:海量数据高并发任务处理!数据处理纵享新丝滑!事务管理机制+并行处理+实例应用讲解
|
2月前
|
Java 数据库 Spring
Spring事务失效的场景详解
Spring事务失效的场景详解
34 0
|
2月前
|
Java 数据库 Spring
Spring事务的传播机制(行为、特性)
Spring事务的传播机制(行为、特性)
36 0
|
3月前
|
Java 关系型数据库 数据库连接
Spring源码解析--深入Spring事务原理
本文将带领大家领略Spring事务的风采,Spring事务是我们在日常开发中经常会遇到的,也是各种大小面试中的高频题,希望通过本文,能让大家对Spring事务有个深入的了解,无论开发还是面试,都不会让Spring事务成为拦路虎。
35 1
|
1天前
|
监控 Java 测试技术
Spring Boot与事务钩子函数:概念与实战
【4月更文挑战第29天】在复杂的业务逻辑中,事务管理是确保数据一致性和完整性的关键。Spring Boot提供了强大的事务管理机制,其中事务钩子函数(Transaction Hooks)允许开发者在事务的不同阶段插入自定义逻辑。本篇博客将详细探讨事务钩子函数的概念及其在Spring Boot中的应用。
9 1
|
2月前
|
XML Java 数据库
【二十四】springboot整合spring事务详解以及实战
【二十四】springboot整合spring事务详解以及实战
111 0
|
2月前
|
Java 数据库 开发者
|
3月前
|
Java 数据库 数据安全/隐私保护
|
3月前
|
Java 关系型数据库 MySQL
深入分析Spring事务和底层原理
深入分析Spring事务和底层原理
39 1
|
3月前
|
Java 数据库 开发者
一文带你掌握Spring事务核心:TransactionDefinition详解!
TransactionDefinition是Spring框架中用于定义事务属性的核心接口。在Spring的事务管理中,这个接口扮演着至关重要的角色,它允许开发者定制事务的各种属性,如隔离级别、传播行为、超时时间以及是否只读。
一文带你掌握Spring事务核心:TransactionDefinition详解!