哈喽!本文将从零带大家吃透 MyBatis 核心知识点,从底层优势、快速入门、Mapper 代理开发,到配置文件实现全套增删改查、参数封装原理、注解开发,全程搭配实战代码示例,重点难点逐一拆解,适合零基础入门、面试复盘、项目实操。

一、MyBatis 核心概述

1.1 什么是 MyBatis?

MyBatis 是一款优秀的开源持久层框架,专门用于简化 Java JDBC 数据库开发,解决原生 JDBC 开发的各类痛点,是 JavaEE 开发中持久层的主流框架。

1.2 JavaEE 三层架构

JavaEE 标准项目分层,各司其职,MyBatis 专注于持久层开发:

  • 表现层(Web层):接收前端请求、响应数据(Controller)
  • 业务层(Service层):处理业务逻辑、事务控制(Service)
  • 持久层(Dao层):负责和数据库交互,完成数据存取(Dao/Mapper),MyBatis 框架作用于此层

1.3 原生 JDBC 开发痛点(MyBatis 解决的核心问题)

原生 JDBC 开发繁琐、冗余代码多、维护性差,核心缺点如下:

  • 硬编码问题:数据库驱动、连接地址、账号密码、SQL 语句全部写死在代码中,修改需要改源码、重启项目
  • 操作繁琐:需要手动加载驱动、创建连接、关闭资源、手动设置 SQL 参数、手动封装查询结果集,重复代码极多

而 MyBatis 通过配置文件解耦硬编码自动完成参数注入和结果封装,彻底简化 JDBC 操作。

二、MyBatis 快速入门

2.1 入门整体步骤

  1. 创建数据库表(示例 user 表/brand 表),初始化测试数据
  2. 创建 Maven 项目,导入 MyBatis、数据库驱动等核心坐标
  3. 编写 MyBatis 核心配置文件:配置数据库连接信息,解决连接参数硬编码问题
  4. 编写 SQL 映射文件(Mapper 文件):统一管理所有 SQL 语句,解决 SQL 硬编码问题
  5. 编写业务代码,完成数据库操作

2.2 核心编码流程

标准 MyBatis 执行流程,固定 4 步:

  1. 定义数据库对应 POJO 实体类(属性映射表字段)
  2. 加载 MyBatis 核心配置文件,获取SqlSessionFactory 工厂对象
  3. 通过工厂获取 SqlSession 会话对象,执行 SQL 操作
  4. 关闭 SqlSession,释放资源

三、Mapper 代理开发(主流开发方式)

原始 MyBatis 需要通过字符串 ID 执行 SQL,存在字符串硬编码、无代码提示、易出错的问题,而 Mapper 代理开发是企业主流用法,通过接口代理自动匹配 SQL,简洁高效、可维护性强。

3.1 原始写法(缺点明显)

// 通过映射文件的 statementId 执行 SQL,字符串硬编码,极易出错
List<User> users = sqlSession.selectList("test.selectAll");
System.out.println(users);

3.2 Mapper 代理写法(推荐)

// 1. 获取 Mapper 接口的代理对象
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
// 2. 调用接口方法,自动执行对应 SQL,无硬编码、有代码提示
List&lt;User&gt; userList = userMapper.selectAll();

3.3 Mapper 代理核心规则

  • Mapper 接口的全限定类名必须和 Mapper 映射文件的 namespace 一致
  • 接口中的方法名必须和映射文件中 SQL 标签的 id 一致
  • 方法返回值、参数类型必须和 SQL 配置的返回值、参数类型匹配

四、MyBatis 核心配置文件 & 映射文件详解

4.1 核心配置文件

核心配置文件用于全局配置 MyBatis 环境,包含数据库连接、环境配置、映射文件引入等,彻底解耦数据库硬编码。核心配置项:环境配置、事务管理、连接池、映射文件注册。

4.2 映射文件核心作用

专门用来存放 SQL 语句,统一管理数据层操作,支持动态 SQL、结果映射、参数绑定,是 MyBatis 操作数据库的核心文件。

五、配置文件实现全套增删改查(重点)

5.1 查询操作

5.1.1 基础查询:解决实体类与数据库字段名不一致问题

数据库字段通常下划线命名(brand_name),Java 实体类属性驼峰命名(brandName),默认无法自动封装数据,提供两种解决方案:

方案1:SQL 片段别名(复用性一般)

通过 <sql> 定义通用字段片段,统一起别名,适合简单场景

