Mybatis 之 XML 映射器详解

240 阅读4分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第3天,点击查看活动详情

前言

在上文:Mybatis 入门篇(含实战)中,我们入门了 Mybatis,并且用 Spring 项目实现了一个接口到 MySQL 的增删查改,今天我们来聊聊 Mybatis 的 XML 映射器

XML 映射器是啥

以下片段截自官网

MyBatis 的真正强大在于它的语句映射,这是它的魔力所在。由于它的异常强大,映射器的 XML 文件就显得相对简单。如果拿它跟具有相同功能的 JDBC 代码进行对比,你会立即发现省掉了将近 95% 的代码。MyBatis 致力于减少使用成本,让用户能更专注于 SQL 代码。

对于 Mybatis 来说,所有定制的 SQL 都维护在 XML 映射文件当中(也可以在 Mapper 层使用注解,但是功能性没有映射文件强),包括动态语句也是基于各种标签来灵活构建。

Dao/Mapper 接口与 XML 文件的关系

在最佳实践中,通常一个 XML 映射文件都会有一个 Dao/Mapper 与之相对应

学习 XML 映射文件,首先要明确几个概念

  • Dao 接口,也有人称为 Mapper 接口,接口的全限定名,就是 XML 文件中 namespace 的值

  • 每一个<select><insert><update><delete> 标签,都会被解析为一个 MappedStatement 对象

  • 接口的方法名,就是映射文件中 MappedStatement 的 Id 值

  • Mapper 接口是没有实现类的,当调用接口方法时,接口全限名+方法名拼接字符串作为 key 值,可唯一定位一个 MappedStatement

  • 拿 5.3 的例子说,cn.wzed.mybatisdemo.dao.StudentDao 类下面的方法 queryById,对应了 StudentDao.xml 文件中id = queryByIdMappedStatement

一级标签

官方详细文档: mybatis – MyBatis 3 | XML 映射器

Mybatis 的一级标签并不多,掌握常用的几个即可

  • insert – 映射插入语句
  • update – 映射更新语句
  • delete – 映射删除语句
  • select – 映射查询语句
  • resultMap - 描述如何从数据库结果集中加载对象,是最复杂也是最强大的元素
  • sql - 可被其它语句引用的可重用语句块

增删查改标签就不详细描述了,这里主要讲一下 resultMapsql 标签

依旧拿 5.3 来举例

<resultMap type="cn.wzed.mybatisdemo.entity.Student" id="StudentMap">
    <result property="id" column="id" jdbcType="INTEGER"/>
    <result property="name" column="name" jdbcType="VARCHAR"/>
    <result property="age" column="age" jdbcType="INTEGER"/>
</resultMap>

动态 SQL

动态 SQL 是 MyBatis 的强大特性之一,如果你使用过 JPA 来自定义动态语句,你就会发现是有多么的痛苦,特别是写完后字符串拼接处忘记加空格

Mybatis 借助标签实现了动态语句,下面介绍一下用来构建动态语句的常用标签

if where

if 经常与 where 搭配来构建简单的动态查询语句

还是以查询学生表为例,这是一个物理分页加使用条件动态查询的例子

<!--动态分页查询-->
<select id="queryAll" resultMap="StudentMap">
    select
    id, name, age
    from student
    <where>
        <if test="student.id != null">
            and id = #{student.id}
        </if>
        <if test="student.name != null and student.name != ''">
            and name = #{student.name}
        </if>
        <if test="student.age != null">
            and age = #{student.age}
        </if>
    </where>
    limit #{pageNo}, #{pageSize}
</select>

在这个例子中,什么条件都不传会生成以下语句

select
id, name, age
from student
limit #{pageNo}, #{pageSize}

而当传入的 Student 对象中任意字段不为空时,会通过 if 标签的判断,从而加上 where 条件;假设传入的 id 不为空,会生成如下语句

select
id, name, age
from student
where id = #{student.id}

这里需要注意的是,Mybatis 会自动帮你祛除 where 后面的无用 and,用过 JPA 写自定义动态语句的人都表示泪目了!

choose、when、otherwise

有时候,我们不想使用所有的条件,而只是想从多个条件中选择一个使用。针对这种情况,MyBatis 提供了 choose 元素,它有点像 Java 中的 switch 语句

比如在 6.3.1 的例子中,我们想实现这样的效果:传入了 id,就根据 id 去查找,传入了 name 就根据 name 去查找,如果都不传,就默认查找年龄等于 18 的学生

<select id="queryByChooseTag" resultMap="StudentMap">
    select
    id, name, age
    from student
    <where>
        <choose>
            <when test="student.id != null">
                and id = #{student.id}
            </when>
            <when test="student.name != null and student.name != ''">
                and name = #{student.name}
            </when>
            <otherwise>
                and age = 18
            </otherwise>
        </choose>
    </where>
    limit #{pageNo}, #{pageSize}
</select>

在这个例子中,假设传入了 id,会加上 where id = #{student.id} 条件,根据优先级,加上其他条件也是没用的;而当 id 为空,name 不为空时,会加上条件 where name = #{student.name};当两个字段都为空时,查询语句的条件是 where age = 18

其他标签……

  • set - 可以用于动态包含需要更新的列,忽略其它不更新的列
  • foreach - 传入一个集合,通过foreach 可以构建 in 条件的语句
  • ……

鉴于常用的标签不多,用法也是类似的,在这里就不介绍这么多啦,上手就完事了