编程式事务,更加精细化的控制

在某些情况下,你会发现虽然使用了 @Transactional 注解 但是事务并没有回滚,后面的逻辑依旧继续执行了,为什么呢?

声明式事务注解为什么失效了

声明式事务失效的原因,主要由以下几点构成:

  • 方法可见性@Transactional 仅在 public 方法上生效
  • 自调用 :当类中的方法是通过调用同一个类当中的另外一个 @Transactional 方法的时候,事务可能不会生效。这是因为这个事务注解是通过 AOP 实现的,而 Spring 的 AOP 代理机制在这种情况下不会触发。
  • 异常处理 :只有 RuntimeExceptionError 类型的异常会触发事务回滚。如果抛出的是 checked exception,事务不会回滚,除非指定 rollback 属性
  • 代理对象 :确保是在 Spring 管理的代理对象上调用方法,如果直接使用 new 关键字实例化的对象(未被 Spring 管理),那么 Spring 的 AOP 代理机制不会触发

什么是编程式事务?有哪些优点?

编程事务(Programmatic Transaction)是一种通过代码显式地管理事务的方式,而不是依赖声明式事务(Declarative Transaction)中使用的注解或者 xml 配置,在编程式事务中,开发人员通过编写代码来开启、提交和回滚事务,以精细控制事务的边界和行为

编程式事务的优点:

  • 精细控制:编程事务允许开发者通过代码精细地控制事务的生命周期,包括开始、提交和回滚。可以更加具体业务需求,灵活地管理事务
  • 动态处理:在允许时可以根据业务逻辑的不同情况,动态决定事务的行为。特别适合需要在代码执行过程中,根据某些条件来开启、提交或者回滚事务的场景。
  • 适用于复杂事务:在一个方法中需要多次开启和关闭事务,或需要嵌套事务的复杂场景中,编程事务可以提供更大的灵活性和控制力
  • 灵活性高:能够在代码中实现复炸的事务逻辑,可以精确控制事务的边界和行为,这在需要多个步骤或调用之间共享事务上下文的时候非常有用
  • 性能提升:通过精细化控制事务的边界,减少不必要的事务开启和提交,从而减少事务开销;通过明确事务的开始和结束,可以确保事务范围尽可能的缩小,减少长时间占用数据库资源,提高系统的并发性,通过灵活的事务管理,可以在必要时才进行事务回滚,减少回滚操作带来的性能开销。

编程式事务的缺点:

  • 代码入侵性强(核心劣势) :事务逻辑与业务 紧密耦合 在一起,业务代码中会夹杂大量的事务控制代码(比如 try-catch 中执行回滚等),这样会导致代码臃肿,可读性变差,可维护性下降
  • 开发效率低,容易出错:开发者需要手动的编写事务的开启、提交和回滚,还要处理各种异常情况,增加了开发工作量,写得越多,越容易出错
  • 代码冗余:如果多个业务都需要进行事务控制,会出现大量重复代码,违反了 DRY (Don't Repeat Yourself)原则,后期修改需要改动多出的代码
  • 对开发人员要求高:开发人员需要熟悉事务的底层原理和相关 api 的使用,必须清除什么时候该提交,什么时候该回滚。

使用示例

下面是在 SpringBoot 使用编程式事务的两种方式,示例化代码如下

第一种

java 复制代码
@Service
public class MyService {

    @Resource
    private PlatformTransactionManager transactionManager;

    @Resource
    private MyMapper myMapper;

    public void myTransactionalMethod() {
        TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());

        try {
            // 业务逻辑代码
            myMapper.insertSomething(...);

            transactionManager.commit(status); // 提交事务
        } catch (Exception ex) {
            transactionManager.rollback(status); // 回滚事务
            throw ex; // 重新抛出异常
        }
    }
}

transactionManager.getTransaction(...): 会创建一个新的事务,并返回 TransactionStatus 对象,这个对象会是当前事务的状态,用它来进行事务的提交和回滚

上述代码的核心流程:开启事务→ 执行业务逻辑 → 无异常提交 / 有异常回滚

第二种

java 复制代码
@Service
public class MyService {

    @Resource
    private TransactionTemplate transactionTemplate;

    @Resource
    private MyMapper myMapper;

    public void myTransactionalMethod() {
        transactionTemplate.execute(status -> {
            try {
                // 业务逻辑代码
                myMapper.insertSomething(...);

            } catch (Exception ex) {
                status.setRollbackOnly(); // 标记事务为回滚
                throw ex; // 重新抛出异常
            }
            return null;
        });
    }
}

TransactionTemplate 是一个简化了事务的工具类,代码编写者可以将代码重心放在业务代码上,可以更加方便的实现进行提交或者回滚,使得事务处理相关的代码更加简洁、冗余更少

总结

编程式事务是通过代码显式管理事务的方式,开发者需手动编写代码控制事务的开启、提交和回滚,而非依赖声明式事务中的注解或 XML 配置,以此实现对事务边界和行为的精细控制。

推荐使用 TransactionTemplate 进行编程式事务管理


本篇文章基于 小哈书 5.13 编写,欢迎大家访问小哈书

欢迎大家访问博主的个人博客网站 AZERL7的博客-a little world for you

相关推荐
Highcharts.js1 小时前
缺失数据可视化图表开发实战|Highcharts创建人员出生统计面积图表示例
开发语言·前端·javascript·信息可视化·highcharts·图表开发
测试员周周5 小时前
【Appium 系列】第16节-WebView-H5上下文切换 — 混合应用的自动化难点
运维·开发语言·人工智能·功能测试·appium·自动化·测试用例
廿一夏8 小时前
MySql存储引擎与索引
数据库·sql·mysql
Mahir088 小时前
Spring 循环依赖深度解密:从问题本质到三级缓存源码级解析
java·后端·spring·缓存·面试·循环依赖·三级缓存
杜子不疼.8 小时前
【C++ AI 大模型接入 SDK】 - DeepSeek 模型接入(上)
开发语言·c++·chatgpt
加号38 小时前
【C#】 串口通信技术深度解析及实现
开发语言·c#
sycmancia9 小时前
Qt——编辑交互功能的实现
开发语言·qt
RyFit9 小时前
SpringAI 常见问题及解决方案大全
java·ai
石山代码9 小时前
C++ 内存分区 堆区
java·开发语言·c++
绝知此事9 小时前
【算法突围 01】线性结构与哈希表:后端开发的收纳术
java·数据结构·算法·面试·jdk·散列表