Mybatis中的ResultMap和ResultType用法和对比
MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
前言
在Mybatis的 select
元素中有两个经常使用的属性 ResultType 和 ResultMap,用于在mapper.xml文件中配置结果集的数据类型。在日常开发中,应该如何正确的选择和使用 ResultType 和 ResultMap?本文将对这两个标签分别进行讲解和演示。
一、ResultMap是什么?
MyBatis 中的 ResultMap 是一个核心组件,属于MyBatis 返回操作结果的一个标签,用于将 SQL 中select标签查询出来的结果集映射到 Java 对象。当数据库表结构与 Java 实体类中的字段不完全匹配时(如列名与属性名不一致、存在复杂嵌套关系等),ResultMap 可以自定义映射规则,解决这种不匹配问题。
1、ResultMap特性
ResultMap 标签包括两个重要属性:
- id 属性:唯一标识, 用于表示这个ResultMap 的唯一性。在使用 select 标签 ResultMap 属性时,就是通过它引用的。
- type 属性:表示该 ResultMap 的结果集映射类型,可以为类的全限定名或者别名。这时候我们就可以定义一个 ResultMap ,来映射结果集中不一样的字段。
1.1 常用子元素
子元素 | 作用 |
---|---|
<id> | 指定主键映射,提高 MyBatis 性能优化能力。 |
<result> | 普通字段映射,用于简单类型(如 String, Integer)。包括两个属性,column:数据库表的列名,是数据库字段名或者用as定义的别名。property:实体类的属性,和列名一模一样,以进行一一映射。 |
<association> | 一对一或多对一关系,映射到嵌套对象(如 User 中的 Address)。(见3.2) |
<collection> | 一对多关系,映射到集合(如 User 中的 List)。(见3.3) |
<discriminator> | 用于实现结果集的条件映射,即多态映射,根据列值选择不同的子类型(高级用法)。 |
<discriminator>的用法比较灵活,详细介绍可以移步Mybatis之ResultMap中的discriminator标签介绍。
1.2 高级特性
- 延迟加载(Lazy Loading)
通过配置 fetchType=“lazy”,可以实现嵌套对象的延迟加载:
<association property="user" column="user_id" javaType="User"
resultMap="userResultMap" fetchType="lazy" />
- 自动映射(Auto Mapping)
当列名与属性名存在简单对应关系(如 user_name → userName)时,可使用 autoMapping=“true” 简化配置:
<resultMap id="userResultMap" type="User" autoMapping="true">
<!-- 仅需配置不一致的字段 -->
<id property="userId" column="user_id" />
</resultMap>
- 使用 片段复用映射逻辑
<sql id="userColumns">
user_id, user_name, create_time
</sql>
<select id="getUserById" resultMap="userResultMap">
SELECT <include refid="userColumns" /> FROM users WHERE user_id = #{id}
</select>
2、 ResultMap的核心作用
2.1、处理列名与属性名不一致
例如,数据库字段采用snake_case(例:user_name),而 Java 属性使用camelCase(例:userName)。
2.2、处理复杂嵌套关系
例如,将多表联查的结果映射到嵌套对象或集合(如 一个用户对应多个订单这种情况,User 对象包含 List)。
2.3、自定义类型转换
当需要把数据库中的类型转换为 Java 中的特定类型时。
2.4、优化查询性能
通过 association 和 collection 实现延迟加载(Lazy Loading)。
3、 ResultMap的基本用法
3.1、 简单映射(处理列名与属性名不一致)
创建数据库表:
CREATE TABLE users (
user_id INT PRIMARY KEY,
user_name VARCHAR(50),
create_time DATETIME
);
Java对象:
public class User {
private Integer userId; // 对应 user_id 列
private String userName; // 对应 user_name 列
private LocalDateTime createTime; // 对应 create_time 列
// getters/setters
}
resultMap配置:
<resultMap id="userResultMap" type="User">
<id property="userId" column="user_id" /> <!-- 主键映射 -->
<result property="userName" column="user_name" /> <!-- 普通字段映射 -->
<result property="createTime" column="create_time" />
</resultMap>
SQL映射:
<select id="getUserById" resultMap="userResultMap">
SELECT user_id, user_name, create_time
FROM users
WHERE user_id = #{id}
</select>
这里会根据resultMap的配置将SQL的查询结果user_id, user_name, create_time,一一映射到Java实体类的属性userId,userName,createTime。
注意:如果数据库字段和实体类属性一致的情况下,可以省略不写。
3.2、复杂映射(处理复杂嵌套关系)
例:1:数据库表:
CREATE TABLE orders (
order_id INT PRIMARY KEY,
user_id INT,
order_number VARCHAR(50),
FOREIGN KEY (user_id) REFERENCES users(user_id)
);
Java对象:
public class Order {
private Integer orderId;
private String orderNumber;
private User user; // 关联的用户对象
// getters/setters
}
resultMap配置:
<resultMap id="orderResultMap" type="Order">
<id property="orderId" column="order_id" />
<result property="orderNumber" column="order_number" />
<!-- 嵌套对象映射:使用 association -->
<association property="user" column="user_id" javaType="User" resultMap="userResultMap" />
</resultMap>
SQL映射:
<select id="getOrderWithUser" parameterType="int" resultMap="orderResultMap">
-- resultMap="orderResultMap":使用之前定义的 resultMap 处理嵌套映射
SELECT
o.order_id,
o.order_number,
o.user_id,
u.user_id AS "user.user_id", -- 使用AS "user.user_id"别名映射到嵌套对象,需确保别名中的user与resultMap中property="user"一致
u.user_name AS "user.user_name", -- 嵌套User对象的属性
u.create_time AS "user.create_time" -- 嵌套User对象的属性
-- 确保userResultMap中定义的column与查询中的别名一致
FROM orders o
LEFT JOIN users u ON o.user_id = u.user_id
WHERE o.order_id = #{orderId}
</select>
注意:对于复杂嵌套,可以考虑使用columnPrefix简化映射配置
例2:假设用户表和地址表存在一对一的关系,用户表有id、user_name、address_id字段,地址表有id、street、city等字段。可以通过以下方式配置嵌套映射:
<resultMap id="userWithAddressResultMap" type="User">
<id property="id" column="id" />
<result property="userName" column="user_name" />
<association property="address" javaType="Address">
<id property="id" column="address_id" />
<result property="street" column="street" />
<result property="city" column="city" />
</association>
</resultMap>
<select id="getUserWithAddress" parameterType="int" resultMap="userWithAddressResultMap">
SELECT u.id, u.user_name, a.id as address_id, a.street, a.city
FROM users u
LEFT JOIN addresses a ON u.address_id = a.id
WHERE u.id = #{id}
</select>
3.3、集合映射(一对多关系)
Java对象:
public class User {
private Integer userId;
private String userName;
private List<Order> orders; // 用户的订单列表
// getters/setters
}
resultMap配置:
<resultMap id="userWithOrdersResultMap" type="User">
<id property="userId" column="user_id" />
<result property="userName" column="user_name" />
<!-- 集合映射:使用 collection -->
<collection property="orders" ofType="Order" resultMap="orderResultMap" />
</resultMap>
查询需要获取用户及其关联的所有订单数据对应的SQL查询映射:
<select id="getUserWithOrders" parameterType="int" resultMap="userWithOrdersResultMap">
SELECT
u.user_id,
u.user_name,
o.order_id AS "orders.order_id",
o.order_number AS "orders.order_number",
o.user_id AS "orders.user_id" -- 用于嵌套Order中的user对象关联(通过orderResultMap)
FROM users u
LEFT JOIN orders o ON u.user_id = o.user_id
WHERE u.user_id = #{userId}
</select>
注意:MyBatis 会自动将相同user_id的记录合并,并将不同的order_id记录映射到orders列表中。
例2,假设一个用户可以有多个订单,订单表有id、order_number、user_id等字段。可以通过以下方式配置集合映射:
<resultMap id="userWithOrdersResultMap" type="User">
<id property="id" column="id" />
<result property="userName" column="user_name" />
<collection property="orders" ofType="Order">
<id property="id" column="order_id" />
<result property="orderNumber" column="order_number" />
</collection>
</resultMap>
对应的SQL查询语句,
<select id="getUserWithOrders" parameterType="int" resultMap="userWithOrdersResultMap">
SELECT u.id, u.user_name, o.id as order_id, o.order_number
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
WHERE u.id = #{id}
</select>
二、ResultType是什么?
MyBatis 的resultType,顾名思义,返回结果,是一种简化的结果映射方式,用于将 SQL 查询结果直接映射到 Java 对象,可以理解为期望从这条语句中返回结果的类全限定名或别名(如:java.lang.String对应的别名是“string”)。与resultMap相比,它不需要显式定义字段与属性的映射关系,适用于简单场景。下面从基本概念、使用场景、与resultMap的对比等方面进行详细介绍。
1.基本概念
resultType是 MyBatis 中用于指定查询结果类型的属性,它可以是 Java 基本类型、POJO 类、Map 等。MyBatis 会根据查询结果列名与 Java 对象属性名的匹配关系(默认不区分大小写)自动进行映射。
2.ResultType的核心作用
- 简单查询:当查询结果与 Java 对象属性名完全一致或符合驼峰命名规则时。
- 动态结果:返回 Map 或 List
- 聚合函数:返回单个值(如 COUNT、SUM 等)。
3.简单示例
3.1 返回 POJO 对象
假设存在用户表,表中有id、user_name、email等字段,对应的 Java 类为User,包含id、userName、email属性。可以使用以下配置:
<select id="getUserById" parameterType="int" resultType="User">
SELECT id, user_name, email
FROM users
WHERE id = #{id}
</select>
MyBatis 会自动将user_name映射到userName(基于驼峰命名规则)。
3.2 返回基本类型
<select id="getUserCount" resultType="int">
SELECT COUNT(*) FROM users
</select>
3.3 返回 Map
<select id="getUserAsMap" parameterType="int" resultType="map">
SELECT id, user_name, email
FROM users
WHERE id = #{id}
</select>
返回结果为:
{id=1, user_name=John, email=john@example.com}
3.4 返回 List
<select id="getAllUsers" resultType="map">
SELECT id, user_name, email
FROM users
</select>
返回结果为:
[
{id=1, user_name=John, email=john@example.com},
{id=2, user_name=Jane, email=jane@example.com}
]
注意
,如果返回的是集合,那应该设置为集合包含的类型,而不是集合本身的类型。即,这里返回的是Map的集合List<Map>,resultType属性设置的为集合包含的类型即Map。
三、ResultMap与 ResultType 的对比
特性 | ResultMap | ResultType |
---|---|---|
映射方式 | 自定义映射规则 | 自动映射(基于列名与属性名的匹配) |
适用场景 | 复杂查询,存在列名与属性名不一致或嵌套关系 | 简单查询,列名与属性名完全一致 |
灵活性 | 高(完全自定义) | 低(依赖命名规则) |
四、总结
ResultType
是 MyBatis 提供的一种简化结果映射方式,适用于简单场景,可以减少配置工作量。而 ResultMap
则适用于复杂的映射需求,提供了更强大的功能。ResultType
不需要配置,但是ResultMap
要配置。ResultType
是直接指定返回类型的,而使用ResultMap
时,需要在外部ResultMap
标签中,设置数据库表的字段名和实体bean对象类属性的一一对应关系。设置后,就算数据库的字段名和实体类的属性名不一样也没有关系,Mybatis依然会给映射出来,所以ResultMap
要更强大一些。在实际开发中,应根据具体场景选择合适的映射方式。需要注意的是,在select
元素中ResultType和 ResultMap之间只能同时使用一个。