如何用jOOQ的unionAll合并同结构异表查询并映射不同Record
问题描述
我有两个结构完全一致的表(tableA对应EntityARecord,tableB对应EntityBRecord),列名和数据类型完全相同。尝试用Kotlin结合jOOQ执行查询并合并结果,再映射到对应Record时遇到编译错误。
代码示例
fun findEntitiesByGroupId(groupId: UUID): ResultData { val entityAQuery = DSL.using(configuration) .selectFrom(ENTITY_A) .where(ENTITY_A.GROUP_ID.eq(groupId)) val entityBQuery = DSL.using(configuration) .selectFrom(ENTITY_B) .where(ENTITY_B.GROUP_ID.eq(groupId)) val entityA = mutableListOf<EventHistory>() val entityB = mutableListOf<EventHistory>() entityAQuery.unionAll(entityBQuery).forEach { when (it) { is EntityARecord -> entityA.add(mapEntityA(it)) is EntityBRecord -> entityB.add(mapEntityB(it)) else -> throw AssertionError("Invalid entity type") } } return ResultData(entityA, entityB) }
编译错误信息
Type mismatch. Required: Select<out EntityARecord!>! Found: SelectConditionStep<EntityBRecord!>
找不到jOOQ针对不同Record类型合并场景的文档,仅能找到同类型Record合并的案例,求解决办法。
解决方案
jOOQ的unionAll要求左右两边查询返回的Record类型必须一致,这是因为SQL的UNION操作本身不会保留数据的源表信息,合并后的结果集无法区分原数据属于哪个表的Record实例。针对这个问题,有两种可行的解决思路:
思路1:手动添加源表标识字段
在两个查询中额外添加一个标记字段,用来标识数据来源,合并后根据该字段区分处理:
fun findEntitiesByGroupId(groupId: UUID): ResultData { val entityAQuery = DSL.using(configuration) .select(ENTITY_A.asterisk(), DSL.inline("A").`as`("source")) .from(ENTITY_A) .where(ENTITY_A.GROUP_ID.eq(groupId)) val entityBQuery = DSL.using(configuration) .select(ENTITY_B.asterisk(), DSL.inline("B").`as`("source")) .from(ENTITY_B) .where(ENTITY_B.GROUP_ID.eq(groupId)) val entityA = mutableListOf<EventHistory>() val entityB = mutableListOf<EventHistory>() entityAQuery.unionAll(entityBQuery).forEach { record -> val source = record.getValue("source", String::class.java) when (source) { "A" -> entityA.add(mapEntityA(record.into(EntityARecord::class.java))) "B" -> entityB.add(mapEntityB(record.into(EntityBRecord::class.java))) else -> throw AssertionError("Invalid entity type") } } return ResultData(entityA, entityB) }
通过DSL.inline("A")添加源表标识字段,合并后根据该字段判断数据来源,再用into()方法将通用Record转换为对应实体Record。
思路2:分别执行两个查询
既然合并查询无法直接区分Record类型,可直接分开执行两个查询,分别处理结果:
fun findEntitiesByGroupId(groupId: UUID): ResultData { val entityA = DSL.using(configuration) .selectFrom(ENTITY_A) .where(ENTITY_A.GROUP_ID.eq(groupId)) .map { mapEntityA(it) } .toMutableList() val entityB = DSL.using(configuration) .selectFrom(ENTITY_B) .where(ENTITY_B.GROUP_ID.eq(groupId)) .map { mapEntityB(it) } .toMutableList() return ResultData(entityA, entityB) }
这种方式逻辑更直观,避免了类型转换的麻烦,适合不需要合并结果集统一处理的场景。
补充说明
jOOQ的类型系统严格遵循SQL语义,UNION操作返回的是统一结构的结果集类型,无法保留原表的Record类型信息,因此直接用不同Record类型的查询执行unionAll会触发类型不匹配错误,上述两种方式是最直接的解决办法。
内容的提问来源于stack exchange,提问作者Lucas Guimaraes
相关产品推荐
相关产品推荐

