Skip to content

MyBatis Mapper 映射详解

XML 映射、注解映射、动态 SQL、SQL 构建


XML Mapper 结构

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

<mapper namespace="com.example.mapper.UserMapper">
    
    <!-- 结果映射 -->
    <resultMap id="BaseResultMap" type="User">
        <id column="id" property="id"/>
        <result column="user_name" property="name"/>
        <result column="create_time" property="createTime"/>
    </resultMap>
    
    <!-- SQL 片段 -->
    <sql id="Base_Column_List">
        id, user_name, email, create_time
    </sql>
    
    <!-- 查询 -->
    <select id="selectById" resultMap="BaseResultMap">
        SELECT <include refid="Base_Column_List"/>
        FROM users
        WHERE id = #{id}
    </select>
    
    <!-- 插入 -->
    <insert id="insert" useGeneratedKeys="true" keyProperty="id">
        INSERT INTO users (user_name, email)
        VALUES (#{name}, #{email})
    </insert>
    
    <!-- 更新 -->
    <update id="update">
        UPDATE users
        SET user_name = #{name},
            email = #{email}
        WHERE id = #{id}
    </update>
    
    <!-- 删除 -->
    <delete id="deleteById">
        DELETE FROM users WHERE id = #{id}
    </delete>
    
</mapper>

CRUD 操作

查询 (select)

xml
<!-- 简单查询 -->
<select id="selectById" resultType="User">
    SELECT * FROM users WHERE id = #{id}
</select>

<!-- 多参数查询(推荐:@Param) -->
<select id="selectByNameAndEmail" resultType="User">
    SELECT * FROM users 
    WHERE user_name = #{name} AND email = #{email}
</select>

<!-- 使用 resultMap -->
<select id="selectAll" resultMap="BaseResultMap">
    SELECT id, user_name, email, create_time
    FROM users
    ORDER BY create_time DESC
</select>

<!-- 返回单个简单类型 -->
<select id="count" resultType="int">
    SELECT COUNT(*) FROM users
</select>

<!-- 返回 Map -->
<select id="selectAsMap" resultType="map">
    SELECT id, user_name as name FROM users WHERE id = #{id}
</select>

<!-- 分页查询 -->
<select id="selectPage" resultMap="BaseResultMap">
    SELECT * FROM users 
    ORDER BY id
    LIMIT #{offset}, #{limit}
</select>

插入 (insert)

xml
<!-- 简单插入 -->
<insert id="insert" parameterType="User">
    INSERT INTO users (user_name, email, age)
    VALUES (#{name}, #{email}, #{age})
</insert>

<!-- 获取自增主键 -->
<insert id="insertAndGetId" useGeneratedKeys="true" keyProperty="id">
    INSERT INTO users (user_name, email)
    VALUES (#{name}, #{email})
</insert>

<!-- 使用 selectKey 获取序列 -->
<insert id="insertWithSequence">
    <selectKey keyProperty="id" resultType="long" order="BEFORE">
        SELECT SEQ_USERS.NEXTVAL FROM DUAL
    </selectKey>
    INSERT INTO users (id, user_name, email)
    VALUES (#{id}, #{name}, #{email})
</insert>

<!-- 批量插入 -->
<insert id="batchInsert" parameterType="java.util.List">
    INSERT INTO users (user_name, email)
    VALUES
    <foreach collection="list" item="item" separator=",">
        (#{item.name}, #{item.email})
    </foreach>
</insert>

<!-- Oracle 批量插入 -->
<insert id="batchInsertOracle" parameterType="java.util.List">
    INSERT ALL
    <foreach collection="list" item="item">
        INTO users (id, user_name, email)
        VALUES (#{item.id}, #{item.name}, #{item.email})
    </foreach>
    SELECT 1 FROM DUAL
</insert>

更新 (update)

xml
<update id="update">
    UPDATE users
    SET user_name = #{name},
        email = #{email},
        age = #{age}
    WHERE id = #{id}
</update>

<!-- 动态更新 -->
<update id="dynamicUpdate">
    UPDATE users
    <set>
        <if test="name != null">user_name = #{name},</if>
        <if test="email != null">email = #{email},</if>
    </set>
    WHERE id = #{id}
</update>

删除 (delete)

xml
<delete id="deleteById">
    DELETE FROM users WHERE id = #{id}
</delete>

<delete id="batchDelete" parameterType="java.util.List">
    DELETE FROM users WHERE id IN
    <foreach collection="list" item="id" open="(" separator="," close=")">
        #{id}
    </foreach>
</delete>

动态 SQL

if 条件

xml
<select id="search" resultType="User">
    SELECT * FROM users
    WHERE 1=1
    <if test="name != null">
        AND user_name LIKE CONCAT('%', #{name}, '%')
    </if>
    <if test="age != null and age > 0">
        AND age = #{age}
    </if>
    <if test="email != null and email.trim() != ''">
        AND email = #{email}
    </if>
</select>

choose / when / otherwise

xml
<select id="searchByCondition" resultType="User">
    SELECT * FROM users
    WHERE 1=1
    <choose>
        <when test="name != null">
            AND user_name = #{name}
        </when>
        <when test="email != null">
            AND email = #{email}
        </when>
        <otherwise>
            AND status = 'ACTIVE'
        </otherwise>
    </choose>
</select>

where 标签

xml
<!-- 自动处理 AND/OR 前缀 -->
<select id="searchAuto" resultType="User">
    SELECT * FROM users
    <where>
        <if test="name != null">
            AND user_name = #{name}
        </if>
        <if test="age != null">
            AND age = #{age}
        </if>
    </where>
</select>

set 标签

xml
<!-- 自动去除末尾逗号 -->
<update id="updateSelective">
    UPDATE users
    <set>
        <if test="name != null">user_name = #{name},</if>
        <if test="email != null">email = #{email},</if>
        <if test="age != null">age = #{age},</if>
    </set>
    WHERE id = #{id}
</update>

foreach 循环

xml
<!-- IN 查询 -->
<select id="selectByIds" resultType="User">
    SELECT * FROM users
    WHERE id IN
    <foreach collection="ids" item="id" open="(" separator="," close=")">
        #{id}
    </foreach>
</select>

<!-- 批量插入 -->
<insert id="batchInsert">
    INSERT INTO users (user_name, email) VALUES
    <foreach collection="list" item="user" separator=",">
        (#{user.name}, #{user.email})
    </foreach>
</insert>

<!-- 批量更新 -->
<update id="batchUpdate">
    <foreach collection="list" item="user" separator=";">
        UPDATE users 
        SET user_name = #{user.name}
        WHERE id = #{user.id}
    </foreach>
</update>

<!-- 使用 @Param 指定参数名 -->
<select id="selectByIds">
    SELECT * FROM users
    WHERE id IN
    <foreach collection="ids" item="id" open="(" separator="," close=")">
        #{id}
    </foreach>
</select>

bind 绑定

xml
<!-- 绑定变量,避免 SQL 注入 -->
<select id="searchLike" resultType="User">
    <bind name="pattern" value="'%' + name + '%'"/>
    SELECT * FROM users
    WHERE user_name LIKE #{pattern}
</select>

trim 自定义

xml
<!-- 等同于 where -->
<trim prefix="WHERE" prefixOverrides="AND|OR">
    <if test="name != null">AND user_name = #{name}</if>
</trim>

<!-- 等同于 set -->
<trim suffixOverrides=",">
    <if test="name != null">user_name = #{name},</if>
    <if test="email != null">email = #{email},</if>
</trim>

resultMap 结果映射

基础映射

xml
<resultMap id="BaseResultMap" type="User">
    <!-- 主键映射 -->
    <id column="id" property="id"/>
    
    <!-- 普通列映射 -->
    <result column="user_name" property="name"/>
    <result column="email" property="email"/>
    <result column="create_time" property="createTime"/>
</resultMap>

自动映射

xml
<!-- 未匹配列使用自动映射(列名=属性名) -->
<resultMap id="AutoMap" type="User" autoMapping="true">
    <id column="id" property="id"/>
    <!-- 其他列自动映射 -->
</resultMap>

构造方法映射

xml
<resultMap id="ConstructorMap" type="User">
    <constructor>
        <idArg column="id" javaType="long"/>
        <arg column="user_name" javaType="string"/>
        <arg column="email" javaType="string"/>
    </constructor>
</resultMap>

注解映射

简单注解

java
public interface UserMapper {
    
    @Select("SELECT * FROM users WHERE id = #{id}")
    User selectById(Long id);
    
    @Insert("INSERT INTO users (user_name, email) VALUES (#{name}, #{email})")
    @Options(useGeneratedKeys = true, keyProperty = "id")
    int insert(User user);
    
    @Update("UPDATE users SET user_name = #{name} WHERE id = #{id}")
    int update(User user);
    
    @Delete("DELETE FROM users WHERE id = #{id}")
    int deleteById(Long id);
}

@Results 定义结果映射

java
@Results(id = "UserResult", value = {
    @Result(column = "id", property = "id"),
    @Result(column = "user_name", property = "name"),
    @Result(column = "email", property = "email"),
    @Result(column = "create_time", property = "createTime")
})
@Select("SELECT * FROM users WHERE id = #{id}")
User selectById(Long id);

@One 一对一关联

java
@Results({
    @Result(column = "user_id", property = "userId"),
    @Result(column = "user_id", property = "user",
            one = @One(select = "com.example.mapper.UserMapper.selectById"))
})
@Select("SELECT * FROM orders WHERE id = #{id}")
Order selectOrderWithUser(Long id);

@Many 一对多关联

java
@Results({
    @Result(column = "id", property = "id"),
    @Result(column = "user_name", property = "name"),
    @Result(property = "orders",
            many = @Many(select = "com.example.mapper.OrderMapper.selectByUserId"))
})
@Select("SELECT * FROM users WHERE id = #{id}")
User selectUserWithOrders(Long id);

动态 SQL 注解

java
public interface UserMapper {
    
    @SelectProvider(type = UserSqlProvider.class, method = "search")
    List<User> search(UserQuery query);
}

public class UserSqlProvider {
    
    public String search(UserQuery query) {
        return new SQL()
            .SELECT("*")
            .FROM("users")
            .WHERE("1=1")
            .ifWhere(query.getName() != null, 
                "user_name LIKE CONCAT('%', #{name}, '%')")
            .ifWhere(query.getAge() != null, 
                "age = #{age}")
            .toString();
    }
}

SQL 片段

xml
<mapper>
    <!-- 可复用 SQL 片段 -->
    <sql id="Base_Column_List">
        id, user_name, email, age, create_time
    </sql>
    
    <select id="selectAll" resultType="User">
        SELECT <include refid="Base_Column_List"/>
        FROM users
    </select>
    
    <select id="selectById" resultType="User">
        SELECT <include refid="Base_Column_List"/>
        FROM users
        WHERE id = #{id}
    </select>
    
    <!-- 引用外部 SQL 片段 -->
    <select id="search" resultType="User">
        SELECT <include refid="com.example.mapper.CommonMapper.Base_Column_List"/>
        FROM users
        <where>
            <if test="name != null">
                AND user_name = #{name}
            </if>
        </where>
    </select>
</mapper>

关联查询

关联类型实现方式
一对一&lt;association&gt;@One
一对多&lt;collection&gt;@Many
多对多中间表 + 两个一对多

详细见:**Java\MyBatis\MyBatis-关联查询**


相关页面

  • **Java\MyBatis\MyBatis-完全指南** - 返回总览
  • **Java\MyBatis\MyBatis-核心配置** - 配置详解
  • **Java\MyBatis\MyBatis-关联查询** - 复杂关系查询
  • **Java\MyBatis\MyBatis-进阶应用** - 分页、插件

#标签 #mybatis #mapper #xml #annotation #dynamic-sql #crud

Knowledge4J — Java 知识库