Mybatis作为国内使用最广泛的持久层框架之一,其核心魅力就在于把SQL语句从Java代码中抽离出来,放到XML映射文件里统一管理。这样做的好处很直接:SQL改动不需要重新编译Java代码,SQL的可读性和维护性也大幅提升。不过不少刚上手的朋友在写第一个映射文件时就会卡住,不知道namespace该填什么、parameterType和resultType有什么区别、动态SQL的标签怎么嵌套。这篇文章就把XML映射文件的写法从头到尾捋一遍,配合完整代码示例,看完就能动手写。

XML映射文件的基本结构
一个标准的Mybatis映射文件以<mapper>标签作为根节点, namespace属性是整个文件的灵魂,它通常绑定一个Dao接口的全限定名。Mybatis在运行时会根据这个namespace加上SQL的id,定位到具体要执行的SQL语句。假设你有一个接口com.demo.mapper.UserMapper,那么映射文件就要写成下面这样:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd"&>
<mapper namespace="com.demo.mapper.UserMapper">
<!-- SQL语句都写在这里 -->
</mapper></code>需要注意的是,文件开头的DTD声明不能省,否则Mybatis解析标签时会报错。另外,映射文件的位置要与Mybatis全局配置文件中<mappers>节点的注册路径一致。如果项目把映射文件放在src\main\resources\mapper目录下,配置时就要写对应的资源路径,Windows开发环境下路径分隔符用反斜杠表示,例如mapper\UserMapper.xml。
namespace绑定接口之后,接口中每个方法对应映射文件里一条SQL语句的id。方法名和id必须完全一致,参数类型和返回类型也要匹配,这是初学者最容易出错的地方。方法名写成了getUserById而XML里id写成了getUserByID,启动时不会报错,调用时才会抛出绑定异常。
增删改查四种SQL语句的写法
查询语句用<select>标签,最关键的两个属性是parameterType和resultType。前者指定传入参数的类型,后者指定单条结果映射的类型。查询单个用户和查询用户列表的写法如下:
<select id="getUserById" parameterType="int" resultType="com.demo.entity.User">
SELECT id, username, email, age
FROM t_user
WHERE id = #{id}
</select>
<select id="listUsers" resultType="com.demo.entity.User">
SELECT id, username, email, age
FROM t_user
ORDER BY id DESC
</select>这里的#{id}是预编译参数占位符,底层会转化为JDBC的PreparedStatement参数,能有效防止SQL注入。取参数时还有另一种写法${id},它是直接做字符串拼接的,一般只用于动态表名、排序字段这类不能预编译的场景,切记不要用它接收用户输入的查询条件。
插入语句用<insert>标签。如果数据库主键是自增的,可以配置useGeneratedKeys和keyProperty,插入完成后Mybatis会把生成的主键回填到实体对象中:
<insert id="addUser" parameterType="com.demo.entity.User"
useGeneratedKeys="true" keyProperty="id">
INSERT INTO t_user (username, email, age)
VALUES (#{username}, #{email}, #{age})
</insert>更新和删除分别用<update>和<delete>标签,写法与查询类似,只是不需要resultType。它们的返回值是受影响的行数,接口方法直接声明为int即可。删除操作建议养成用<where>条件限定主键的习惯,避免因为条件拼接失误导致全表更新或删除。
resultMap结果映射与动态SQL
当数据库字段名和实体类属性名不一致时,单靠resultType就不够了,这时候需要<resultMap>显式定义映射关系。比如数据库字段是user_name而实体属性是username:
<resultMap id="userResultMap" type="com.demo.entity.User">
<id property="id" column="id"/>
<result property="username" column="user_name"/>
<result property="email" column="email"/>
<result property="age" column="age"/>
</resultMap>
<select id="getUserList" resultMap="userResultMap">
SELECT id, user_name, email, age FROM t_user
</select><id>标签标记主键字段有助于提升性能和唯一性判断,一对多、多对一关联映射也建立在resultMap之上,通过<association>和<collection>标签可以完成复杂对象关系的装配。
动态SQL是XML映射文件最实用的能力。常用的标签有<if>、<where>、<foreach>、<choose>等。以多条件查询为例,条件个数不确定时,<if>配合<where>标签可以自动处理掉多余的AND:
<select id="searchUsers" parameterType="com.demo.dto.UserQuery"
resultType="com.demo.entity.User">
SELECT id, username, email, age FROM t_user
<where>
<if test="username != null and username != ''">
AND username LIKE CONCAT('%', #{username}, '%')
</if>
<if test="minAge != null">
AND age >= #{minAge}
</if>
<if test="email != null and email != ''">
AND email = #{email}
</if>
</where>
</select><foreach>标签专门用于遍历集合参数,批量删除的典型写法是用它拼接IN条件。test属性中的判断直接写OGNL表达式,字符串判空用!= null and != ''双条件,只判null会漏掉空字符串的情况。把上面这些内容组合起来,就足以应付日常开发中绝大多数映射文件的编写需求了,遇到复杂SQL时再逐步深入关联映射和SQL片段复用即可。