Mybatis的变更
一、MyBatis 到底解决什么问题
MyBatis 是一个持久层框架,支持自定义 SQL、存储过程和高级映射,目标是减少几乎所有 JDBC 模板代码,并把参数设置、结果取回这些脏活抽掉。
官方介绍里写得很直白:它可以用 XML 或注解 来配置,把基本类型、Map、POJO 映射到数据库记录。
你可以这样理解
JDBC 时代你得自己写:
1 | Connection conn = dataSource.getConnection(); |
MyBatis 的价值在于把下面几件事接住:
安全执行 SQL
参数对象映射到 JDBC 占位符
结果集映射到 Java 对象
动态生成 SQL
二、第一阶段:原生 MyBatis,核心是 XML 映射
这阶段的主角有两个:
1. 全局配置 mybatis-config.xml
官方配置文档给出的高层结构包括:properties、settings、typeAliases、typeHandlers、plugins、environments、databaseIdProvider、mappers。也就是说它是 MyBatis 的总控配置。
2. 每个 Mapper 对应一个 Mapper.xml
官方文档强调,Mapper XML Files 的真正威力在 Mapped Statements,也就是 SQL 映射定义本身。
三、原生 XML 写法长什么样
1. 全局配置例子
1 |
|
2. Mapper 接口
1 | public interface UserMapper { |
3. Mapper XML
1 | <mapper namespace="com.example.mapper.UserMapper"> |
这里的关键是:
namespace对应接口全限定名id对应方法名#{}做参数绑定resultType指定结果类型
官方文档一直强调 Mapper XML 的重点就在这些 Mapped Statements。
四、为什么早期大家很爱 XML
SQL 和 Java 分离
复杂 SQL 放 XML,比直接塞进注解里更可读。
结果映射更强
尤其复杂对象映射、关联映射、集合映射,XML 的表达力很强。官方也把 MyBatis 定位成支持“高级映射”的框架。
JDBC 代码大幅减少
官方直接说,相比等价 JDBC 代码,Mapper XML 通常能看到接近 95% 的代码节省。这个说法很夸张,但它反映了设计目标。
五、第二阶段:从“静态 XML”到“动态 XML”
这一步是 MyBatis 真正封神的地方。
因为静态 SQL 很快会碰到组合爆炸:
有 name 就按 name 查
有 age 就按 age 查
两个都有就都加
有 ids 就
IN有排序就拼
ORDER BY有更新时间区间就再拼两段
如果每种组合单独写一条 SQL,XML 会直接变成怪兽。
于是 MyBatis 的答案是 动态 SQL。官方文档给出的核心动态标签是:
ifchoose / when / otherwisetrimwheresetforeach
而且官方明确提到,MyBatis 3 相比过去的版本,动态元素数量减少到不到一半,并借助 OGNL 表达式来简化书写。
六、动态 XML 的典型写法
1. if:条件成立才拼接
1 | <select id="listUsers" resultType="User"> |
作用:
避免你手动在 Java 里拼字符串。官方把 if 作为最常见的动态 SQL 元素来讲。
2. where:自动处理前导 AND
你手写时最烦的是:
没条件时别出现孤零零的
where第一个条件别变成
where and ...
<where> 就是为这个准备的。官方把它视为 trim 的一个便利形式。
3. set:动态更新时自动处理逗号
这样就不用自己费劲处理最后一个逗号。官方也把 set 列为核心动态元素之一。
4. foreach:批量 IN、批量插入、批量删除
1 | <select id="selectByIds" resultType="User"> |
foreach 是处理集合类参数的经典方式。官方动态 SQL 文档把它作为核心能力保留。
5. choose:多选一逻辑
1 | <select id="queryUser" resultType="User"> |
这个很像 Java 里的 if else if else。
6. trim:更底层、更灵活的裁剪器
1 | <select id="listUsers" resultType="User"> |
where 和 set 本质上都可以看成 trim 的便利包装。
七、你担心的“十几个字段怎么办”到底怎么落地
情况 A:字段 3 到 6 个
动态 XML 非常合适,写起来快,团队也容易看懂。
情况 B:字段 10 个以上,条件还是可选
还能写,但建议开始做分层:
1. 参数对象化
不要十几个参数散落方法签名里。
1 | public class UserQuery { |
2. SQL 片段复用
官方 Mapper XML 提供了 <sql> 和 <include> 来复用片段。
1 | <select id="selectById" resultType="User"> |
3. 把“筛选条件”和“联表结果映射”分开想
很多人把复杂度全怪到 XML 头上,其实真正复杂的是业务查询本身。
八、第三阶段:注解式 Mapper 出现了
官方介绍里就明确说,MyBatis 可以使用 XML 或注解 进行配置。
典型例子
1 |
|
适合场景
简单 CRUD
SQL 很短
不想多维护一个 XML
不适合场景
大段复杂 SQL
动态条件很多
复杂结果映射很多
换句话说,注解让小 SQL 更方便,但没有把 XML 淘汰掉。官方 MyBatis Dynamic SQL 甚至在新文档里还写得很直白:总体推荐注解 Mapper,但某些 JOIN 场景仍然需要 XML 来定义结果映射。
九、第四阶段:和 Spring 集成,SqlSession 被藏到幕后
MyBatis 的核心 Java API 是 SqlSession,官方文档明确说:
SqlSession 是使用 MyBatis 的主要 Java 接口,而 SqlSessionFactory 用来创建 SqlSession。
原生用法像这样:
1 | SqlSession session = sqlSessionFactory.openSession(); |
后来和 Spring 集成后,很多样板都被框架接管了。
你只看到:
1 |
|
背后其实是 Spring 帮你管理了 Mapper 代理、SqlSessionFactory、SqlSessionTemplate 等对象。MyBatis Spring Boot 自动配置文档和测试文档都明确提到这些组件会被自动配置。
十、第五阶段:Spring Boot 时代,YAML 接管大部分框架配置
这就是你记忆里的“以前 XML,后来 YAML”。
它的真相是:
1. SQL 映射文件 Mapper.xml 还在
复杂 SQL 仍然常常放 XML。
2. MyBatis 总配置、数据源配置很多被搬到 application.yml
官方 Spring Boot Starter 文档明确支持自动扫描 @Mapper,也支持通过配置和 @MapperScan 做更多控制。
典型 application.yml
1 | spring: |
这意味着什么
数据源交给 Spring Boot
Mapper 扫描交给 Starter
你还是可以保留 XML 写 SQL
只是“框架装配层”很多不用手写
mybatis-config.xml了
十一、第六阶段:Java DSL,MyBatis Dynamic SQL
这是 MyBatis 官方生态里非常重要的一步。
官方文档对这个库的定义很明确:它利用 MyBatis 负责安全执行 SQL、参数绑定、结果映射,而它自己则充当另一种动态 SQL 模板引擎。
也就是说,它把这件事:
在 XML 里写动态 SQL 模板
变成了:
在 Java 里用 DSL 构建动态 SQL
适合谁
字段多
条件多
想要类型安全
不想被 XML 套娃
官方还特别提到
这个库可同时用于 XML 和注解 Mapper,但总体推荐注解 Mapper;JOIN 结果映射常常仍需要 XML。
例子风格大概像这样
1 | SelectStatementProvider selectStatement = |
然后 Mapper 方法可以接这个 SelectStatementProvider 去执行。这个风格的意义在于:
条件组合靠 Java 链式表达
减少 XML 条件分支
更适合复杂筛选器
官方文档还有一整套 insert、update、select 的 provider 模式,且对 insert 官方甚至明确说通常不推荐 XML mapper 写法,更推荐注解式 provider。
十二、一个完整的“书写演进例子”
我给你用同一个需求串起来:
“按可选条件查询用户列表,支持 name、age、status、ids”
阶段 1:纯静态 XML,几乎不可维护
1 | <select id="listByName" resultType="User"> |
1 | <select id="listByAge" resultType="User"> |
1 | <select id="listByNameAndAge" resultType="User"> |
问题:组合爆炸。
阶段 2:动态 XML,主流做法
1 | <select id="listUsers" resultType="User"> |
优点:组合爆炸被控制住。
缺点:字段再多,XML 还是会越来越长。
阶段 3:注解版,适合简单查询
1 |
|
优点:简单快。
缺点:复杂 SQL 写起来难受。
阶段 4:Spring Boot 配置化
1 | spring: |
优点:框架配置集中。
缺点:SQL 的复杂度并没有减少,只是框架装配更轻松。
阶段 5:Java DSL,适合更复杂条件
1 | SelectStatementProvider selectStatement = |
优点:条件表达更程序化,更适合复杂筛选。
缺点:学习成本高一点,团队需要统一风格。
十三、你写笔记时可以这么总结“进化史”
1. 原生时代
mybatis-config.xml管总配置Mapper.xml管 SQLSqlSessionFactory创建SqlSessionSqlSession执行 SQL、拿 Mapper
这一套是 MyBatis 最原始也最完整的形态。
2. 动态 XML 时代
为了解决条件组合爆炸
引入
if、where、set、foreach等标签MyBatis 3 对动态标签做了简化,依赖 OGNL 表达式
这是 XML 方案最成熟的形态。
3. 注解 Mapper 时代
简单 SQL 可直接写在接口上
降低样板代码
但复杂 SQL 仍常常回到 XML
MyBatis 官方始终把 XML 和注解并列作为两种主流配置方式。
4. Spring / Spring Boot 整合时代
数据源、Mapper 扫描、会话模板自动配置
很多配置从
mybatis-config.xml转到application.yml但复杂 SQL 的
Mapper.xml并未消失
这就是你脑中“xml 变 yml”的来源。
5. Java DSL 时代
官方提供 MyBatis Dynamic SQL
让动态 SQL 从 XML 模板转向 Java 构建器
更适合复杂筛选和更强类型约束
这是官方生态对“XML 规则也会炸”这个问题的一种进阶回应。
十四、你笔记里最值得抄的一段结论
MyBatis 的本质
MyBatis 不是 ORM 全自动魔法,它更像一个以 SQL 为中心的持久层框架。它负责安全执行 SQL、参数绑定、结果映射,并提供 XML 动态标签或 Java DSL 来生成动态 SQL。
MyBatis 的演进主线
最早主要依赖 XML 映射 SQL,随后通过 动态 XML 解决条件组合问题;再后来支持 注解 Mapper,并在 Spring/Spring Boot 生态中把大量框架配置迁移到 application.yml;面对更复杂的动态查询,官方又提供了 MyBatis Dynamic SQL,把动态 SQL 构建从 XML 进一步推进到 Java DSL。
什么时候选什么
简单 CRUD:注解或简单 XML
中等复杂查询:动态 XML
非常复杂筛选:优先考虑拆业务,再考虑 Java DSL
联表结果映射复杂:XML 依然强势
这套选择逻辑和官方能力边界是吻合的。
十五、MyBatis 和 MyBatis Plus
MyBatis
传统写法是什么样
你得先自己写 Mapper 接口:
1 | public interface UserMapper { |
再自己写对应的 XML:
1 | <mapper namespace="com.example.mapper.UserMapper"> |
一句话说,它更像是:你自己写 SQL,框架负责执行、参数绑定和结果映射。
它更适合什么场景
多表
JOIN很复杂聚合统计很多
结果映射复杂
想精确控制 SQL 和执行细节
这类场景里,原生 MyBatis 仍然更稳,因为你对 SQL 的掌控是最直接的。
它和 MyBatis Plus 的关系怎么理解
MyBatis 是底盘,MyBatis Plus 是建立在它之上的增强层。
所以你可以把它们理解成:
MyBatis:保留原生 SQL 控制力
MyBatis Plus:把大量重复 CRUD 和中等复杂查询封装掉
更复杂的时候,还是可能回到:
自定义 SQL
注解 SQL
XML SQL
也就是说,MyBatis Plus 不是推翻 MyBatis,而是在 MyBatis 之上减少你写无聊 SQL 的次数。
MyBatis Plus
它为什么流行
因为你前面吐槽的点,正是它要解决的点:
单表 CRUD 重复代码很多
Service 层模板代码容易越写越机械
中等复杂查询总在 XML 里反复拼条件
MyBatis Plus 的核心价值,就是把这几类高频重复劳动压缩掉。
对应的写法会简化到什么程度
很多时候你只要定义:
1 | public interface UserMapper extends BaseMapper<User> { |
然后就能直接用:
1 | userMapper.selectById(1L); |
因为 BaseMapper 已经内置了很多通用 CRUD 方法,这就是它最核心的卖点之一。
它到底帮你省了什么
1. 少写 Mapper XML
很多基础操作直接不用自己写 SQL。官方 Quick Start 展示的典型方式,就是继承 BaseMapper<T>,然后直接用它提供的方法做常见 CRUD。
2. 少写 Service 层模板代码
官方文档里有 IService / ServiceImpl 这一套通用 Service 抽象,就是为了把很多重复服务层代码再压一层。
比如:
1 | public interface UserService extends IService<User> { |
实现类:
1 |
|
这样很多通用服务能力就能直接复用,不用每个业务都从零写一遍。
3. 条件查询不用手搓动态 XML
它提供了各种 Wrapper 条件构造器,能把很多 if + where + and + like 这种中等复杂度查询,从 XML 挪到 Java 链式调用里。
比如你想查:
age > 18
name like “Gil”
status = 1
可以写成:
1 | LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>(); |
Wrapper / LambdaQueryWrapper 是 MP 的核心使用形态之一。
为什么 LambdaQueryWrapper 很香
1 | User::getName |
比直接写字符串 "name" 更稳。字段改名时,IDE 重构更友好,不容易出现魔法字符串翻车。
4. 分页插件开箱即用
MyBatis Plus 提供分页支持,这也是它在业务开发里非常常见的原因。
大概用法像这样:
1 | Page<User> page = new Page<>(1, 10); |
如果纯手写 MyBatis,往往还要自己写分页 SQL,或者额外接 PageHelper 之类的东西;MP 把这块也顺手包掉了。
5. 注解配置更丰富
它支持很多常见注解,比如:
@TableName@TableId@TableField
这些注解用来做表名、主键、字段映射配置。
例子
1 |
|
这能解决:
类名和表名不一致
属性名和字段名不一致
主键策略配置
一个完整例子
实体类
1 |
|
Mapper
1 |
|
查询
按 id 查:
1 | User user = userMapper.selectById(1L); |
查全部:
1 | List<User> list = userMapper.selectList(null); |
条件查:
1 | LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>(); |
插入:
1 | User user = new User(); |
这个味道你应该能感觉到:大量简单 SQL 已经不需要自己写 XML 了。
它最适合什么
单表 CRUD 很多的项目
管理后台、基础业务表、字典表、用户表、角色表,这类场景特别舒服。
中等复杂度条件查询
eq、like、between、in、orderByDesc 这类组合,Wrapper 很顺手。
想快速起业务
尤其你这种要做项目、做后台、做可展示工程,MP 很适合提高开发速度。
它不那么香的地方
1. 特别复杂的多表联查
复杂 JOIN、复杂结果映射这类场景,仍然更依赖原生 MyBatis 的 XML 或手写 SQL。
2. 容易让人过度依赖“自动 CRUD”
很多人一上来什么都想用 Wrapper 和通用方法糊过去,最后 SQL 可读性反而变差。这不是 MP 本身的问题,是使用姿势的问题。
3. 复杂业务 SQL 还是得回到原生能力
比如:
多表
JOIN很复杂聚合统计很多
子查询多
特殊索引优化要求高
这时候你还是可能写 XML 或手写 SQL。MP 没把 SQL 本身的复杂性消灭,只是把简单和中等复杂度的重复劳动压缩了。
面试里怎么说比较像样
你可以直接背这个版本:
MyBatis Plus 是什么
MyBatis Plus 是基于 MyBatis 的增强工具,在不改变 MyBatis 核心的前提下,提供了通用 CRUD、条件构造器、分页、通用 Service 等能力,用来进一步简化持久层开发。
它的优点
减少单表 CRUD 样板代码
通过
BaseMapper提供通用数据库操作通过
Wrapper构造查询条件支持注解配置、分页插件等能力
它的局限
复杂多表联查和复杂统计场景下,仍然需要手写 SQL
过度依赖通用封装可能影响 SQL 可读性和可控性
这个说法挺稳,不会一吹就飞。