Skip to content

Mapper XML 映射文件

  Mapper XML 是 MyBatis 的核心所在,承载了 SQL 语句、参数映射、结果映射等所有数据库操作的定义。

一、Mapper XML 基本结构

xml
<?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">

<!-- namespace = 接口全限定名 -->
<mapper namespace="com.example.mapper.UserMapper">

    <!-- 可复用的 SQL 片段 -->
    <sql id="baseColumns">id, name, age, email, create_time</sql>

    <!-- 结果映射 -->
    <resultMap id="userMap" type="User">
        <id property="id" column="id"/>
        <result property="name" column="name"/>
        <result property="age" column="age"/>
    </resultMap>

    <!-- CRUD 操作 -->
    <select id="selectById" resultMap="userMap">
        SELECT <include refid="baseColumns"/>
        FROM user WHERE id = #{id}
    </select>

</mapper>

二、CRUD 操作详解

2.1 select(查询)

xml
<!-- ========== 基础查询 ========== -->
<!-- 单条查询 -->
<select id="selectById" resultType="User">
    SELECT * FROM user WHERE id = #{id}
</select>

<!-- 列表查询 -->
<select id="selectAll" resultType="User">
    SELECT * FROM user
</select>

<!-- 统计查询 -->
<select id="count" resultType="int">
    SELECT COUNT(*) FROM user
</select>