<!-- 名称空间:绑定对应 Mapper 接口 -->
<mapper namespace="com.hebu.mapper.BrandMapper">
    <!-- 定义通用字段片段,统一别名 -->
    <sql id="brand_column">
        id,brand_name brandName,company_name companyName,ordered,description
    </sql>
    <!-- 查询所有数据 -->
    <select id="selectAll" resultType="brand">
        select <include refid="brand_column"/> from tb_brand
    </select>
</mapper>
方案2:ResultMap 结果映射(推荐、灵活)

通过 <resultMap> 手动绑定数据库字段与实体类属性,适配所有复杂场景,企业首选

<mapper namespace="com.hebu.mapper.BrandMapper">
    <!-- 结果映射:解决字段与属性名不一致 -->
    <resultMap id="brandResultMap" type="brand">
        <!-- column:数据库字段名,property:实体类属性名 -->
        <result column="brand_name" property="brandName"/>
        <result column="company_name" property="companyName"/>
    </resultMap>
    
    <!-- 查询所有,引用自定义 ResultMap -->
    <select id="selectAll" resultMap="brandResultMap">
        select * from tb_brand
    </select>
</mapper>

5.1.2 单条详情查询 & 参数占位符

参数占位符核心区别(高频面试点)
  • #{}:预编译占位符,替换为 ?防止 SQL 注入,日常参数传递首选
  • ${}:字符串拼接 SQL,存在 SQL 注入风险,仅用于动态表名、动态列名场景

补充:parameterType 参数类型可省略,MyBatis 可自动推断

详情查询示例
<select id="selectById" resultMap="brandResultMap">
    select * from tb_brand where id=#{id}
</select>
特殊字符处理

XML 中 <、> 等特殊字符无法直接使用,两种解决方案:

  1. 转义字符:< 替代 <,> 替代 >
  2. CDATA 区:<![CDATA[ 内容 ]]> 包裹特殊语法

5.1.3 多条件动态查询

通过 <if> 标签判断参数是否为空,实现动态拼接 SQL,搭配 <where> 标签自动处理多余 and/or,规避语法错误

参数接收三种方式
  1. 散装参数:多参数需加 @Param("参数名") 绑定占位符
  2. 对象参数:占位符名称与实体类属性名一致
  3. Map 参数:占位符名称与 Map 的 key 一致
多条件查询完整示例
<select id="selectByCondition" resultMap="brandResultMap">
    select * from tb_brand
    <where>
        <!-- 非空判断,动态拼接条件 -->
        <if test="status!=null">
            and status=#{status}
        </if>
        <if test="brandName!=null and brandName!=''>
            and brand_name like #{brandName}
        </if>
        <if test="companyName!=null and companyName!=''>
            and company_name like #{companyName}
        </if>
    </where>
</select>

5.1.4 单条件单选查询

通过 <choose><when><otherwise> 实现类似 switch-case 效果,只匹配一个条件,优先级从上到下

<select id="selectByConditionSingle" resultMap="brandResultMap">
    select * from tb_brand
    <where>
        <choose>
            <when test="status!=null">and status=#{status}</when>
            <when test="brandName!=null and brandName!=''>and brand_name like #{brandName}</when>
            <when test="companyName!=null and companyName!=''>and company_name like #{companyName}</when>
            <!-- 所有条件不匹配时的默认值 -->
            <otherwise>1=1</otherwise>
        </choose>
    </where>
</select>

5.2 新增操作

5.2.1 基础新增

MyBatis 默认需要手动提交事务,调用 sqlSession.commit();可通过 openSession(true) 设置自动提交事务

