JOOQ自定义ExecuteListener未触发捕获DataChangedException
问题描述
我正尝试实现通用的乐观锁解决方案,目标是当记录版本发生变更时执行指定逻辑。目前已完成的实现如下:
- 自定义
ExecuteListener实例,用于捕获DataChangedException,该监听器已注册为Spring Beanclass 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.")) } } } - 配置类中注册监听器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,可选择适配的拦截方案:- 实现jOOQ提供的
RecordListener:该监听器的回调与UpdatableRecord的全生命周期内存操作绑定,可覆盖包括checkIfChanged在内的记录操作全流程,能正常捕获这类前置校验抛出的异常 - 在全局统一异常处理层(比如Spring的全局异常拦截组件、自定义AOP切面)拦截
DataChangedException,完成异常类型转换,无需依赖jOOQ内部执行链路的回调触发
- 实现jOOQ提供的
内容的提问来源于stack exchange,提问作者Cube.
相关产品推荐
相关产品推荐

