如何高效将JOOQ Records映射到Kotlin复杂领域模型
解决JOOQ多表查询映射到Kotlin嵌套数据类的问题
首先先修正你数据类的语法错误,否则无法编译:
data class Role( val id: String, val personId: String, val name: String, val description: String, // 补充类型声明 val authorizations: List<Authorization> = emptyList() // 修正字段名和类型声明 )
你的原查询存在两个问题:一是没有关联AUTHORIZATION表,无法获取角色对应的权限数据;二是fetchOne()只能返回单条扁平记录,无法处理多表join后产生的重复分组数据。以下是完整的解决方案:
1. 修正查询语句
首先写出包含所有关联表的查询,获取完整的扁平结果集:
val records = mysqlDSLContext.select() .from(PERSON) .leftJoin(ROLE).on(PERSON.ID.eq(ROLE.PERSON_ID)) .leftJoin(AUTHORIZATION).on(ROLE.ID.eq(AUTHORIZATION.ROLE_ID)) .leftJoin(VEHICLE).on(PERSON.ID.eq(VEHICLE.PERSON_ID)) .fetch()
2. 分组映射为嵌套数据类
由于多表join后,同一个Person会对应多条记录(每条记录对应一个Role或Authorization),我们需要通过分组聚合来构造嵌套结构:
val persons = records // 第一步:按Person ID分组,聚合同一个人的所有关联记录 .groupBy { it[PERSON.ID] } .mapValues { (_, personRecords) -> val baseRecord = personRecords.first() // 构造Person基础信息 val person = Person( id = baseRecord[PERSON.ID], name = baseRecord[PERSON.NAME], state = baseRecord[PERSON.STATE], // 构造Vehicle:假设一个人对应一辆车,取第一条非空的Vehicle记录 vehicle = personRecords.firstNotNullOf { Vehicle( id = it[VEHICLE.ID], personId = it[VEHICLE.PERSON_ID], name = it[VEHICLE.NAME], type = it[VEHICLE.TYPE] ) }, // 构造Role列表:先按Role ID分组,聚合每个角色的权限 roles = personRecords .filter { it[ROLE.ID] != null } // 过滤无角色的记录 .groupBy { it[ROLE.ID] } .mapValues { (_, roleRecords) -> val roleBase = roleRecords.first() Role( id = roleBase[ROLE.ID], personId = roleBase[ROLE.PERSON_ID], name = roleBase[ROLE.NAME], description = roleBase[ROLE.DESCRIPTION], // 构造当前角色的权限列表 authorizations = roleRecords .filter { it[AUTHORIZATION.ID] != null } .map { Authorization( id = it[AUTHORIZATION.ID], roleId = it[AUTHORIZATION.ROLE_ID], type = it[AUTHORIZATION.TYPE] ) } ) } .values .toList() ) person } .values .toList()
3. 优化思路(通用方案)
如果需要复用这类嵌套映射逻辑,可以封装通用的分组工具函数,比如:
- 提取
mapToPerson、mapToRole等单独的映射函数 - 针对一对多、一对一关系封装通用分组器,减少重复代码
这种手动分组的方式性能高效,逻辑清晰,完全基于JOOQ的基础API实现,不需要额外依赖。
内容的提问来源于stack exchange,提问作者da_user_2023
相关产品推荐
相关产品推荐

