JOOQ Multiset查询结合自定义类型时触发DataTypeException的问题及解决方法寻求
JOOQ Multiset查询结合自定义类型时触发DataTypeException的问题及解决方法寻求
最近在项目里用jOOQ做查询时碰到了一个挺头疼的问题,想跟大家聊聊细节,也求个可行的临时 workaround。
问题背景
我们项目用的是PostgreSQL,定义了一个自定义复合类型:
CREATE TYPE attached_file AS ( id int8, file_name varchar(255), file_size int8, bucket bucket_type );
查询里用到了multiset()调用,内部包含了涉及这个自定义类型的复杂表达式。偶尔会触发DataTypeException,错误信息大概是:
1234 (BigDecimal) cannot be converted to Bucket.class
看报错栈是在org.jooq.impl.JSONReader的312行,对应代码逻辑是:
if (value instanceof List) l = patchRecord(ctx, multiset, actualRow, (List<Object>) value); else if (value instanceof Map) l = patchRecord(ctx, multiset, actualRow, new ArrayList<>(((Map<?, ?>) value).values())); else throw new IllegalStateException();
调试发现的根因
我debug了一下,发现问题出在actualRow参数和value的字段顺序不匹配:
value是个LinkedHashMap,但它的键值对顺序和actualRow里预期的字段顺序完全不一致patchRecord方法直接调用map.values()把值按顺序取出来,返回的Object列表顺序完全错位- 结果就出现了把
file_size字段的数字值强行转成Bucket枚举的离谱情况,直接触发类型转换异常
看起来这像是jOOQ的一个bug,但很难稳定复现,也没法快速写出精准的测试用例。从代码逻辑看,patchRecord处理Map的时候应该按key匹配字段,而不是直接取values的顺序,但现在没修复的情况下,得先找个临时办法绕开。
已经尝试过但没用的Workaround
我已经试了几个方向,但都没解决问题:
- 切换到其他multiset模拟模式
- 替换成
multiset_agg - 调整查询里
row()的写法,比如把原来的:
改成了:row( LOAD_BOOKING_INFO.fkBookedBy(), LOAD_BOOKING_INFO.fkBookedBy().portalUser(), LOAD_BOOKING_INFO.fkBookedBy().disposalFacilityAccessCode(), LOAD_BOOKING_INFO.fkBookedBy().disposalFacilityContact(), LOAD_BOOKING_INFO.fkBookedBy().companyAccessCode().company() ).convertFrom(mapping(AuthLoginDto::new))
但因为自定义类型字段还是隐含了关联查询,本质上没解决顺序不匹配的问题。row( AUTH_LOGIN.asTable(), AUTH_LOGIN.portalUser(), AUTH_LOGIN.disposalFacilityAccessCode(), AUTH_LOGIN.disposalFacilityContact(), COMPANY_ACCESS_CODE.company() ).convertFrom(mapping(AuthLoginDto::new)).as(BOOKED_BY)
现在急需一个能临时绕开这个问题的办法,有没有朋友碰到过类似情况,或者有什么思路可以试试?
内容来源于stack exchange
相关产品推荐
相关产品推荐

