【小家java】Spring事务嵌套引发的血案---Transaction rolled back because it has been marked as rollback-only(上)

简介: 【小家java】Spring事务嵌套引发的血案---Transaction rolled back because it has been marked as rollback-only(上)

相关阅读

Netflix OSS套件一站式学习驿站(Eureka、Hystrix、Ribbon、Feign、Zuul…)

【小家java】java8新特性(简述十大新特性) 饱受赞誉

【小家java】java11新特性(简述八大新特性) 首个重磅LTS版本

【小家Spring】Spring IOC容器启动流程 AbstractApplicationContext#refresh()方法源码分析(一)

【小家Spring】Spring IOC容器启动流程 AbstractApplicationContext#refresh()方法源码分析(二)

【小家Spring】源码分析Spring的事务拦截器:TransactionInterceptor和事务管理器:PlatformTransactionManager


每篇一句

技多不压身。知识只有质量,没有重量,压不死人的。

1、概述


想必大家一想到事务,就想到ACID,或者也会想到CAP。但笔者今天不讨论这个,哈哈~本文将从应用层面稍带一点源码,来解释一下我们平时使用事务遇到的一个问题但让很多人又很棘手的问题:Transaction rolled back because it has been marked as rollback-only,中文翻译为:事务已回滚,因为它被标记成了只回滚。囧,中文翻译出来反倒更不好理解了,本文就针对此种事务异常做一个具体分析:


看此篇博文之前,建议先阅读:【小家java】Spring事务不生效的原因大解读


2、栗子


我们如果使用了spring来管理我们的事务,将会使事务的管理变得异常的简单,比如如下方法就有事务:

@Transactional
@Override
public boolean create(User user) {
    int i = userMapper.insert(user);
    System.out.println(1 / 0); //此处抛出异常,事务回滚,因此insert不会生效
    return i == 1;
}

这应该是我们平时使用的一个缩影。但本文不对事务的基础使用做讨论,只讨论异常情况。但本文可以给读者导航到我的另外一篇博文,介绍了事务不生效的N种可能性:【小家java】spring事务不生效的原因大解读


看下面这个例子,将是我们今天讲述的主题:


@Transactional
@Override
public boolean create(User user) {
    int i = userMapper.insert(user);
    personService.addPerson(user);
    return i == 1;
}

//下面是personService的addPerson方法,也是有事务的

@Transactional
@Override
 public boolean addPerson(User user) {
     System.out.println(1 / 0);
     return false;
 }


这种写法是我们最为普通的写法,显然是可以回滚的。但是如果上面这么写:

@Transactional
 @Override
  public boolean create(User user) {
      int i = userMapper.insert(user);
      try {
          personService.addPerson(user);
      } catch (Exception e) {
          System.out.println("不断程序,用来输出日志~");
      }
      return i == 1;
  }

这里我们把别的service方法try住,不希望它阻断我们的程序继续执行。表面上看合乎情理没毛病,but:



这里需要注意:如果我是这么写:

