MyBatis 完整知识体系

一、核心配置文件

1. application.yml 配置

mybatis:
  # mapper.xml 文件位置
  mapper-locations: classpath:mapper/*.xml
  # 实体类别名包(resultType 可以写类名,不用全路径)
  type-aliases-package: com.fitow.system.entity
  configuration:
    # 驼峰命名自动映射
    map-underscore-to-camel-case: true
    # 打印 SQL 日志
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    # 延迟加载
    lazy-loading-enabled: true
    aggressive-lazy-loading: false

2. mybatis-config.xml(传统方式)

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <settings>
        <setting name="mapUnderscoreToCamelCase" value="true"/>
        <setting name="logImpl" value="STDOUT_LOGGING"/>
    </settings>
    <typeAliases>
        <package name="com.fitow.system.entity"/>
    </typeAliases>
</configuration>

二、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.fitow.system.mapper.SysMenuMapper">

    <!-- SQL 写在这里 -->

</mapper>

要点

  • namespace 必须是对应的 Mapper 接口全路径
  • 一个 XML 对应一个 Mapper 接口

三、四种基本操作标签

1. <select> - 查询

<!-- 查询单条 -->
<select id="findById" resultType="SysMenu">
    SELECT * FROM sys_menu WHERE id = #{id}
</select>

<!-- 查询列表 -->
<select id="findAll" resultType="SysMenu">
    SELECT * FROM sys_menu
</select>

<!-- 条件查询 -->
<select id="search" resultType="SysMenu">
    SELECT * FROM sys_menu
    WHERE label LIKE CONCAT('%', #{keyword}, '%')
</select>

2. <insert> - 插入

<!-- 普通插入 -->
<insert id="insert">
    INSERT INTO sys_menu(label, path, menu_type)
    VALUES(#{label}, #{path}, #{menuType})
</insert>

<!-- 插入并返回自增ID -->
<insert id="insert" useGeneratedKeys="true" keyProperty="id">
    INSERT INTO sys_menu(label, path, menu_type)
    VALUES(#{label}, #{path}, #{menuType})
</insert>

<!-- 批量插入 -->
<insert id="batchInsert">
    INSERT INTO sys_menu(label, path, menu_type) VALUES
    <foreach collection="list" item="item" separator=",">
        (#{item.label}, #{item.path}, #{item.menuType})
    </foreach>
</insert>

3. <update> - 更新

<!-- 根据ID更新 -->
<update id="updateById">
    UPDATE sys_menu
    SET label = #{label},
        path = #{path},
        menu_type = #{menuType}
    WHERE id = #{id}
</update>

4. <delete> - 删除

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

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

四、参数传递

1. #{} - 预编译(推荐,防SQL注入)

<select id="findByName" resultType="SysMenu">
    SELECT * FROM sys_menu WHERE label = #{name}
</select>

2. ${} - 字符串拼接(慎用,有SQL注入风险)

<select id="findByColumn" resultType="SysMenu">
    SELECT * FROM sys_menu ORDER BY ${column}
</select>

3. 多参数传递

// Mapper 接口
List<SysMenu> findByTypeAndStatus(@Param("type") Integer type, 
                                   @Param("status") Integer status);
<select id="findByTypeAndStatus" resultType="SysMenu">
    SELECT * FROM sys_menu 
    WHERE menu_type = #{type} AND noshow = #{status}
</select>

4. Map 传参

List<SysMenu> findByMap(Map<String, Object> params);
<select id="findByMap" resultType="SysMenu">
    SELECT * FROM sys_menu 
    WHERE menu_type = #{type} AND label LIKE CONCAT('%', #{keyword}, '%')
</select>

五、resultType vs resultMap

1. resultType(简单映射)

<!-- 列名和属性名一致,或开启了驼峰映射 -->
<select id="findAll" resultType="com.fitow.system.entity.SysMenu">
    SELECT * FROM sys_menu
</select>

2. resultMap(复杂映射)

<resultMap id="menuMap" type="SysMenu">
    <id column="id" property="id"/>           <!-- 主键 -->
    <result column="label" property="label"/> <!-- 普通字段 -->
    <result column="menu_type" property="menuType"/>
    <result column="create_time" property="createTime"/>
</resultMap>

<select id="findById" resultMap="menuMap">
    SELECT * FROM sys_menu WHERE id = #{id}
</select>

六、动态 SQL(核心技能)

1. <if> - 条件判断

<select id="search" resultType="SysMenu">
    SELECT * FROM sys_menu WHERE 1=1
    <if test="label != null and label != ''">
        AND label LIKE CONCAT('%', #{label}, '%')
    </if>
    <if test="menuType != null">
        AND menu_type = #{menuType}
    </if>
</select>

2. <where> - 动态 WHERE(自动处理 AND/OR)

<select id="search" resultType="SysMenu">
    SELECT * FROM sys_menu
    <where>
        <if test="label != null">
            AND label LIKE CONCAT('%', #{label}, '%')
        </if>
        <if test="menuType != null">
            AND menu_type = #{menuType}
        </if>
    </where>
</select>

3. <set> - 动态更新(自动去逗号)

<update id="updateSelective">
    UPDATE sys_menu
    <set>
        <if test="label != null">label = #{label},</if>
        <if test="path != null">path = #{path},</if>
        <if test="menuType != null">menu_type = #{menuType},</if>
    </set>
    WHERE id = #{id}
</update>

4. <foreach> - 循环遍历

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

<!-- 批量插入 -->
<insert id="batchInsert">
    INSERT INTO sys_menu(label, path) VALUES
    <foreach collection="list" item="item" separator=",">
        (#{item.label}, #{item.path})
    </foreach>
</insert>

5. <choose>/<when>/<otherwise> - 条件分支

<select id="findAll" resultType="SysMenu">
    SELECT * FROM sys_menu
    ORDER BY
    <choose>
        <when test="sortField == 'createTime'">
            create_time DESC
        </when>
        <when test="sortField == 'sort'">
            sort ASC
        </when>
        <otherwise>
            id ASC
        </otherwise>
    </choose>
</select>

6. <sql>/<include> - SQL 片段复用

<!-- 定义 -->
<sql id="baseColumns">
    id, label, path, menu_type, create_time
</sql>

<sql id="whereClause">
    <where>
        <if test="label != null">
            AND label LIKE CONCAT('%', #{label}, '%')
        </if>
    </where>
</sql>

<!-- 引用 -->
<select id="findAll" resultType="SysMenu">
    SELECT <include refid="baseColumns"/>
    FROM sys_menu
    <include refid="whereClause"/>
</select>

七、关联查询

1. 一对一(<association>

public class Order {
    private Long id;
    private User user;  // 关联的用户对象
}
<resultMap id="orderMap" type="Order">
    <id column="id" property="id"/>
    <association property="user" javaType="User">
        <id column="user_id" property="id"/>
        <result column="user_name" property="name"/>
    </association>
</resultMap>

<select id="findOrderWithUser" resultMap="orderMap">
    SELECT o.*, u.id AS user_id, u.name AS user_name
    FROM orders o
    LEFT JOIN user u ON o.user_id = u.id
</select>

2. 一对多(<collection>)- 嵌套查询

<resultMap id="menuTreeMap" type="SysMenu">
    <id column="id" property="id"/>
    <collection property="children" 
                ofType="SysMenu"
                column="id"
                select="selectChildrenByParentId"/>
</resultMap>

<select id="selectChildrenByParentId" resultType="SysMenu">
    SELECT * FROM sys_menu WHERE parent_id = #{parentId}
</select>

<select id="selectMenuTree" resultMap="menuTreeMap">
    SELECT * FROM sys_menu WHERE parent_id IS NULL
</select>

3. 一对多(<collection>)- 联合查询

<resultMap id="menuWithButtonsMap" type="MenuWithButtonsDTO">
    <id column="id" property="id"/>
    <result column="label" property="label"/>
    <collection property="buttons" ofType="ButtonDTO">
        <id column="button_id" property="id"/>
        <result column="button_name" property="name"/>
    </collection>
</resultMap>

<select id="selectMenuWithButtons" resultMap="menuWithButtonsMap">
    SELECT m.*, b.id AS button_id, b.name AS button_name
    FROM sys_menu m
    LEFT JOIN sys_button b ON m.id = b.menu_id
</select>

八、分页查询

1. 手动分页(MySQL)

<select id="pageList" resultType="SysMenu">
    SELECT * FROM sys_menu
    <where>
        <if test="label != null">
            AND label LIKE CONCAT('%', #{label}, '%')
        </if>
    </where>
    ORDER BY sort ASC
    LIMIT #{offset}, #{pageSize}
</select>

2. PageHelper 分页(推荐)

// 引入依赖
<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper-spring-boot-starter</artifactId>
    <version>1.4.6</version>
</dependency>
// 使用
PageHelper.startPage(pageNum, pageSize);
List<SysMenu> list = sysMenuMapper.findAll();
PageInfo<SysMenu> pageInfo = new PageInfo<>(list);

// 获取分页信息
pageInfo.getTotal();      // 总条数
pageInfo.getPages();      // 总页数
pageInfo.getList();       // 当前页数据

九、缓存

1. 一级缓存(默认开启,SqlSession 级别)

<!-- 不需要配置,默认开启 -->

2. 二级缓存(Mapper 级别)

<mapper namespace="com.fitow.system.mapper.SysMenuMapper">
    <!-- 开启二级缓存 -->
    <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>
</mapper>

十、MyBatis vs MyBatis-Plus 对比

对比项 MyBatis MyBatis-Plus
基础CRUD 手写 SQL 自动生成
动态SQL 手写 <if><where> LambdaQueryWrapper
分页 手写 LIMIT 或 PageHelper this.page()
逻辑删除 手写条件 @TableLogic 自动处理
乐观锁 手写 version 判断 @Version 自动处理
自动填充 手写 MetaObjectHandler
代码量 少(减少约70%)
灵活性 高(完全控制SQL) 中(复杂SQL仍需手写)

十一、使用优先级

第1优先:MyBatis-Plus Service 方法
    this.save()、this.list()、this.page()

第2优先:MyBatis-Plus LambdaQueryWrapper
    wrapper.eq().like().list()

第3优先:MyBatis XML 基本操作
    <select>、<insert>、<update>、<delete>

第4优先:MyBatis XML 动态SQL
    <if>、<where>、<foreach>、<set>

第5优先:MyBatis XML 关联查询
    <association>、<collection>

十二、快速记忆口诀

四个标签要记牢:select insert update delete
参数传递用井号:#{} 安全又可靠
动态SQL三件套:if where foreach
结果映射两兄弟:resultType 简单,resultMap 复杂
关联查询两招:association 一对一,collection 一对多
代码复用靠 sql + include
条件分支 choose when otherwise

这份 MyBatis 笔记和 MP 那套结构一样,覆盖了 MyBatis 全部核心知识点,日常开发够用了。


一、MyBatis XML 常用标签(最重要)

标签 作用
<select> 查询 SQL
<insert> 插入 SQL
<update> 更新 SQL
<delete> 删除 SQL
<resultMap> 结果集映射
<parameterMap> 参数映射(已不推荐)
<sql> 定义可复用 SQL 片段
<include> 引入 SQL 片段
<cache> 二级缓存配置
<cache-ref> 引用其他 namespace 的缓存

二、动态 SQL 关键字(高频考点)

标签 说明
<if> 条件判断
<choose> / <when> / <otherwise> 类似 switch-case
<trim> 去多余逗号/AND/OR
<where> 自动处理 WHERE
<set> 自动处理 SET
<foreach> 遍历集合(IN、批量)
<bind> 绑定变量(常用于模糊查询)

✅ 示例(<where> + <if>):

<select id="selectUser">
  SELECT * FROM user
  <where>
    <if test="name != null">
      AND name = #{name}
    </if>
  </where>
</select>

三、SQL 中 MyBatis 常用关键字(结合 JDBC)

关键字 说明
#{} 预编译参数(防 SQL 注入 ✅)
${} 字符串拼接(有注入风险 ⚠️)
resultType 返回类型
resultMap 自定义结果映射
parameterType 参数类型
useGeneratedKeys 获取自增主键
keyProperty 主键字段
keyColumn 数据库主键列
flushCache 是否刷新缓存
useCache 是否使用二级缓存
timeout SQL 超时时间
fetchSize JDBC fetchSize

四、Mapper 接口相关关键字(注解)

注解 说明
@Select 查询
@Insert 插入
@Update 更新
@Delete 删除
@Param 给参数命名
@Result 字段映射
@Results 多个 Result
@One 一对一
@Many 一对多

五、关联映射关键字(ResultMap)

标签 说明
<id> 主键
<result> 普通字段
<association> 一对一
<collection> 一对多
columnPrefix 列前缀
lazyLoadingEnabled 延迟加载
aggressiveLazyLoading 侵入式懒加载

六、MyBatis 核心配置(mybatis-config.xml)

配置项 说明
<configuration> 根标签
<environments> 环境配置
<environment> 单个环境
<transactionManager> 事务管理器
<dataSource> 数据源
<mappers> Mapper 扫描
<typeAliases> 别名
<plugins> 插件(分页、拦截器)

七、常见面试一句话总结 ✅

MyBatis 常用关键字 = SQL 关键字 + 动态 SQL 标签 + #{} / ${} + resultMap + 缓存配置


更多推荐