<!-- 模糊查询 -->
<select id="selectByName" resultType="User">
    SELECT * FROM user WHERE name LIKE CONCAT('%', #{name}, '%')
</select>

<!-- 多参数查询 -->
<select id="selectByAgeRange" resultType="User">
    SELECT * FROM user
    WHERE age BETWEEN #{minAge} AND #{maxAge}
</select>

2.2 insert(新增)

xml
<!-- 普通新增 -->
<insert id="insert">
    INSERT INTO user (name, age, email)
    VALUES (#{name}, #{age}, #{email})
</insert>

<!-- 新增并返回自增主键 -->
<insert id="insertAndGetId" useGeneratedKeys="true" keyProperty="id">
    INSERT INTO user (name, age, email)
    VALUES (#{name}, #{age}, #{email})
</insert>

<!-- 支持子查询插入 -->
<insert id="insertBatch">
    INSERT INTO user (name, age, email)
    SELECT name, age, email FROM temp_user WHERE status = 1
</insert>

2.3 update(修改)

xml
<!-- 根据 ID 修改 -->
<update id="update">
    UPDATE user
    SET name = #{name}, age = #{age}, email = #{email}
    WHERE id = #{id}
</update>

<!-- 动态修改(只更新不为 null 的字段) -->
<update id="updateSelective">
    UPDATE user
    <set>
        <if test="name != null">name = #{name},</if>
        <if test="age != null">age = #{age},</if>
        <if test="email != null">email = #{email},</if>
    </set>
    WHERE id = #{id}
</update>

2.4 delete(删除)

xml
<!-- 根据 ID 删除 -->
<delete id="deleteById">
    DELETE FROM user WHERE id = #{id}
</delete>

<!-- 批量删除 -->
<delete id="deleteByIds">
    DELETE FROM user WHERE id IN
    <foreach collection="list" item="id" open="(" separator="," close=")">
        #{id}
    </foreach>
</delete>

三、参数传递

3.1 单个参数

java
// 接口
User selectById(Long id);
xml
<!-- 单个参数:直接写 #{任意名} -->
<select id="selectById" resultType="User">
    SELECT * FROM user WHERE id = #{id}
</select>

3.2 多个参数

java
// 接口
List<User> selectByAgeRange(@Param("minAge") int minAge,
                             @Param("maxAge") int maxAge);
xml
<!-- 多个参数:必须用 @Param 注解指定名字 -->
<select id="selectByAgeRange" resultType="User">
    SELECT * FROM user
    WHERE age BETWEEN #{minAge} AND #{maxAge}
</select>
如果不加 @Param 注解,默认名字是:
  第一个参数 → param1
  第二个参数 → param2
  ...
  或者使用 arg0, arg1, arg2...(MyBatis 3.4.1+)

3.3 对象参数

java
// 接口
List<User> selectByCondition(UserQuery query);
xml
<!-- 对象参数:直接写属性名 -->
<select id="selectByCondition" resultType="User">
    SELECT * FROM user
    WHERE name = #{name}
    AND age &gt; #{age}
</select>

3.4 Map 参数

java
// 接口
List<User> selectByMap(Map<String, Object> params);
xml
<!-- Map 参数:直接写 key -->
<select id="selectByMap" resultType="User">
    SELECT * FROM user
    WHERE name = #{name}
    AND age &gt; #{minAge}
</select>

3.5 List/数组参数

java
// 接口
List<User> selectByIds(@Param("ids") List<Long> ids);
xml
<!-- List 参数:用 foreach 遍历 -->
<select id="selectByIds" resultType="User">
    SELECT * FROM user WHERE id IN
    <foreach collection="ids" item="id" open="(" separator="," close=")">
        #{id}
    </foreach>
</select>

四、#{ } 与 ${ } 的区别

xml
<!-- #{ } 预编译占位符(推荐) -->
<select id="selectById" resultType="User">
    SELECT * FROM user WHERE id = #{id}
</select>
<!-- 实际执行:SELECT * FROM user WHERE id = ? -->

<!-- ${ } 字符串替换(有风险) -->
<select id="selectByTable" resultType="User">
    SELECT * FROM ${tableName} WHERE id = #{id}
</select>
<!-- 实际执行:SELECT * FROM user WHERE id = ? -->
对比#$
处理方式预编译参数占位符 ?字符串直接拼接
SQL 注入安全有风险
类型处理自动加引号原样拼接
使用场景普通参数值动态表名/列名/排序
使用 ${ } 的典型场景:
  ① 动态表名:SELECT * FROM ${tableName}
  ② 动态列名:ORDER BY ${columnName}
  ③ 动态排序:ORDER BY ${column} ${direction}
  ④ 动态数据库名:USE ${dbName}

注意:${ } 必须严格校验,防止 SQL 注入!

五、SQL 片段(复用)

xml
<!-- 定义 SQL 片段 -->
<sql id="baseColumns">
    id, name, age, email, create_time
</sql>

<sql id="whereCondition">
    <where>
        <if test="name != null and name != ''">
            AND name LIKE CONCAT('%', #{name}, '%')
        </if>
        <if test="age != null">
            AND age = #{age}
        </if>
        <if test="email != null and email != ''">
            AND email = #{email}
        </if>
    </where>
</sql>

<!-- 使用 SQL 片段 -->
<select id="selectByCondition" resultType="User">
    SELECT
    <include refid="baseColumns"/>
    FROM user
    <include refid="whereCondition"/>
</select>
xml
<!-- SQL 片段也可以传参 -->
<sql id="columns">
    ${alias}.id, ${alias}.name, ${alias}.age
</sql>

<select id="selectWithAlias" resultType="User">
    SELECT
    <include refid="columns">
        <property name="alias" value="u"/>
    </include>
    FROM user u
</select>

六、selectKey(获取主键)

xml
<!-- 方式一:useGeneratedKeys(推荐) -->
<insert id="insert" useGeneratedKeys="true" keyProperty="id">
    INSERT INTO user (name, age, email) VALUES (#{name}, #{age}, #{email})
</insert>

<!-- 方式二:selectKey(适用于不支持自增的数据库) -->
<insert id="insert">
    <selectKey keyProperty="id" order="BEFORE" resultType="long">
        SELECT SEQ_USER.NEXTVAL FROM DUAL
    </selectKey>
    INSERT INTO user (id, name, age) VALUES (#{id}, #{name}, #{age})
</insert>

<!-- 方式三:selectKey AFTER(MySQL UUID 场景) -->
<insert id="insertWithUUID">
    <selectKey keyProperty="id" order="AFTER" resultType="string">
        SELECT UUID()
    </selectKey>
    INSERT INTO user (name, age) VALUES (#{name}, #{age})
</insert>

七、速记总结

四大标签:
  <select>  → 查询
  <insert>  → 新增
  <update>  → 修改
  <delete>  → 删除

参数传递:
  单参数   → #{任意名}
  多参数   → @Param("name") + #{name}
  对象     → #{属性名}
  Map     → #{key}
  List    → foreach 遍历

两个占位符:
  #{ }  → 预编译,防注入,用参数值
  ${ }  → 字符串拼接,有风险,用动态表名/列名

八、面试要点

问题答案要点
#{ } 和 ${ } 区别?#{} 预编译防注入,${} 字符串拼接有风险
多参数怎么传?@Param 注解指定名字
怎么获取自增主键?useGeneratedKeys + keyProperty
SQL 片段怎么用?sql 标签定义,include 引用