# MyBatis教程 - 6 查询结果

下面介绍一下 MyBatis 中查询结果的处理。

查询结果可能是一条记录、多条记录、数据库中的字段和 Java 实体类中的属性值不对应等情况,下面进行介绍。

# 6.1 查询结果为单条记录

如果查询结果为单条记录,那么可以直接使用实体类对象进行接收。前面也多次用到,再介绍一下。


在通过用户名查询用户的时候,可以编写接口如下:

UserMapper.java

/**
 * 根据用户名查询用户
 */
User selectByUsername(@Param("username") String username);
1
2
3
4

在 UserMapper.xml 中通过 resultType 映射返回结果的类型(这里使用了别名,前面讲过),如下:

<select id="selectByUsername" resultType="User">
    SELECT * FROM tb_user
    WHERE username = #{username}
</select>
1
2
3
4

# 6.2 查询结果为多条记录

当查询返回多条记录时,MyBatis 会将每一条记录映射为一个 Java 对象,并将这些对象放入一个 List 中。


举个栗子:

查询所有的用户,可以编写接口如下:

UserMapper.java

/**
 * 查询所有用户
 */
List<User> selectAll();
1
2
3
4

接口需要使用 List 来接收。当然返回的单条记录,接口返回类型也可以定义为 List。

在 UserMapper.xml 中和查询单条是一样的,也是通过 resultType 映射返回结果的类型,如下:

<select id="selectAll" resultType="User">
    SELECT * FROM tb_user
</select>
1
2
3

# 6.3 查询单列数据

当使用聚合函数,例如使用 count 函数来查询数据库中记录数的时候,返回的是一列数据,那么此时如何接收数据呢?

举个栗子:

查询用户个数,可以编写接口如下:

UserMapper.java

/**
 * 查询所有用户数量
 */
Integer selectAllCount();
1
2
3
4

返回的数据类型是整形的,是条目的数量。

在 UserMapper.xml 中,编写 SQL ,使用 count 查询记录数:

<select id="selectAllCount" resultType="java.lang.Integer">
    SELECT COUNT(*) FROM tb_user
</select>
1
2
3

返回的结果类型为 java.lang.Integer ,MyBatis 是内置了类型别名,所以这里的 java.lang.Integer 可以写为:IntegerintegerintInt 都可以。


再举个栗子:

下面查询所有的用户名,单列,但是是一个 username 的 List<String>

编写接口如下:

UserMapper.java

/**
 * 查询所有用户的用户名
 */
List<String> selectAllUsername();
1
2
3
4

返回的是一个 username 的集合。

在 UserMapper.xml 中,查询所有的用户名:

<select id="selectAllUsername" resultType="String">
    SELECT username FROM tb_user
</select>
1
2
3

# 6.4 返回Map类型

我们可以将查询的结果解析到一个 Map 中,但是注意,这种情况,只能处理返回结果是单条记录的情况。


举个栗子:

根据 ID 查询用户,编写接口如下。

UserMapper.java

/**
 * 根据ID查询用户信息,并返回Map类型
 */
Map<String, Object> selectByIdToMap(@Param("id") Integer id);
1
2
3
4

返回的是 Map 类型。

在 UserMapper.xml 中,设置 resultType="Map" ,SQL 没有区别:

<select id="selectByIdToMap" resultType="Map">
    SELECT * FROM tb_user
    WHERE id = #{id}
</select>
1
2
3
4

查询到的数据格式是:

{
  password=123456, 
  update_time=2024-08-17T08:03:52, 
  create_time=2024-08-17T08:03:52, 
  id=1, 
  email=doubi@foooor.com, 
  age=30, 
  username=doubi
}
1
2
3
4
5
6
7
8
9

key 为数据库中的字段,value 为对应的值。

如果查询出的结果,没有或不想使用实体类对应,可以使用 Map 来接收。

# 6.5 使用Map接收多条记录

上面使用 Map 接收返回的结果,只能接收单条查询记录,如果使用 Map 接收多条记录,需要返回一个 Map 的 List 集合。


举个栗子:

查询所有的用户信息,使用 List<Map> 接收。

UserMapper.java

/**
 * 查询所有用户的信息,并返回Map类型
 */
List<Map<String, Object>> selectAllToMap();
1
2
3
4

在 UserMapper.xml 中,还是设置 resultType="Map" ,没有区别:

<select id="selectAllToMap" resultType="Map">
    SELECT * FROM tb_user
</select>
1
2
3

这样每条记录对应 List 中的一个 Map 对象中的数据。


