如何在SQLRPGLE中控制Commit和Rollback操作
SQLRPGLE事务提交(Commit)与回滚(Rollback)控制规范
前置配置校验
很多人写了一堆事务逻辑完全不生效,第一步先排查两个基础配置:
- 编译SQLRPGLE程序时,提交控制级别不能选
COMMIT(*NONE),要根据业务隔离需求选COMMIT(*CHG)、COMMIT(*CS)、COMMIT(*RR)等级别,选*NONE的话所有SQL操作会执行完自动提交,显式写的Commit/Rollback完全无效。 - 所有参与事务的物理文件、逻辑文件必须挂接系统日志(Journal),未加日志的文件无法纳入事务管控,执行DML时会直接抛出SQLSTATE 57007类错误。
- 不要混用RPG原生DML操作码(WRITE/UPDATE/DELETE等)和SQL语句做事务控制,原生操作默认不纳入SQL事务边界,要让原生操作走事务,必须在F表定义行加上
COMMIT关键字。
事务边界核心原则
- 事务要尽可能短,绝对不要在事务中间加用户交互、长耗时外部调用、等待输入这类操作,长事务会长时间持有记录锁,轻则拖慢系统性能,重则引发大面积死锁。
- 一个事务内只放逻辑上强原子关联的操作,比如订单头写入、订单明细写入、库存扣减这三个必须同成同败,就放在同一个事务里,无关的查询、日志写入这类操作不要塞进事务。
- 永远不要依赖程序正常结束的自动提交逻辑,必须显式编写Commit/Rollback分支,避免程序异常终止时出现半提交的脏数据。
标准事务实现逻辑
每执行完一条写操作(INSERT/UPDATE/DELETE),必须立刻检查SQL返回状态,不要等所有操作跑完再统一判断,出错第一时间回滚。不要用WHENEVER SQLERROR CONTINUE后跳过状态检查,这是漏回滚的最高发原因。
标准实现参考代码:
ctl-opt main(OrderTran) dftactgrp(*no) actgrp(*new) commit(*chg); dcl-proc OrderTran; dcl-s sqlstate char(5); dcl-s affectRow int(10); dcl-s errCode int(5) inz(0); // 显式设置事务隔离级别为读提交,普通业务场景通用 exec sql SET TRANSACTION ISOLATION LEVEL READ COMMITTED; // 写入订单头 exec sql INSERT INTO ORDERHDR (ORDER_ID, CUST_ID, CRT_TS, TOTAL_AMT) VALUES (:iOrderId, :iCustId, current timestamp, :iTotalAmt); if sqlstate <> '00000' and sqlstate <> '01000'; errCode = 101; exec sql ROLLBACK; return errCode; endif; // 写入订单明细 exec sql INSERT INTO ORDER_DTL (ORDER_ID, LINE_NO, PROD_ID, BUY_QTY, UNIT_PRICE) VALUES (:iOrderId, :iLineNo, :iProdId, :iBuyQty, :iUnitPrice); if sqlstate <> '00000' and sqlstate <> '01000'; errCode = 102; exec sql ROLLBACK; return errCode; endif; // 扣减库存 exec sql UPDATE INVENTORY SET STOCK_QTY = STOCK_QTY - :iBuyQty WHERE PROD_ID = :iProdId AND STOCK_QTY >= :iBuyQty; if sqlstate <> '00000' and sqlstate <> '01000'; errCode = 103; exec sql ROLLBACK; return errCode; endif; // 校验库存扣减影响行数,避免库存不足时update 0行还正常提交 exec sql GET DIAGNOSTICS :affectRow = ROW_COUNT; if affectRow = 0; errCode = 104; exec sql ROLLBACK; return errCode; endif; // 所有步骤校验通过,最终提交 exec sql COMMIT; if sqlstate <> '00000'; errCode = 105; exec sql ROLLBACK; return errCode; endif; return 0; // 事务执行成功返回0 end-proc;
常见踩坑规避
- 批量操作不要单条提交:比如批量导入千条级数据,不要每插一条就Commit,按业务场景选100-500条的批次提交即可,单条提交会产生大量日志,性能会差一个数量级。
- 跨程序调用注意事务隔离:同一个活动组(ACTGRP)下的所有程序共享同一个事务,被调用程序里写的Commit/Rollback会直接把当前程序未提交的操作一起提交/回滚,跨服务调用优先给程序分配独立活动组,避免事务互相干扰。
- 保存点(SAVEPOINT)不能替代全量回滚:回滚到指定保存点不会结束当前事务,只有最终执行Commit/Rollback才会真正关闭事务、释放锁。
- 非必要不要用
COMMIT HOLD/ROLLBACK HOLD:带HOLD参数的提交/回滚会在操作完成后继续持有记录锁,普通业务场景用无HOLD版本即可,避免不必要的锁占用引发等待。 - 异常兜底逻辑不用额外开发:如果程序遇到断电、作业被强制结束这类极端异常,IBM i操作系统会自动回滚该作业下所有未提交的事务,不会出现脏数据,前提是你没有在错误分支做半提交操作。
内容的提问来源于stack exchange,提问作者Kunal Roy
相关产品推荐
相关产品推荐

