如何使用jOOQ基于多个SQL查询结果构建POJO实例
jOOQ多查询结果映射嵌套结构POJO实现思路
常规单SQL查询直接调用into(目标类.class)是jOOQ提供的平结果映射能力,遇到字段来自多个查询、包含嵌套对象/集合属性的场景,可以按下面几种方案实现:
1. 分步查询+手动组装
这是最直观、无额外学习成本的实现方式,逻辑完全可控,调试方便,适合单条记录查询、或者各查询逻辑差异极大无法关联的场景。
// 执行第一个查询拿到基础字段 MyPojo pojo = ctx.fetch("select field1, field2 from main_table where id = ?", mainId) .into(MyPojo.class) .get(0); // 执行第二个查询拿到集合类型字段 List<SomeClass> field3Data = ctx.fetch("select * from biz_table where main_id = ?", mainId) .into(SomeClass.class); pojo.setField3(field3Data); // 执行第三个查询拿到单对象类型字段 SomeClass2 field4Data = ctx.fetch("select * from ext_table where main_id = ?", mainId) .into(SomeClass2.class) .get(0); pojo.setField4(field4Data);
注意:如果是批量查询多条主记录,不要在循环中单条查询关联表,会触发N+1性能问题,要先批量拿到所有主记录ID,一次性用in条件查询所有关联数据,再按主ID分组后批量组装。
2. 单SQL关联+jOOQ嵌套自动映射
如果几个查询的数据源可以通过JOIN整合成单条SQL,优先选这个方案,性能最好,只需要和数据库做一次交互。jOOQ 3.14及以上版本原生支持嵌套结构映射,会自动对JOIN产生的重复主记录做去重、对集合属性做分组,不需要手动处理。
List<MyPojo> result = ctx.select( // 主表字段直接映射 MAIN_TABLE.FIELD1, MAIN_TABLE.FIELD2, // 关联集合字段用「属性名.子属性名」的格式起别名,自动映射到List<SomeClass> RELATED_TABLE.ID.as("field3.id"), RELATED_TABLE.BIZ_NAME.as("field3.bizName"), // 关联单对象字段同理,自动映射到SomeClass2属性 EXT_TABLE.ID.as("field4.id"), EXT_TABLE.EXT_CONTENT.as("field4.extContent") ) .from(MAIN_TABLE) .leftJoin(RELATED_TABLE).on(RELATED_TABLE.MAIN_ID.eq(MAIN_TABLE.ID)) .leftJoin(EXT_TABLE).on(EXT_TABLE.MAIN_ID.eq(MAIN_TABLE.ID)) .where(MAIN_TABLE.ID.in(idList)) .fetch() .into(MyPojo.class);
如果是手写原生SQL不使用jOOQ代码生成的字段,只要保持别名的属性名.子属性名格式,jOOQ默认的映射器就能识别,不需要额外配置。
3. 批量查询+分组映射
这个方案是第一种手动组装的批量优化版,适合批量查询大量主记录、又无法整合成单条JOIN SQL的场景,既避免N+1问题,又不需要逐字段手动set。
// 1. 批量查询所有主记录 List<MyPojo> pojoList = ctx.fetch("select id, field1, field2 from main_table where create_time > ?", startTime) .into(MyPojo.class); Set<Long> mainIds = pojoList.stream().map(MyPojo::getId).collect(Collectors.toSet()); // 2. 批量查询所有关联集合数据,按主ID分组 Map<Long, List<SomeClass>> field3Group = ctx.fetch("select main_id, id, biz_name from related_table where main_id in (?)", mainIds) .intoGroups(RELATED_TABLE.MAIN_ID, SomeClass.class); // 3. 批量查询所有关联单对象数据,按主ID分组 Map<Long, SomeClass2> field4Group = ctx.fetch("select main_id, id, ext_content from ext_table where main_id in (?)", mainIds) .intoMap(EXT_TABLE.MAIN_ID, SomeClass2.class); // 4. 批量组装所有POJO pojoList.forEach(item -> { item.setField3(field3Group.getOrDefault(item.getId(), Collections.emptyList())); item.setField4(field4Group.get(item.getId())); });
选型参考
- 关联逻辑简单、对性能要求高,优先选单SQL嵌套映射
- 各查询逻辑独立、无法做JOIN(比如部分数据来自异构表、需要单独做统计计算),批量场景选分组映射,单条场景选直接手动组装
内容的提问来源于stack exchange,提问作者user1058106
相关产品推荐
相关产品推荐