@Transactional
    @Override
    public boolean addPerson(User user) {
        userMapper.updateByIdSelective(user);
        try {
            editById(user);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }
    @Transactional
    @Override
    public boolean editById(User user) {
        System.out.println(1 / 0);
        return false;
    }

也是不会产生上面所述的那个rollback-only异常的:

@Transactional
    @Override
    public boolean addPerson(User user) {
        try {
            editById(user);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }
    @Transactional
    @Override
    public boolean editById(User user) {
        userMapper.updateByIdSelective(user);
        System.out.println(1 / 0);
        return false;
    }

但是,我们的updateByIdSelective持久化是生效了的。分析如下:


1.为什么update持久化生效?


因为addPerson有事务,所以editById理论上也有事务应该回滚才对,但是由于上层方法给catch住了,所以是没有回滚的,所以持久化生效。


2.为何没发生roolback-only的异常呢?


原因是因为editById的事务是沿用的addPerson的事务。所以其实上仍然是只有一个事务的,所以catch住不允许回滚也是没有任何问题的,因为事务本身是属于addPerson的,而不属于editById。


但是我们这么来玩:


@Transactional
    @Override
    public boolean addPerson(User user) {
        try {
            personService.editById(user);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }
    @Transactional
    @Override
    public boolean editById(User user) {
        userMapper.updateByIdSelective(user);
        System.out.println(1 / 0);
        return false;
    }

就毫无疑问会抛出如下异常:

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


但这么玩,去掉addPerson方法的事务,只保留editById的事务呢?

@Override
    public boolean addPerson(User user) {
        try {
            personService.editById(user);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }
    @Transactional
    @Override
    public boolean editById(User user) {
        userMapper.updateByIdSelective(user);
        System.out.println(1 / 0);
        return false;
    }

发现rollback-only异常是永远不会出来的。


因此我们可以得出结论,rollback-only异常,是发生在异常本身才有可能出现,发生在子方法内部是不会出现的。因此这种现象最多是发生在事务嵌套里。


相关文章
|
14天前
|
Java 开发者 Spring
Spring高手之路24——事务类型及传播行为实战指南
本篇文章深入探讨了Spring中的事务管理,特别是事务传播行为(如REQUIRES_NEW和NESTED)的应用与区别。通过详实的示例和优化的时序图,全面解析如何在实际项目中使用这些高级事务控制技巧,以提升开发者的Spring事务管理能力。
28 1
Spring高手之路24——事务类型及传播行为实战指南
|
18天前
|
人工智能 前端开发 Java
基于开源框架Spring AI Alibaba快速构建Java应用
本文旨在帮助开发者快速掌握并应用 Spring AI Alibaba,提升基于 Java 的大模型应用开发效率和安全性。
基于开源框架Spring AI Alibaba快速构建Java应用
|
26天前
|
前端开发 Java 数据库连接
Spring 框架:Java 开发者的春天
Spring 框架是一个功能强大的开源框架,主要用于简化 Java 企业级应用的开发,由被称为“Spring 之父”的 Rod Johnson 于 2002 年提出并创立,并由Pivotal团队维护。
43 1
Spring 框架:Java 开发者的春天
|
8天前
|
XML Java 数据库连接
Spring中的事务是如何实现的
Spring中的事务管理机制通过一系列强大的功能和灵活的配置选项,为开发者提供了高效且可靠的事务处理手段。无论是通过注解还是AOP配置,Spring都能轻松实现复杂的事务管理需求。掌握这些工具和最佳实践,能
14 3
|
26天前
|
Java 数据库连接 开发者
Spring 框架:Java 开发者的春天
【10月更文挑战第27天】Spring 框架由 Rod Johnson 在 2002 年创建,旨在解决 Java 企业级开发中的复杂性问题。它通过控制反转(IOC)和面向切面的编程(AOP)等核心机制,提供了轻量级的容器和丰富的功能,支持 Web 开发、数据访问等领域,显著提高了开发效率和应用的可维护性。Spring 拥有强大的社区支持和丰富的生态系统,是 Java 开发不可或缺的工具。
|
30天前
|
存储 人工智能 Java
将 Spring AI 与 LLM 结合使用以生成 Java 测试
AIDocumentLibraryChat 项目通过 GitHub URL 为指定的 Java 类生成测试代码,支持 granite-code 和 deepseek-coder-v2 模型。项目包括控制器、服务和配置,能处理源代码解析、依赖加载及测试代码生成,旨在评估 LLM 对开发测试的支持能力。
36 1
|
2月前
|
Java 数据库连接 数据库
spring复习05,spring整合mybatis,声明式事务
这篇文章详细介绍了如何在Spring框架中整合MyBatis以及如何配置声明式事务。主要内容包括:在Maven项目中添加依赖、创建实体类和Mapper接口、配置MyBatis核心配置文件和映射文件、配置数据源、创建sqlSessionFactory和sqlSessionTemplate、实现Mapper接口、配置声明式事务以及测试使用。此外,还解释了声明式事务的传播行为、隔离级别、只读提示和事务超时期间等概念。
spring复习05,spring整合mybatis,声明式事务
|
1月前
|
人工智能 缓存 Java
深入解析Spring AI框架:在Java应用中实现智能化交互的关键
【10月更文挑战第12天】Spring AI 是 Spring 框架家族的新成员,旨在满足 Java 应用程序对人工智能集成的需求。它支持自然语言处理、图像识别等多种 AI 技术,并提供与云服务(如 OpenAI、Azure Cognitive Services)及本地模型的无缝集成。通过简单的配置和编码,开发者可轻松实现 AI 功能,同时应对模型切换、数据安全及性能优化等挑战。
104 3
|
1月前
|
Java 关系型数据库 MySQL
Spring事务失效,我总结了这7个主要原因
本文详细探讨了Spring事务在日常开发中常见的七个失效原因,包括数据库不支持事务、类不受Spring管理、事务方法非public、异常被捕获、`rollbackFor`属性配置错误、方法内部调用事务方法及事务传播属性使用不当。通过具体示例和源码分析,帮助开发者更好地理解和应用Spring事务机制,避免线上事故。适合所有使用Spring进行业务开发的工程师参考。
30 2
|
1月前
|
Java 程序员 Spring
Spring事务的1道面试题
每次聊起Spring事务,好像很熟悉,又好像很陌生。本篇通过一道面试题和一些实践,来拆解几个Spring事务的常见坑点。
Spring事务的1道面试题