MyBatis转换结果至Java Record关联报错排查求助
问题描述
我们正将MyBatis框架中用作DTO的Java POJO迁移为Java Record,使用的两个Record类型如下:
public record User(Integer cprid, String inlognaam, String upn, Boolean actief, String email, Locatie locatie, Collection<RoleExtra> rolesExtra) {} public record RoleExtra(Role role) { }
用户的标识符为cprid,Locatie和Role是简单枚举。
MyBatis查询语句如下:
<select id="getAllUsers" resultMap="suezUserResult"> SELECT g.inlognaam, g.cprid, g.upn, g.actief, g.email, g.locatie, grl.rol FROM suez.gebruikers g LEFT JOIN suez.gebruikers_rollen_link grl USING (cprid) WHERE cprid = 107884 ORDER BY g.naam </select>
查询返回以下元组:
inlognaam | cprid | upn | actief | email | locatie | rol -----------+--------+-----------------------------+--------+-----------------------------+---------+--------- msmith | 107884 | m.smith@amsterdamumc.nl | t | m.smith@amsterdamumc.nl | AMC | ROLE_PI msmith | 107884 | m.smith@amsterdamumc.nl | t | m.smith@amsterdamumc.nl | AMC | ROLE_PL
使用的结果映射配置如下:
<resultMap id="suezUserResult" type="amr.bi.elsalvador.domain.suez.User"> <constructor> <arg column="cprid" javaType="java.lang.Integer"/> <arg column="inlognaam" javaType="java.lang.String"/> <arg column="upn" javaType="java.lang.String"/> <arg column="actief" javaType="java.lang.Boolean"/> <arg column="email" javaType="java.lang.String"/> <arg column="locatie" javaType="amr.bi.elsalvador.enums.Locatie"/> <arg javaType="java.util.Collection" resultMap="roleExtraResult" /> </constructor> <id column="cprid"/> </resultMap> <resultMap type="amr.bi.elsalvador.domain.suez.RoleExtra" id="roleExtraResult"> <constructor> <arg column="rol" javaType="amr.bi.elsalvador.enums.Role" /> </constructor> </resultMap>
调用getAllUsers时出现异常:
Error instantiating class amr.bi.elsalvador.domain.suez.User with invalid types (Integer,String,String,Boolean,String,Locatie,Collection) or values (107884,msmith,m.smith@amsterdamumc.nl,true,m.smith@amsterdamumc.nl,AMC,**RoleExtra[role=Principal Investigator]**). Cause: java.lang.IllegalArgumentException: argument type mismatch
看起来MyBatis未合并角色生成单个User实例,而是为每个角色生成一个,导致参数类型不匹配,请问哪里配置错误?
问题分析与解决
当前配置的核心错误是直接在构造器的<arg>标签中用resultMap映射集合,MyBatis无法通过这种方式将多行结果中的角色合并为一个Collection<RoleExtra>实例,反而会把单条RoleExtra对象直接传给User的构造器,导致类型不匹配(期望Collection但实际传入单个RoleExtra)。
正确的做法是使用MyBatis的<collection>标签处理关联集合,结合Record构造器的参数顺序完成映射:
修正后的结果映射配置
<resultMap id="suezUserResult" type="amr.bi.elsalvador.domain.suez.User"> <!-- 主键需放在构造器前,用于MyBatis识别重复行并合并 --> <id column="cprid" property="cprid"/> <!-- 构造器映射基础字段 --> <constructor> <arg column="cprid" javaType="java.lang.Integer"/> <arg column="inlognaam" javaType="java.lang.String"/> <arg column="upn" javaType="java.lang.String"/> <arg column="actief" javaType="java.lang.Boolean"/> <arg column="email" javaType="java.lang.String"/> <arg column="locatie" javaType="amr.bi.elsalvador.enums.Locatie"/> <!-- 仅声明集合类型,后续由collection标签填充数据 --> <arg javaType="java.util.Collection" /> </constructor> <!-- 用collection标签映射集合,关联RoleExtra的结果映射 --> <collection property="rolesExtra" resultMap="roleExtraResult"/> </resultMap> <resultMap type="amr.bi.elsalvador.domain.suez.RoleExtra" id="roleExtraResult"> <constructor> <arg column="rol" javaType="amr.bi.elsalvador.enums.Role" /> </constructor> </resultMap>
关键说明
- 主键位置:
<id>标签必须放在<constructor>之前,MyBatis需要先通过主键识别哪些行属于同一个User实例,才能正确合并集合数据。 - 集合参数处理:构造器中仅声明集合的类型,不直接赋值,而是通过
<collection>标签将多行中的rol字段映射为RoleExtra实例并收集到rolesExtra集合中。 - 字段冲突处理(可选):如果查询字段存在命名冲突,可通过
columnPrefix属性区分关联表字段,当前场景字段名唯一可省略。
配置修正后,MyBatis会根据cprid合并相同用户的行,将多个RoleExtra实例收集到Collection中,再传入User的构造器,避免类型不匹配的异常。
内容的提问来源于stack exchange,提问作者Mark Smith
相关产品推荐
相关产品推荐

