You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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>

关键说明

  1. 主键位置:<id>标签必须放在<constructor>之前,MyBatis需要先通过主键识别哪些行属于同一个User实例,才能正确合并集合数据。
  2. 集合参数处理:构造器中仅声明集合的类型,不直接赋值,而是通过<collection>标签将多行中的rol字段映射为RoleExtra实例并收集到rolesExtra集合中。
  3. 字段冲突处理(可选):如果查询字段存在命名冲突,可通过columnPrefix属性区分关联表字段,当前场景字段名唯一可省略。

配置修正后,MyBatis会根据cprid合并相同用户的行,将多个RoleExtra实例收集到Collection中,再传入User的构造器,避免类型不匹配的异常。

内容的提问来源于stack exchange,提问作者Mark Smith

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 01:57:45