Spring事务(四)-事务失效场景

Spring事务(四)-事务失效场景

有时候,我们明明在类或者方法上添加了 @Transactional注解,却发现方法并没有按事务处理。其实,以下场景会导致事务失效。

1、事务方法所在的类没有加载到Spring IOC容器中。

Spring声明式事务的实现完全依赖于Spring的AOP代理机制,未被Spring管理的类中的方法不受Spring的AOP代理管理,因此,声明式事务失效。

2、方法没有被public修饰。

众所周知,java的访问权限修饰符有:private、default、protected、public四种,但是 @Transactional注解只能作用于public修饰的方法上。之所以会失效是因为在Spring AOP 代理时,TransactionInterceptor (事务拦截器)在目标方法执行前后进行拦截,DynamicAdvisedInterceptor(CglibAopProxy 的内部类)的 intercept 方法或 JdkDynamicAopProxy 的 invoke 方法会间接调用 AbstractFallbackTransactionAttributeSource的 computeTransactionAttribute 方法,获取Transactional 注解的事务配置信息。

protected TransactionAttribute computeTransactionAttribute(Method method,
    Class targetClass) {
        // Don't allow no-public methods as required.

        if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
        return null;
}

此方法会检查目标方法的修饰符是否为 public,不是 public则不会获取@Transactional 的属性配置信息。

注意:protected、private 修饰的方法上使用 @Transactional 注解,虽然事务无效,但不会有任何报错。

3、在同一个类中的方法调用。

假如在同一个类中有A、B两个方法,如下:

@Service
public class UserServiceImpl {

    @Autowired
    UserMapper userMapper;

    public void A() {
        B();
    }

    @Transactional
    public void B() {
        userMapper.deleteById(1);
        int i = 10 / 0; //模拟发生异常
    }

}

像上面的代码,B方法使用 @Transactional注解标注,在A方法中调用了B方法,在外部调用A方法时,B方法的事务不会生效。这是由于使用Spring AOP代理造成的,因为只有当事务方法被当前类以外的代码调用时,才会由Spring生成的代理对象来管理。

那么如果确实在同一类中调用事务方法怎么办呢?有以下3种方法解决:

  • *引入自身bean
@Service
public class UserServiceImpl {

    @Autowired
    UserMapper userMapper;

    @Autowired
    UserServiceImpl userServiceImpl;

    public void A() {
        userServiceImpl.B();
    }

    @Transactional
    public void B() {
        userMapper.deleteById(1);
        int i = 10 / 0; //模拟发生异常
    }

}
  • *通过ApplicationContext引入bean
@Service
public class UserServiceImpl {

    @Autowired
    UserMapper userMapper;

    @Autowired
    ApplicationContext applicationContext;

    public void A() {
        ((UserServiceImpl) applicationContext.getBean("userServiceImpl")).B();
    }

    @Transactional
    public void B() {
        userMapper.deleteById(1);
        int i = 10 / 0; //模拟发生异常
    }

}
  • *通过AopContext获取当前代理类

在启动类上添加注解 @EnableAspectJAutoProxy(exposeProxy = true),表示是否对外暴露代理对象,即是否可以获取AopContext。然后,在业务类上使用AopContext。

@Service
public class UserServiceImpl {

    @Autowired
    UserMapper userMapper;

    public void A() {
        ((UserServiceImpl) AopContext.currentProxy()).B();
    }

    @Transactional
    public void B() {
        userMapper.deleteById(1);
        int i = 10 / 0; //模拟发生异常
    }

}

4、方法的事务传播类型不支持事务。

若propagation属性设置如下三种事务传播行为,事务将不会发生回滚。

  • SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
  • NOT_SUPPORTED:以非事务方式运行,如果当前存在事务,则把当前事务挂起。
  • NEVER:以非事务方式运行,如果当前存在事务,则抛出异常。

5、不正确地捕获异常。

使用了try-catch代码块将异常捕捉了,没有向上抛出异常,事务不会回滚。

@Transactional
private void A() throws Exception {
    @Autowired
    UserMapper userMapper;

    @Autowired
    B b;

    try {
        //A方法插入数据
        User u = New User();
        u.setId(1);
        u.setName("张三");
        userMapper.insert(u);

        /**
         * 这里调用外部方法插入数据
         */
        b.insert();
    } catch (Exception e) {
        e.printStackTrace();
    }
}

上面的代码,如果B类insert方法内部抛了异常,而A方法此时try catch了B类方法的异常,那么,事务并不会回滚,而且会抛出如下异常:

org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only

因为当B类insert方法中抛出了一个异常以后,标识当前事务需要rollback。但是A方法中由于手动的捕获这个异常并进行处理,A方法认为当前事务应该正常commit。此时就出现了前后不一致,也就是因为这样,抛出了前面的UnexpectedRollbackException异常。

6、属性rollbackFor设置错误。

rollbackFor可以指定能够触发事务回滚的异常类型。Spring默认抛出了未检查unchecked异常(继承自 RuntimeException 的异常)或者 Error才回滚事务。其他异常不会触发回滚事务。如果在事务中抛出其他类型的异常,但却期望 Spring 能够回滚事务,就需要设置 rollbackFor属性。

7、数据库不支持事务。

事务本来就是数据库的功能,如果数据库本身不支持事务,那任凭代码上如何设置也是没用的。以MySQL为例,InnoDB引擎是支持事务的,而像MyISAM、MEMORY等是不支持事务的。从MySQL5.5.5开始默认的存储引擎是InnoDB,之前默认都是MyISAM。

8、方法使用final修饰。

