Java / MyBatis resultMap 与 sql

MyBatis resultMap 与 sql

1. 前言

本小节,我们将一起学习 MyBatis resultMap 和 sql。

在前面的小节中,我们了解到 MyBatis 可以自动帮助我们映射数据库数据和 Java 对象,其实这是 MyBatis 在幕后帮我们创建了 resultMap 对象;虽然 MyBatis 可以自动帮助我们做数据映射,但是对于复杂的对象,我们就必须自定义 resultMap 了。

而在书写 SQL 时,势必会有一些 SQL 代码段出现了重复,为了更好的复用它们,MyBatis 提供了 sql 标签。

2. 定义

慕课解释:resultMap 标签用于将数据库数据映射为 Java 对象;sql 标签则用来定义可重用的 SQL 代码段。

3. 实例

4.1 resultMap 实例

4.1.1 xml 实例

在下面这段 select 标签中,SQL 语句返回的是一个复杂对象,即 resultType 上指定的 User。

<select id="selectUserByAgeAndScore" parameterType="com.imooc.mybatis.model.User"
        resultType="com.imooc.mybatis.model.User">
  SELECT * FROM imooc_user WHERE age = #{age} AND score = #{score}
</select>

在这种情况下,MyBatis 会自动创建 resultMap 对象进行数据的映射,接下来我们直接定义出 resultMap,避免 MyBatis 推断和映射带来的性能损耗。如下:

<resultMap id="userMap" type="com.imooc.mybatis.model.User">
  <id property="id" column="id"/>
  <result property="username" column="username"/>
  <result property="age" column="age"/>
  <result property="score" column="score"/>
</resultMap>

<select id="selectUserByAgeAndScore" parameterType="com.imooc.mybatis.model.User"
        resultMap="userMap">
  SELECT * FROM imooc_user WHERE age = #{age} AND score = #{score}
</select>

我们定义了名为 userMap 的 resultMap 且指定其对应的 Java 类型为 User,在标签的内部,我们还需指定字段之间的映射,除 id 这个特殊的字段外,其它字段均使用 result 标签来映射。

其中 property 是 Java 对象中的字段名称,column 是数据表与之对应的字段名称。

resultMap 定义完毕后,我们在 select 标签中通过 resultMap 属性来设置对应的 id。

TIPS: 注意, resultMap 和 resultType 不能共存,只能二选一。

这样,一次简单的 resultMap 使用就完毕了。

4.1.2 注解实例

通过注解,我们也可以指定 Java 模型对象与数据库字段之间的映射关系,如下:

@Results({
  @Result(property = "id", column = "id", id = true),
  @Result(property = "username", column = "username"),
  @Result(property = "age", column = "age"),
  @Result(property = "score", column = "score")
})
@Select("SELECT * FROM imooc_user WHERE id = #{id}")
User selectUserById(Integer id);

Results 注解与 resultMap 对象,包含多个 Result 注解,每个 Result 注解对应了一个字段映射关系。

提示: Results 注解以及 ResultMap 注解虽然存在,但是很少在实际的开发中使用,只需了解即可。

4.2 SQL 实例

我们将目光放到 selectUserByAgeAndScore 语句的内部,在实际的开发中像SELECT * FROM imooc_user这样的代码段其实非常常见,会在多个 select 标签中用到它。我们可以将其定义为一个 sql 标签,这样所有的 select 标签都可以快速复用到这段代码。

<sql id="selectUser">
  SELECT * FROM imooc_user
</sql>

同样的,我们必须为这个代码段定义一个唯一的 id,定义好后,我们就可以在其它标签中使用了:

<select id="selectUserByAgeAndScore" parameterType="com.imooc.mybatis.model.User"
        resultMap="userMap">
  <include refid="selectUser"/>
  WHERE age = #{age} AND score = #{score}
</select>

这里,我们必须使用一个 include 标签来将 SQL 标签包含进来,并且 refid 属性必须是该 SQL 标签的 id 值。这样这段代码仍然可以正常工作。

SQL 标签没有对应注解,只能在 xml 中使用。

5. 实践

接下来,我们一起来实操巩固一下。

5.1 例1. 查询用户姓名和年龄

请使用 MyBatis 完成在 imooc_user 表中查询用户简略信息的功能。

分析:

很多应用都会有查询用户简略信息这样一个需求,比如我们只需获取用户名和年龄,运用本小节的知识我们可以这样实现它。

先在对应 UserMapper.xml 文件中添加查询用户简略信息的 select 标签,然后在 UserMapper.java 中增加上对应的方法即可。

步骤:

首先,我们新建一个用户简略信息的模型 UserShortCut.java:

package com.imooc.mybatis.model;

public class UserShortCut {
  private String username;
  private Integer age;
  // 省略了重要的 getter 和 setter 方法
}

并在 UserMapper.xml 添加上 resultMap 、sql 和 select 标签:

<!--  复用的 sql 代码段   -->
<sql id="selectUserShortcutMap">
  SELECT username,age FROM imooc_user
</sql>
<!--  结果映射集   -->
<resultMap id="userShortcutMap" type="com.imooc.mybatis.model.UserShortCut">
  <result property="username" column="username"/>
  <result property="age" column="age"/>
</resultMap>
<!--  查询语句   -->
<select id="selectUserShortcutById" resultMap="userShortcutMap">
  <include refid="selectUserShortcutMap"/>
  WHERE id = #{id}
</select>

接下来,给 UserMapper.java 接口添加上对应的方法,以便在程序中调用。

package com.imooc.mybatis.mapper;

import com.imooc.mybatis.model.UserShortCut;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface UserMapper {
  UserShortCut selectUserShortcutById(Integer id);
}

结果:

通过如下代码,我们运行 selectUserShortcutById 方法。

UserMapper userMapper = session.getMapper(UserMapper.class);
UserShortCut shortCut = userMapper.selectUserShortcutById(1);
System.out.println(shortCut);

结果如下:

UserShortCut{username='peter', age=18}

6. 小结

  • SQL 是一个非常好用的标签,能够减少大量重复的 SQL 代码书写,但是也降低 SQL 的可读性。
  • MyBatis 会自动生成 resultMap,这会为我们节省了大量的时间,如果不是对性能十分严苛,那么 resultType 是够用的。