Guidewire Financials支票作废报错Java.Financials.Exception.EmptyTransactionSet求助
Guidewire Financials支票作废异常排查与解决建议
问题背景
在Guidewire Financials系统中,理赔处理人员手动点击「作废支票」按钮后,系统抛出No transactions to save及Java.Financials.Exception.EmptyTransactionSet异常。已知场景:
- 支票已成功创建,且外部集成确认完成
- 异常在手动操作约30秒后触发
- 低环境可断点调试,但无法复现该问题
- 怀疑问题与
CCLocationUtil.runAndCommit()事务处理逻辑相关
作废逻辑代码
function voidCheck() { try { gw.api.util.CCLocationUtil.runAndCommit({ if (Check.isInternalCostPayment or Check.isFrontingPayment) { Logger.info("Voiding Internal Cost Check" + Check.PublicID) Check.voidCheck() Check.updateCheckStatus(Check.CheckNumber, Date.CurrentDate.toCalendar(), TransactionStatus.TC_VOIDED) Logger.info("Voided Internal Cost Check" + Check.PublicID) } else { Logger.info("Voiding Check and Triggering Disbursement" + Check.PublicID) DisbursementVoidDetails.Instance.invokeVoidService(Check) Logger.info("Triggerred Disbursement service and got response" + Check.PublicID) Check.voidCheck() Logger.info("Check set to Pending void (${Check.Status})") } }, CurrentLocation) } catch (e : IllegalStateException) { throw new gw.api.util.DisplayableException(DisplayKey.get("Web.Financials.CheckCancel.Error.IllegalFinancialsStatus"), e) } catch (all : Exception){ Logger.error("Exception encountered during voiding of check: ${Check.PublicID} for claim ${Check.Claim.ClaimNumber} with message ",#voidCheck(),all) throw new gw.api.util.DisplayableException(DisplayKey.get("Web.Financials.CheckCancel.Error.void")) } }
排查方向
事务边界与变更有效性:
runAndCommit()自动提交事务,需确认闭包内操作是否生成了持久化变更:- 检查
Check.voidCheck()是否在特定状态(如支票已作废)下不产生数据修改 - 验证
updateCheckStatus是否被voidCheck()内部逻辑覆盖,导致最终无有效变更 - 排查
invokeVoidService外部调用是否耗时过长,引发事务超时或上下文丢失
- 检查
环境差异分析:对比生产与低环境的核心配置差异:
- 事务隔离级别(如生产用REPEATABLE READ,低环境用READ COMMITTED)
- 外部服务响应延迟(生产环境高延迟可能导致事务内操作时序异常)
- 并发操作情况(生产环境可能有后台任务同时修改Check状态)
日志与调试增强:
- 在
voidCheck()和updateCheckStatus前后添加Check状态、版本号日志,确认数据是否真的被修改 - 在
runAndCommit()闭包末尾输出当前事务变更集:def txManager = gw.api.persistence.TransactionManager.current() Logger.info("Transaction changes for check ${Check.PublicID}: ${txManager.getChanges()}") - 单独捕获
EmptyTransactionSet异常并添加上下文日志,避免被通用Exception块覆盖
- 在
runAndCommit()机制验证:确认该方法是否会在无变更时强制提交事务,触发系统级EmptyTransactionSet异常。
解决建议
拆分事务与外部调用:将
invokeVoidService移出runAndCommit()事务块,避免外部调用导致事务超时或上下文问题:function voidCheck() { try { // 先处理外部服务调用,再执行本地事务操作 if (!Check.isInternalCostPayment && !Check.isFrontingPayment) { Logger.info("Triggering Disbursement service for check ${Check.PublicID}") DisbursementVoidDetails.Instance.invokeVoidService(Check) Logger.info("Disbursement service response received for check ${Check.PublicID}") } gw.api.util.CCLocationUtil.runAndCommit({ if (Check.isInternalCostPayment or Check.isFrontingPayment) { Logger.info("Voiding Internal Cost Check ${Check.PublicID}") Check.voidCheck() Check.updateCheckStatus(Check.CheckNumber, Date.CurrentDate.toCalendar(), TransactionStatus.TC_VOIDED) Logger.info("Internal Cost Check ${Check.PublicID} voided") } else { Logger.info("Setting check ${Check.PublicID} to pending void") Check.voidCheck() Logger.info("Check ${Check.PublicID} status: ${Check.Status}") } // 校验变更集,避免空事务提交 def txManager = gw.api.persistence.TransactionManager.current() if (!txManager.getChanges()) { Logger.error("No transaction changes detected for check ${Check.PublicID}, aborting commit") txManager.rollback() throw new gw.api.util.DisplayableException(DisplayKey.get("Web.Financials.CheckCancel.Error.NoChanges")) } }, CurrentLocation) } catch (e : IllegalStateException) { throw new gw.api.util.DisplayableException(DisplayKey.get("Web.Financials.CheckCancel.Error.IllegalFinancialsStatus"), e) } catch (all : Exception){ Logger.error("Exception during voiding check ${Check.PublicID} (claim ${Check.Claim.ClaimNumber}): ${all.message}", all) throw new gw.api.util.DisplayableException(DisplayKey.get("Web.Financials.CheckCancel.Error.void")) } }前置状态校验:在进入事务块前,检查支票状态是否允许作废,避免无效操作:
if (Check.Status in [TransactionStatus.TC_VOIDED, TransactionStatus.TC_PENDING_VOID]) { throw new gw.api.util.DisplayableException(DisplayKey.get("Web.Financials.CheckCancel.Error.AlreadyVoided")) }调整操作顺序:对于内部支付场景,尝试先执行
updateCheckStatus再调用voidCheck(),避免状态覆盖导致无变更:Check.updateCheckStatus(Check.CheckNumber, Date.CurrentDate.toCalendar(), TransactionStatus.TC_VOIDED) Check.voidCheck()自定义事务校验:在
runAndCommit()闭包内主动校验变更集,若为空则回滚并抛出明确业务异常,替代系统级的EmptyTransactionSet异常。
内容的提问来源于stack exchange,提问作者Jakub Petrik
相关产品推荐
相关产品推荐

