← 返回题目列表

MyBatis 中 resultType 和 resultMap 有什么区别?

高频 简单 第 2 / 24 题 更新于 2026/07/26
MyBatisresultTyperesultMap结果映射

简化版

resultType 指定每行直接映射成的 Java 类型,适合列名与属性名基本一致的简单结果;resultMap 显式描述列、属性、构造器、关联和集合关系,适合字段不一致或复杂对象图。同一查询通常在 resultType 与 resultMap 中选择一种,复杂映射优先使用可复用的 resultMap。

详细版

resultType 依赖自动映射,可通过 SQL 别名或 mapUnderscoreToCamelCase 处理常见命名差异。resultMap 可用 <id><result><association><collection><constructor><discriminator> 精确建模,还能组合继承已有映射。

JOIN 一对多结果会重复父表列,MyBatis 依靠 resultMap 中的 id 标识识别同一父对象并合并子集合。缺少正确 id 映射可能影响去重和性能;列名冲突则应使用别名和 columnPrefix 等方式消除歧义。

完整版教学

一、简单映射什么时候够用

<select id="findById" resultType="com.example.User">
  SELECT id, user_name AS userName, email
  FROM users WHERE id = #{id}
</select>

列别名与 Java 属性一致时,自动映射就能完成赋值。若全局开启下划线转驼峰,user_name 也可映射到 userName,但显式别名更容易看出 SQL 契约。

维度resultTyperesultMap
映射方式自动按列名/别名匹配属性显式声明列到属性
适用结果单表或平面 DTO复杂列名、构造器、关联、集合、多态
可复用性依赖每条 SQL 的列别名可作为映射蓝图复用
一对多组装不擅长通过 <collection><id> 识别对象
维护成本简单但隐式啰嗦但边界清楚

记忆钩子:resultType 像“自动填表”,列名对上就行;resultMap 像“施工图纸”,复杂对象图必须画清楚。

二、resultMap 的基本结构

<resultMap id="userMap" type="User">
  <id property="id" column="user_id"/>
  <result property="name" column="user_name"/>
</resultMap>

id 不只是普通字段声明,它帮助 MyBatis 在嵌套结果中识别对象身份,并可提升映射性能。主键或能唯一标识对象的列应准确标为 id。

三、association 与 collection

association 映射“一个”关联对象,collection 映射“多个”元素。两者既可使用嵌套 resultMap 消化 JOIN 结果,也可用 select 属性触发另一条查询。

嵌套查询写法直观,但遍历父列表时容易产生 N+1;JOIN 嵌套结果只需一次 SQL,却会产生重复行和分页困难。选择要基于数据量、查询形态和分页语义。

四、JOIN 结果如何组装

<resultMap id="orderMap" type="Order">
  <id property="id" column="order_id"/>
  <collection property="items" ofType="OrderItem">
    <id property="id" column="item_id"/>
    <result property="name" column="item_name"/>
  </collection>
</resultMap>

多行拥有相同 order_id 时,MyBatis 复用父 Order 并向 items 添加不同子对象。父子两层都应提供可靠 id;一对多 JOIN 直接做数据库分页可能截断子集合,常需先分页父 ID 再查询明细。

JOIN 结果 3 行:
order_id=10, item_id=1
order_id=10, item_id=2
order_id=11, item_id=3

正确 resultMap:
  生成 2 个 Order
  Order(10).items = [1, 2]
  Order(11).items = [3]

如果父级没有正确标 <id property="id" column="order_id"/>,MyBatis 难以判断哪些行属于同一个父对象,可能重复创建父对象或增加映射开销。这个 <id> 的意义不只是“映射主键字段”,还关系到嵌套结果的去重和合并。

五、自动映射的边界

自动映射方便,但列名冲突或 SQL 新增同名列时可能产生意外赋值。复杂 JOIN 应显式列出字段,避免 SELECT *,并按前缀区分不同表。

不可变对象可使用 constructor 映射;多态结果可使用 discriminator,但若映射过于复杂,拆成明确查询或 DTO 往往更易维护。

六、列冲突、前缀和 DTO 取舍

复杂 JOIN 最怕 SELECT *。比如 users 和 orders 都有 idcreated_at,自动映射时如果没有别名,列名冲突会让属性来源变得不清楚。更稳妥的写法是给列加前缀,如 u_ido_id,再在 resultMap 中明确 column,或者配合 columnPrefix 复用子 resultMap。

接口只需要展示字段时,不一定要还原完整领域对象图。比如订单列表只展示订单号、用户名、总金额,用一个平面 OrderListDTOresultType 或简单 resultMap 反而更清楚。只有需要复用复杂关联关系、构造嵌套对象、处理一对多集合时,才值得引入较完整的 resultMap。

七、常见误区与追问

  • 误区:resultType 比 resultMap 高级或更推荐。 两者没有高低,resultType 适合简单平面结果,resultMap 适合复杂显式映射。
  • 误区:开启驼峰映射后所有 JOIN 都不用 resultMap。 JOIN 常有列名冲突、一对多合并和别名歧义,仍需要显式映射。
  • 追问:<id> 标签为什么重要? 它帮助 MyBatis 识别对象身份,尤其在嵌套结果中用于父子对象去重和集合合并。
  • 追问:一对多 JOIN 分页为什么危险? 数据库 LIMIT 限制的是 JOIN 后的行数,不是父对象数,可能截断某个父对象的子集合。
  • 误区:SELECT * 配 resultMap 也没问题。 复杂查询应显式列出字段并加别名,避免同名列和表结构变化破坏映射。
  • 追问:什么时候用 DTO 而不是复杂 resultMap? 当接口只需要平面展示字段时,DTO 更简单稳定;完整对象图适合真正需要关联语义的业务。

八、加强记忆

resultType 依赖自动映射,适合平面简单结果;resultMap 是显式映射蓝图,能处理构造器、association、collection 和多态。JOIN 一对多时,父子 id 是对象去重组装的关键,分页还要防止子集合被截断。