如果一个方法不想被子类重写,那么我们就可以把他写成final修饰的方法。如果事务方法使用final修饰,那么Spring AOP就无法在代理类中重写该方法,事务就不会生效。同样的,static修饰的方法也无法通过代理变成事务方法。

9、未开启事务。

如果是SpringBoot项目,那么SpringBoot通过DataSourceTransactionManagerAutoConfiguration自动配置类帮我们开启了事务。如果是传统的Spring项目,则需要我们自己配置。在Spring配置文件配置如下:


或者,使用注解的方式。


10、多线程调用

@Service
public class UserServiceImpl {

    @Autowired
    UserMapper userMapper;

    @Transactional
    public void A() {
        userMapper.deleteById(1);
        new Thread(()->{
            userMapper.deleteById(2);
            int i = 10/0; //模拟发生异常
            }).start();
    }

}

以上代码,A方法中,启动了一个新的线程,并在新的线程中发生了异常,这样A方法是不会发生回滚的。因为两个操作不在一个线程中,获取到的数据库连接不一样,从而是两个不同的事务,所以也不会回滚。

Original: https://www.cnblogs.com/ayic/p/16698508.html
Author: Yi00
Title: Spring事务(四)-事务失效场景

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/684962/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • os里边的函数用法(持续更新)

    os.environ 对于官方的解释,environ是一个字符串所对应环境的映像对象我们想要用python获得一些有关系统的各种信息的时候就不得不想到os的environ,那这里面…

    技术杂谈 2023年7月11日
    080
  • 设计一个回调要注意哪些事情

    设计一个回调要注意哪些事情 回调是我们在设计系统的时候经常会使用到的, A服务调用B服务, 但是如果B服务提供的是一个较长时间的、异步的接口, 那么我们就会想到使用一个回调, 让B…

    技术杂谈 2023年6月1日
    0100
  • 如何使用MBP制作Win启动盘

    最近有一个需求,想给家人的一台笔记本安装一套win 10的操作系统,但是我手头上现在没有对应的启动U盘。由于工作原因,很多年没用win了,工作电脑也都是MBP,根本没有之前使用wi…

    技术杂谈 2023年5月31日
    078
  • 使用java调用 salesforce SOAP API

    可以从github上下载最新版本然后用maven工具构建 下面列下我用wsc工具打成jar包测试过程中的代码: java -cp force-wsc-27.0.0-jar-with…

    技术杂谈 2023年7月25日
    084
  • raster 像素化

    Background & Problem Statement Our current compositor thread architecture is built aro…

    技术杂谈 2023年5月31日
    095
  • Reactor模型

    要无障碍阅读本文,需要对NIO有一个大概的了解,起码要可以写一个NIO的Hello World。 说到NIO、Netty,Reactor模型一定是绕不开的,因为这种模式架构太经典了…

    技术杂谈 2023年7月25日
    057
  • 三分钟图解事务隔离级别,看一遍就懂

    前文说过,”锁” 是数据库系统区别于文件系统的一个关键特性,其对象是 事务,用来锁定的是数据库中的对象,如表、页、行等。锁确实提高了并发性,但是却不可避免地…

    技术杂谈 2023年7月25日
    087
  • MVC与MVVM?

    model-数据层 view-视图层 controller-控制层 MVC的目的是实现M和V的分离,单向通信,必须通过C来承上启下 MVVM中通过VM(vue中的实例化对象)的发布…

    技术杂谈 2023年5月31日
    080
  • 卡卡游戏引擎之快速入门

    前言 卡卡游戏引擎是一个跨平台的2d游戏引擎,并配有跨平台的卡卡编辑器作为游戏开发辅助工具。卡卡游戏引擎采用mvc开发模式,即模型(Model)-视图(View)-控制器(Cont…

    技术杂谈 2023年5月31日
    074
  • airflow sample to pass metadata to task. —–XCOM

    At first, let us take a look at one sample which one we do not need to pass the metadata h…

    技术杂谈 2023年5月31日
    086
  • 初识Python系列(二)

    对于Python selenium操作的总结(二) –小破站每日登录投币30经验实战练习 一、操作步骤 环境:Python 3.8,selenium库 在上一章,讲述了…

    技术杂谈 2023年7月23日
    081
  • 从理论到实践,刨根问底探索Java对象内存布局

    从理论到实践,刨根问底探索Java对象内存布局 所谓对象的内存布局,就是对象在分配到内存中后的存储格式。 对象在内存中的布局一共包含三部分: 对象头(Header) 实例数据(In…

    技术杂谈 2023年7月25日
    087
  • 如果对象的引用被置为null,;垃圾回收器是否会立即释放对象占用的内存?

    不会,在下一个垃圾回调周期中,这个对象将是被可回收的。 也就是说并不会立即被垃圾收集器立刻回收,而是在下一次垃圾回收时才会释放其占用的内存。 Original: https://w…

    技术杂谈 2023年5月31日
    091
  • 复制状态机系统架构抽象

    化繁为简,聊一聊复制状态机系统架构抽象 https://mp.weixin.qq.com/s/ZjMlarihKdPIkuz2sMuAGw 这是阿里技术2022年的第27篇文章 (…

    技术杂谈 2023年5月31日
    059
  • SpringBoot 项目部署(初级)

    之前的项目一直在本地电脑上写,最近需要将项目部署到服务器上进行联调测速度。于是,在网上搜集资料后简单的进行一下总结。 _由于本次打包部署是为了测试,于是很多内容做的还不算详尽 ,_…

    技术杂谈 2023年6月21日
    084
  • Intel网卡的漫游主动性

    posted @2019-09-22 22:22 樊伟胜 阅读(2129 ) 评论() 编辑 Original: https://www.cnblogs.com/fanweishe…

    技术杂谈 2023年5月30日
    096
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球