还有一种使用 Map 接收多条记录的方式,在接口上添加 @MapKey 注解:

/**
 * 查询所有用户的信息,并返回Map类型
 */
@MapKey("id")
Map<String, Map<String, Object>> selectAllToMap();
1
2
3
4
5

上面的配置的意思,返回结果是一个 Map,Map 的 key 是每条记录的 id 字段的值,因为使用 @MapKey("id") 指定了。

value 是一个 Map,这个 Map 中是每条记录在数据库中对应的字段和属性值。

所以结果的格式如下:

{
  1={
    password=123456, 
    update_time=2024-08-17T08:03:52, 
    create_time=2024-08-17T08:03:52, 
    id=1, 
    email=doubi@foooor.com, 
    age=30, 
    username=doubi
  }, 
  2={
    password=1234qwer, 
    update_time=2024-08-17T08:03:52, 
    create_time=2024-08-17T08:03:52, 
    id=2, 
    email=niubi@foooor.com, 
    age=28, 
    username=niubi}, 
  3={
    password=88888888, 
    update_time=2024-08-17T08:03:52, 
    create_time=2024-08-17T08:03:52, 
    id=3, 
    email=erbi@foooor.com, 
    age=35, 
    username=erbi
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

UserMapper.xml 中的配置和上面 List<Map> 接收的方式一样,没有变化。

# 6.6 字段与属性匹配

数据库中的字段一般使用的都是小写字母,多个单词使用下划线 _ 来分割。而在 Java 中的属性一般都是使用小写字母开头的驼峰规则。

这样数据库的字段和 Java 实体类中的属性名称不完全对应,这样就会存在数据库查询的结果无法赋值给 Java 对象,导致对象属性值为 null。

针对这种情况有三种处理方式,下面介绍一下。

# 1 全局配置

在 HelloWorld 章节已经讲过了,通过在 mybatis-config.xml 中配置如下属性,可以让数据库中的字段使用驼峰命名法与 Java 实体类中的属性进行映射。

<!-- 设置 -->
<settings>
    <!-- 驼峰命名法 -->
    <setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
1
2
3
4
5

这样数据库的 user_name 可以对应 Java 实体类中的 userName。这个配置默认为 false,所以需要显式配置为 true

# 2 SQL别名

但是有时候,数据库的字段与 Java 实体类中的字段名称没有那么对应,例如数据库字段叫 email ,Java 属性中叫 mailBox (只是举个例子),这样即使配置了驼峰命名法也无法解决。

那么我们可以修改 SQL,使用字段别名来解决。

举个栗子:

<select id="selectById" resultType="User">
    SELECT id, username, password, email as mailBox FROM tb_user
    WHERE id = #{id}
</select>
1
2
3
4

在 SQL 中,通过给 email 设置别名 email as mailBoxas 可以省略),和 Java 中的 mailBox 属性对应即可。

# 3 ResultMap

使用 ResultMap 可以将数据库的字段和 Java 实体类中的属性进行一一映射。

举个栗子:

首先需要在 XxxMapper.xml 中使用 <resultMap> 标签定义 ResultMap,然后在查询的时候,指定 resultMap

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.foooor.mybatis.mapper.UserMapper">

    <!--
        1. id:指定resultMap的唯一标识符
        2. type:指定对应的实体类型
        2. <id> 表示的是主键
        3. <result> 表示的是普通字段,property 表示的是实体类的属性;column 表示的是数据库的字段
    -->
    <!-- 定义resultMap结果集映射 -->
    <resultMap id="UserResultMap" type="User">
        <id property="id" column="id"/>
        <result property="username" column="username"/>
        <result property="password" column="password"/>
        <result property="mailBox" column="email"/>
        <result property="age" column="age"/>
        <result property="createTime" column="create_time"/>
        <result property="updateTime" column="update_time"/>
    </resultMap>

  	<!-- 查询,使用resultMap -->
    <select id="selectById" resultMap="UserResultMap">
        SELECT * FROM tb_user
        WHERE id = #{id}
    </select>

    <!-- ... 其他SQL映射 -->

</mapper>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

首先定义 ResultMap,然后在查询的时候通过 resultMap 属性来指定。

注意:在 ResultMap 中,如果字段名称匹配,可以不写;所以上面 usernamepassword 等选项可以省略的,只写 mailBox。因为指定了类为 User,会将名称匹配的字段和属性自动进行映射。


ResultMap 更多的使用方式是用来配置一对多、多对一的映射。后面再介绍。