You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

JOOQ自定义ExecuteListener未触发捕获DataChangedException

问题描述

我正尝试实现通用的乐观锁解决方案,目标是当记录版本发生变更时执行指定逻辑。目前已完成的实现如下:

  1. 自定义ExecuteListener实例,用于捕获DataChangedException,该监听器已注册为Spring Bean
    class LockingListener : DefaultExecuteListener() {
        override fun exception(ctx: ExecuteContext) {
            val exception = ctx.exception()
            if (exception is DataChangedException) {
                ctx.exception(IllegalStateException("Accessed data has been altered mid-operation."))
            }
        }
    }
    
  2. 配置类中注册监听器Provider:
    @Configuration
    class JooqConfig {
        @Bean
        fun lockingListenerProvider() = DefaultExecuteListenerProvider(LockingListener())
    }
    

已做过的验证:

  • 在org.jooq.impl.ExecuteListeners#get方法设置断点,确认自定义监听器已与LoggerListener、JooqExceptionTranslator一同加载到监听器链中

但运行测试用例时存在异常表现:
调用UpdateableRecord#update方法触发的DataChangedException并未被自定义监听器捕获,直接抛出如下堆栈信息,没有出现预期的IllegalStateException;且经调试确认LockingListener#exception方法根本没有被触发执行:

org.jooq.exception.DataChangedException: Database record has been changed or doesn't exist any longer
    at org.jooq.impl.UpdatableRecordImpl.checkIfChanged(UpdatableRecordImpl.java:540)
    at org.jooq.impl.UpdatableRecordImpl.storeMergeOrUpdate0(UpdatableRecordImpl.java:349)
    at org.jooq.impl.UpdatableRecordImpl.storeUpdate0(UpdatableRecordImpl.java:241)
    at org.jooq.impl.UpdatableRecordImpl.access$100(UpdatableRecordImpl.java:89)
    at org.jooq.impl.UpdatableRecordImpl$2.operate(UpdatableRecordImpl.java:232)
    at org.jooq.impl.RecordDelegate.operate(RecordDelegate.java:149)
    at org.jooq.impl.UpdatableRecordImpl.storeUpdate(UpdatableRecordImpl.java:228)
    at org.jooq.impl.UpdatableRecordImpl.update(UpdatableRecordImpl.java:165)
原因分析

ExecuteListener的所有回调(包括exception方法),仅会在jOOQ实际发起数据库SQL执行的流程中被触发。
本次遇到的DataChangedException是jOOQ在客户端内存层面做乐观锁版本校验时抛出的——从堆栈可以看到,异常抛出点在UpdatableRecordImpl.checkIfChanged阶段,此时流程还停留在内存校验环节,没有走到生成SQL、提交数据库执行的核心入口,自然不会触发绑定在SQL执行流程上的ExecuteListener回调。

解决方法
  • 明确两类异常的处理边界:ExecuteListener可以正常捕获SQL执行阶段抛出的异常(比如语法错误、连接中断、数据库约束冲突等),但无法覆盖记录操作流程中纯内存校验环节抛出的DataChangedException
  • 针对内存校验类的DataChangedException,可选择适配的拦截方案:
    1. 实现jOOQ提供的RecordListener:该监听器的回调与UpdatableRecord的全生命周期内存操作绑定,可覆盖包括checkIfChanged在内的记录操作全流程,能正常捕获这类前置校验抛出的异常
    2. 在全局统一异常处理层(比如Spring的全局异常拦截组件、自定义AOP切面)拦截DataChangedException,完成异常类型转换,无需依赖jOOQ内部执行链路的回调触发

内容的提问来源于stack exchange,提问作者Cube.

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.31 13:24:06