<insert id="insert">
    insert into tb_brand(brand_name,company_name,ordered,description,status)
    values(#{brandName},#{companyName},#{ordered},#{description},#{status})
</insert>

5.2.2 主键返回(自增主键)

新增数据后,自动获取数据库自增主键 ID,核心属性:useGeneratedKeys="true" 开启主键返回,keyProperty="id" 绑定实体类主键属性

<insert id="insert" useGeneratedKeys="true" keyProperty="id">
    insert into tb_brand(brand_name,company_name,ordered,description,status)
    values(#{brandName},#{companyName},#{ordered},#{description},#{status})
</insert>

5.3 修改操作

通过 <set> + <if> 实现动态修改,只更新非空字段,自动去除最后多余的逗号,避免 SQL 语法错误

<update id="update">
    update tb_brand
    <set>
        <if test="brandName!=null and brandName!=''>brand_name=#{brandName},</if>
        <if test="companyName!=null and companyName!=''>company_name=#{companyName},</if>
        <if test="ordered!=null">ordered=#{ordered},</if>
        <if test="description!=null and description!=''>description=#{description},</if>
        <if test="status!=null">status=#{status}</if>
    </set>
    where id=#{id}
</update>

5.4 删除操作

5.4.1 批量删除

MyBatis 会将数组参数封装为 Map 集合,默认 key 为 array,可通过 @Param 自定义 key,搭配 <foreach> 遍历数组实现批量删除

方式1:@Param 自定义参数名(推荐,可读性强)

Mapper 接口方法:

void deletes(@Param("ids") Integer[] ids);

Mapper 映射文件:

<delete id="deletes">
    delete from tb_brand where id in
    <foreach collection="ids" item="id" separator="," open="(" close=")">
        #{id}
    </foreach>
</delete>
方式2:使用默认 array key

Mapper 接口方法:

void deletes(Integer[] ids);

Mapper 映射文件:

<delete id="deletes">
    delete from tb_brand where id in
    <foreach collection="array" item="id" separator="," open="(" close=")">
        #{id}
    </foreach>
</delete>

六、MyBatis 参数封装底层原理

MyBatis 通过 ParamNameResolver 类完成参数封装,核心是将所有参数统一封装为 Map 集合,通过 key 匹配 SQL 占位符。

6.1 单个参数封装规则

  • POJO 对象:直接封装,占位符名称与对象属性名一致即可
  • Map 集合:直接封装,占位符名称与 Map 的 key 一致
  • Collection/List:封装为 Map,默认 key:collection/list/arg0,推荐用 @Param 自定义 key
  • Array 数组:封装为 Map,默认 key:array/arg0
  • 基本类型:直接使用,无需额外处理

6.2 多个参数封装规则

多个参数默认封装为 Map 集合,默认 key 为 arg0、arg1、param1、param2,可读性极差,必须使用 @Param 注解自定义参数名

七、MyBatis 注解开发

MyBatis 支持注解式开发,无需编写 Mapper 映射文件,代码更简洁,适合简单 CRUD 功能;复杂动态 SQL、多表查询仍推荐配置文件开发。

7.1 注解查询示例

// 通过 @Select 注解直接编写 SQL,无需 xml 文件
@Select("select * from tb_brand where id=#{id}")
Brand selectById(Integer id);

7.2 常用注解

  • @Select:查询语句
  • @Insert:新增语句
  • @Update:修改语句
  • @Delete:删除语句

八、MyBatis 核心知识点对比表(高频考点)

整理 MyBatis 开发中最容易混淆、面试高频的知识点对比,一目了然,快速区分用法差异:

对比维度

选项一

选项二

使用场景 & 核心区别

参数占位符

#{} 预编译占位符

${} 字符串拼接

#{}:生成 ? 预编译语句,防止 SQL 注入,适配所有普通参数传递(日常首选);${}:直接拼接 SQL 字符串,有注入风险,仅用于动态表名、动态列名排序字段

字段映射方案

SQL 片段别名

ResultMap 映射

SQL别名:简单快速、代码复用,但灵活性差,适合固定字段查询;ResultMap:配置一次全局复用、支持复杂映射、灵活度高,企业项目首选

条件动态查询

<if> + <where> 多条件

<choose> 单条件

<if>:多条件可同时生效,适用于组合筛选、多参数模糊查询;<choose>:单选匹配,只执行一个符合条件的分支,适用于优先级筛选

数据更新方式

普通 update 标签

<set> + <if> 动态更新

普通更新:全字段覆盖更新,容易覆盖空值;动态更新:只更新非空字段,自动去除多余逗号,适配前端局部修改场景

开发方式

XML 配置文件开发

注解开发

XML:支持复杂动态 SQL、多表关联、SQL 统一管理,适合复杂业务;注解:代码简洁、无需 XML 文件,仅适合简单单表 CRUD

数组批量参数

默认 array 集合key

@Param 自定义key

默认array:无需注解,写法简单、可读性差;@Param:自定义参数名称,语义清晰、团队协作友好,企业推荐

事务提交

手动 commit()

openSession(true) 自动提交

手动提交:精准控制事务,支持事务回滚,适配复杂业务;自动提交:简单CRUD快速开发,无需手动提交

九、总结

  1. MyBatis 核心价值:解耦硬编码、简化 JDBC 操作、自动封装结果集
  2. 主流开发方式:Mapper 代理开发,规避字符串硬编码
  3. 字段属性不一致解决方案:优先 ResultMap 映射
  4. 参数占位符:#{} 防注入(常规参数),${} 拼接 SQL(动态表名/列名)
  5. 动态 SQL 核心标签:、、、、
  6. 开发选型:简单功能用注解,复杂动态 SQL 用XML 配置文件
Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