尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
Spring编程式事务实战:手动控制事务边界,精准管理数据一致性
1. 为什么需要编程式事务在日常开发中我们最常使用的是声明式事务也就是通过Transactional注解来管理事务。这种方式简单方便适合大多数场景。但声明式事务有一个明显的局限性它的事务边界是固定的要么整个方法成功提交要么全部回滚。想象一个电商订单处理的场景用户下单后需要依次完成库存扣减、优惠券核销、订单创建三个操作。如果使用声明式事务这三个操作要么全部成功要么全部失败。但实际业务中我们可能希望库存扣减失败时整个事务回滚而优惠券核销失败时只记录日志并继续创建订单。这种精细控制的需求就是编程式事务的用武之地。编程式事务的核心价值在于精确控制。你可以在任意代码位置开启事务根据业务条件决定提交或回滚设置保存点实现部分回滚灵活处理异常情况2. 编程式事务的两种实现方式2.1 PlatformTransactionManager方式这是最基础也是最灵活的方式。我们通过一个完整的订单处理案例来演示Service public class OrderService { Autowired private PlatformTransactionManager txManager; Autowired private InventoryDao inventoryDao; Autowired private CouponDao couponDao; Autowired private OrderDao orderDao; public void processOrder(OrderDTO order) { // 定义事务属性 DefaultTransactionDefinition def new DefaultTransactionDefinition(); // 设置事务传播行为为REQUIRES_NEW表示新建独立事务 def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW); // 开启事务 TransactionStatus status txManager.getTransaction(def); try { // 第一步扣减库存必须成功 inventoryDao.reduceStock(order.getSkuId(), order.getQuantity()); // 第二步核销优惠券允许失败 try { couponDao.useCoupon(order.getUserId(), order.getCouponId()); } catch (Exception e) { log.warn(优惠券核销失败继续处理订单, e); } // 第三步创建订单必须成功 orderDao.createOrder(order); // 提交事务 txManager.commit(status); } catch (Exception e) { // 发生异常时回滚 txManager.rollback(status); throw new RuntimeException(订单处理失败, e); } } }几个关键点DefaultTransactionDefinition用于定义事务属性包括隔离级别、传播行为等getTransaction()方法会返回一个TransactionStatus对象代表当前事务状态业务代码执行完毕后需要显式调用commit()或rollback()2.2 TransactionTemplate方式对于更简单的场景Spring提供了TransactionTemplate来简化编程式事务Service public class PaymentService { Autowired private TransactionTemplate transactionTemplate; Autowired private PaymentDao paymentDao; Autowired private AccountDao accountDao; public void processPayment(Payment payment) { transactionTemplate.execute(status - { try { // 扣减账户余额 accountDao.debit(payment.getUserId(), payment.getAmount()); // 记录支付流水 paymentDao.create(payment); return Boolean.TRUE; } catch (Exception e) { // 标记事务为回滚状态 status.setRollbackOnly(); log.error(支付处理失败, e); return Boolean.FALSE; } }); } }TransactionTemplate的优点自动管理事务生命周期无需手动commit/rollback支持Lambda表达式代码更简洁内置异常处理机制3. 高级应用场景3.1 多数据源事务控制当系统使用多个数据源时声明式事务很难跨数据源保证一致性。编程式事务可以这样解决public void multiDataSourceOperation() { // 获取第一个数据源的事务管理器 DataSourceTransactionManager ds1Manager ctx.getBean(ds1TransactionManager); // 获取第二个数据源的事务管理器 DataSourceTransactionManager ds2Manager ctx.getBean(ds2TransactionManager); // 定义事务属性 DefaultTransactionDefinition def new DefaultTransactionDefinition(); def.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED); // 开启两个事务 TransactionStatus ds1Status ds1Manager.getTransaction(def); TransactionStatus ds2Status ds2Manager.getTransaction(def); try { // 操作第一个数据源 ds1Dao.updateSomething(); // 操作第二个数据源 ds2Dao.updateSomethingElse(); // 都成功才提交 ds1Manager.commit(ds1Status); ds2Manager.commit(ds2Status); } catch (Exception e) { // 任一失败则全部回滚 ds1Manager.rollback(ds1Status); ds2Manager.rollback(ds2Status); throw e; } }3.2 保存点与部分回滚编程式事务支持设置保存点实现更细粒度的控制public void partialRollbackDemo() { TransactionStatus status txManager.getTransaction(new DefaultTransactionDefinition()); try { // 第一步操作 step1(); // 设置保存点 Object savepoint status.createSavepoint(); try { // 第二步操作可能失败 step2(); } catch (BusinessException e) { // 只回滚到保存点保留第一步操作 status.rollbackToSavepoint(savepoint); } // 第三步操作 step3(); txManager.commit(status); } catch (Exception e) { txManager.rollback(status); } }4. 常见问题与最佳实践4.1 事务嵌套处理编程式事务与声明式事务混用时需要特别注意传播行为。例如Transactional public void outerMethod() { // 这里使用的是声明式事务 // 内层编程式事务 TransactionTemplate template new TransactionTemplate(transactionManager); template.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW); template.execute(status - { // 这里的代码会在新事务中执行 return null; }); }最佳实践避免过度嵌套事务明确设置传播行为内层事务尽量使用REQUIRES_NEW或NESTED4.2 异常处理要点编程式事务中异常处理尤为关键捕获异常后如果不希望回滚需要清除回滚标记try { // 业务代码 } catch (Exception e) { // 标记事务不需要回滚 status.setRollbackOnly(false); // 处理异常 }对于需要回滚的特定异常try { // 业务代码 } catch (BusinessException e) { // 只回滚特定异常 status.setRollbackOnly(); throw e; } catch (Exception e) { // 其他异常不处理 throw e; }4.3 性能优化建议避免长事务编程式事务需要手动关闭容易忘记导致事务时间过长合理设置超时通过DefaultTransactionDefinition.setTimeout()设置复用TransactionTemplate避免重复创建谨慎使用保存点保存点会消耗数据库资源我在实际项目中遇到过这样一个案例一个批量处理任务使用编程式事务但没有设置超时时间。当处理大量数据时事务执行时间过长导致数据库连接被占满。后来通过添加超时设置和分批次处理解决了这个问题。
RELATED

相关推荐

C++11生产者消费者模型:用互斥锁与条件变量实现线程安全队列

C++11生产者消费者模型:用互斥锁与条件变量实现线程安全队列

1. 项目概述:从“共享”到“有序”的经典挑战在并发编程的世界里,生产者消费者模型是一个绕不开的经典问题。它描述的场景非常生活化:一个或多个“生产者”线程负责生产数据,放入一个共享的“缓冲区”;同时&#xff0c…

📅 2026/9/29 14:13:26
C/C++静态库封装实战:从原理到工程实践

C/C++静态库封装实战:从原理到工程实践

1. 为什么我们需要用静态库来封装C/C核心代码?干了这么多年C/C开发,我见过太多因为代码保护不当而引发的麻烦。你可能花了好几个月甚至几年时间,精心打磨出一个算法库、一个通信框架,或者一套业务逻辑的核心模块。当你把这些代码交…

📅 2026/10/12 22:35:53
C++实现LU分解:从数学原理到高性能代码的工程实践

C++实现LU分解:从数学原理到高性能代码的工程实践

1. 项目概述:从数学公式到C代码的旅程“线性代数-LU分解(C代码实现)”这个标题,对于任何从事科学计算、机器学习底层开发或者游戏引擎物理模拟的程序员来说,都像是一道熟悉的“家常菜”。它看似基础,却是连接理论数学与高性能计算…

📅 2026/10/16 19:06:04
MORE NEWS

更多资讯

📰

RAL色卡:颜色编码标准与跨行业应用指南

简介:这份RAL劳尔色卡电子版面向工业设计、建筑装饰、产品制造及艺术创作领域的从业者与学习者,用于解决跨场景颜色识别、比对与沟通不统一的问题。文档以RAL编号体系串联从黄色、橙色、红色、紫色、蓝色、绿色到灰色的完整色谱,每个编号对应…

📰

Landsat 8遥感影像ENVI全链路处理:从DN值到专题图复现

简介:这份ENVI遥感影像处理实验报告面向地信、遥感专业学生及实习实训者,围绕Landsat8多光谱影像的完整处理流程展开,适合课程实验、软件操作复盘与实习报告撰写参考。内容按实验目的、数据与实验区概况、图像裁剪、辐射定标与反射率求算、大…

📰

工业互联网集成应用赛全链路解析:从RFID通讯到平台建模的备赛指南

简介:这份PDF是2024年上海高职院校学生技能大赛“工业互联网集成应用师生同赛”赛项的官方样题,面向备赛的高职师生、指导教师及工业互联网集成应用方向的学习者,帮助其了解赛项规程、任务结构与考核重点。资源包共1个PDF文件,约3…

📰

AI 机器人找漏洞,开源软件价值反而更高了

「无漏洞、安全的代码现在可以实现了」——这个说法听起来很美,但它恰恰是错的。这是 Eric Raymond 关于 AI 辅助漏洞发现的一番乐观表态下 80 多条回复中的一条。Raymond 认为,人类编写的代码总量是有限的,因此缺陷总量也是有限的&#xff1…

📰

工业互联网集成应用工作站:从设备配置到平台联调的完整指南

简介:这份PDF是2024年上海高职院校学生技能大赛“工业互联网集成应用师生同赛”赛项的样题,面向高职院校参赛师生及工业互联网相关专业教学人员,用于熟悉赛项流程、任务结构与评分要点。内容涵盖竞赛须知、职业素养与安全规范、扣分项说明&am…

📰

C语言底层探秘:整数与浮点数在内存里到底是怎么藏起来的?

引言很多人写C语言,天天写int a; float b;,但很少思考一个问题:我们写的数字,在计算机内存这一大片字节格子里,到底是怎么存放的? 为什么同样一串二进制,当int读是9,当float读就变成…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