学习 MyBatis 的“收益”
你在 MyBatis 里学的东西,真的都是“收益”吗?
MyBatis 官网、文档、社区都在告诉你:它帮你解决了 SQL 和 Java 的分离问题、动态 SQL 问题、结果映射问题。它有一整套体系:Mapper 接口、XML 配置、OGNL 表达式、几十个标签、上百个属性、十几个注解……
你学会这些东西,就能在 XML 里拼字符串了。
但这些问题,Spring JDBC 解决不了吗?
答案是:Spring JDBC 本来就能做,而且不需要你额外学任何东西。
下面这张表,列出了你为了在 XML 里拼一个字符串,需要学习的所有“收益”。
一、为了“挂接”SQL,你学到的收益
| MyBatis 概念 | 它的作用 | 这个“收益”是真实的吗? |
|---|---|---|
| Mapper 接口 | 定义方法,XML 里的 id 要跟方法名一致 | Spring JDBC 里你直接写方法,不需要这个接口。 |
| XML 文件 | 存放 SQL 的地方 | Spring JDBC 里 SQL 就在 Java 里,不需要另一个文件。 |
| Namespace | 区分不同 XML 文件里的同名 id | Spring JDBC 没有这个问题,方法名天然唯一。 |
| XML 文件头 | <!DOCTYPE...> 那一大坨 |
没有 XML,就不需要这个头。 |
| 接口 ID ↔ XML id 映射 | 手工维护两个文件的一致性 | Spring JDBC 里方法名就是方法名。 |
#{} vs ${} |
一个参数化查询,一个直接拼接 | Spring JDBC 里只有 ?,全参数化。 |
@Param 注解 |
给方法参数起别名,供 XML 引用 | Spring JDBC 里参数名就是参数名。 |
@Mapper / @MapperScan |
让 Spring 识别这个接口 | Spring JDBC 里直接注入 JdbcTemplate。 |
这一组的真实收益:把本来在一个文件里能写完的东西,拆成两个文件,然后你必须同时维护它们的一致性。
二、为了“动态拼接 SQL”,你学到的收益
| MyBatis 概念 | 它的作用 | 这个“收益”是真实的吗? |
|---|---|---|
<if> 标签 |
条件判断 | Java 里 if 就能做,不需要学 XML 标签。 |
<where> 标签 |
自动处理第一个 AND | 这个逻辑你自己写,三行代码。 |
<set> 标签 |
自动处理 UPDATE 末尾的逗号 | 这个逻辑你自己写,三行代码。 |
<foreach> 标签 |
遍历集合拼 IN 条件 | Java 里 for 循环就能做。 |
<choose> / <when> / <otherwise> |
多路分支 | Java 里 if-else 就能做。 |
<trim> 标签 |
去掉前缀/后缀 | Java 里 replaceAll 或 substring 就能做。 |
<bind> 标签 |
在 XML 里定义变量 | Java 里直接定义变量就行了。 |
| OGNL 表达式 | test、item、index、collection |
你学 Java 已经学了表达式,现在还要再学一个 OGNL 的变种。 |
这一组的真实收益:把 Java 里的 if、for、else 替换成 XML 里的标签,换了个地方写同样的逻辑。
三、为了“控制 SQL 执行细节”,你学到的收益
| MyBatis 概念 | 它的作用 | 这个“收益”是真实的吗? |
|---|---|---|
fetchSize |
控制 JDBC 每次取多少行 | Spring JDBC 里也有,直接设置。 |
timeout |
SQL 执行超时时间 | Spring JDBC 里也有,直接设置。 |
flushCache |
清空二级缓存 | Spring JDBC 没有二级缓存,不需要。 |
useCache |
是否使用二级缓存 | Spring JDBC 没有二级缓存,不需要。 |
statementType |
STATEMENT / PREPARED / CALLABLE |
Spring JDBC 的方法名已经区分了。 |
resultSetType |
FORWARD_ONLY / SCROLL_INSENSITIVE / SCROLL_SENSITIVE |
Spring JDBC 里也有对应的设置。 |
这一组的真实收益:把本可以直接调用的 JDBC 参数配置,塞进 XML 的属性里。
四、为了“映射结果”,你学到的收益
| MyBatis 概念 | 它的作用 | 这个“收益”是真实的吗? |
|---|---|---|
resultType |
自动映射到普通 Java 类 | Spring JDBC 里 RowMapper 一行代码。 |
resultMap |
手动配置字段映射 | Spring JDBC 里 RowMapper 直接按字段名匹配。 |
<id> 标签(在 resultMap 中) |
标记主键字段 | Spring JDBC 不需要区分主键。 |
<association> |
一对一关联映射 | Spring JDBC 里自己处理结果集组装。 |
<collection> |
一对多关联映射 | Spring JDBC 里自己处理结果集组装。 |
@Results / @Result |
在接口方法上配置映射 | Spring JDBC 不需要。 |
@One |
一对一关联 | 本质上是再发一次查询,N+1 的源头之一。 |
@Many |
一对多关联 | 本质上是再发一次查询,N+1 的源头之一。 |
@MapKey |
指定 Map 结果的 key 字段 | Spring JDBC 里自己组装 Map。 |
@Options |
配置 useGeneratedKeys、fetchSize 等 |
跟 XML 属性完全重叠,换了个地方写。 |
@ResultType |
指定返回类型 | 方法签名已经决定了返回类型。 |
这一组的真实收益:把本来可以在 RowMapper 里直接处理的逻辑,拆成了 XML 配置或注解配置。
五、为了“配置 MyBatis 本身”,你学到的收益
| MyBatis 概念 | 它的作用 | 这个“收益”是真实的吗? |
|---|---|---|
mybatis-config.xml |
主配置文件 | Spring JDBC 没有单独的配置文件。 |
settings |
几十个配置项 | Spring JDBC 没有这些配置项。 |
typeAliases |
包扫描,省略 XML 里的全限定类名 | Spring JDBC 直接用类名,不需要别名。 |
plugins |
拦截器配置 | Spring JDBC 用 AOP,不需要学拦截器。 |
environments |
环境配置 | Spring Boot 的 application.yml 已经做了。 |
| DatabaseIdProvider | 多数据库方言切换 | Spring JDBC 直接写 SQL,不需要单独配置。 |
<databaseId> |
为不同数据库写不同版本的 SQL | Spring JDBC 里你用一个变量判断就行了。 |
这一组的真实收益:多了一份配置文件,多了一堆配置项,多了一整套扩展机制的学习成本。
六、为了“定位错误”,你学到的收益
| MyBatis 概念 | 它的作用 | 这个“收益”是真实的吗? |
|---|---|---|
BindingException |
参数绑定出问题了 | Spring JDBC 里没有这种异常,参数直接绑定。 |
ExecutorException |
执行器出问题了 | Spring JDBC 里没有这种异常。 |
TooManyResultsException |
预期一条但查出了多条 | Spring JDBC 里 queryForObject 会抛 IncorrectResultSizeDataAccessException,但异常信息更清晰。 |
ReflectionException |
反射出问题了 | Spring JDBC 里不会因为反射失败而抛出这种异常。 |
CacheException |
缓存出问题了 | Spring JDBC 没有缓存。 |
PersistenceException |
持久层通用异常 | Spring JDBC 直接抛 DataAccessException。 |
| 总计 31 类异常(MyBatis 官方源码统计) | 你需要学习每一种异常的出处和含义 | Spring JDBC 只有 3 类 核心异常。 |
这一组的真实收益:你花时间调试的不是“数据库出了什么问题”,而是“框架的哪一层又炸了”。
你学完这些“收益”,最终实现的是什么?
就是在 XML 里拼一个字符串。
- Mapper 接口 + XML 文件 + namespace + XML 头 + 接口 ID 映射 +
@Param→ 为了把 SQL 放进 XML - 几十个标签 + OGNL 表达式 → 为了在 XML 里做动态条件判断
- 上百个属性 → 为了控制 XML 里 SQL 的执行细节
- 十几个注解 → 为了在接口上标注 XML 对应的配置
- 几十个异常类 → 为了定位 XML 拼字符串时出的错
mybatis-config.xml+ settings + plugins + typeAliases → 为了配好环境让 XML 能正常运行
所有这些概念,解决的是同一个问题:在 XML 里拼一个字符串。但你在 Java 里本来就能拼。
你用 Spring JDBC,需要的只是一个工具类
如果你把条件拼接、参数管理、分页逻辑封装成几个方法:
addWhere():拼接条件 + 自动收集参数addIf():条件判断 + 自动决定是否拼接page():分页 + 自动处理 COUNT 和 LIMIT
全工程复用,覆盖 90% 以上的业务场景。
你不再需要:
- Mapper 接口 → 方法就在类里,不需要额外接口
- XML 文件 → SQL 就是字符串
- namespace → 类名和方法名天然唯一
- XML 头 → 没有 XML 就没有头
#{}vs${}→ 只有?resultType/resultMap→RowMapper直接处理- 几十个标签 → Java 的
if已经够用 - 上百个属性 → 方法参数直接设置
- 十几个注解 → 什么都不需要
- 几十个异常 → 数据库错误就是
SQLException - 拦截器 → Spring AOP 就能扩展
三个方法,解决 90% 的问题。剩下的 10%,本来就要手写 SQL,不关工具的事。
这个工具类已经有了现成的
它叫 SimpleDAO。
它在 Spring JDBC 的基础上,做了这些事情:
| 你原本需要手写的事 | SimpleDAO 做了什么 |
|---|---|
| 条件拼接 + 参数管理 | add()、and()、in()、notIn() 一行搞定,参数自动收集,空值自动跳过 |
| 多条件组合 + 跨位置参数合并 | mergeParams(c1, c2, c3) 自动合并多个条件类的参数,顺序与 SQL 中 ? 一致,无需手动维护 Object[] |
| 模糊查询转义 | add("AND u.name LIKE ?", name, 3),site 控制 % 位置(左/右/前后),自动转义 % 和 _,防 SQL 注入 |
| IN 条件 | in("id", ids),数组自动展开成 (?,?,?),参数自动收集 |
| 无参数 SQL 片段 + 动态开关 | add("AND dr = 0")、add("AND status = 1", flag),带布尔参数控制是否拼接 |
| 单表 CRUD | 继承 BaseDao<T>,空类即得 save、update、delete、findById、list、page、count、exists 等 20+ 方法,零代码、零 SQL |
| 差异更新 | update(T) 不更新 null 字段,updateNull(T) 更新 null 字段,按需选择 |
| 批量操作 | saveBatch(list)、replaceBatch(list),底层复用数据库驱动批处理能力 |
| 自动分页 | page(cond) 一行搞定,自动生成 COUNT SQL,自动计算 LIMIT/OFFSET,返回 Page<T> 含数据列表 + 总条数 + 总页数;极端复杂 SQL 用 page0() 兜底(子查询法) |
| 主键自动生成 | @Id("snow") 雪花算法、@Id("uuid") UUID、@Id("auto") 数据库自增、@Id("custom") 自定义,插入时自动赋值 |
| 审计字段自动填充 | save() 自动填充 createTime / createBy / dr,update() 自动填充 updateTime / updateBy |
| 逻辑删除 | delete() 自动判断表是否有 dr 字段,有则 UPDATE SET dr=1,无则 DELETE FROM |
| 结果集映射 | 查询结果自动映射到实体/VO,驼峰 ↔ 下划线自动转换,无需 XML 配置 |
| 联表查询 | SQL 完整手写,支持 JOIN、子查询、UNION、CTE、窗口函数、聚合统计——数据库能写啥,它就能跑啥,SQL 直接复制到客户端可执行 |
| 复杂报表 | 原生 SQL + BaseCondition 条件拼接,mergeParams 多条件合并,报表开发效率提升 50% 以上 |
| 数据权限 | Spring AOP 切面 + cond.setExtendCondition(and) 一行注入权限条件,无侵入、零框架依赖 |
| 字段脱敏 | Spring AOP 切面在 Service 层处理,不污染持久层 |
| SQL 日志调试 | 自动打印带参数的完整 SQL,字符串/日期自动加引号,复制即可在数据库客户端执行,无需手动替换占位符 |
| 异常处理 | 数据库原生异常直接透传,不封装、不转换、不自造 31 类框架异常 |
| 原生能力保留 | 危机时刻可直接调用 JdbcTemplate 或 NamedParameterJdbcTemplate,框架不挡路 |
| 框架存在感 | < 5%,业务代码浓度 > 95%,没有 XML、没有 OGNL、没有 Mapper 接口、没有 SqlSession |
SimpleDAO 没有替换 Spring JDBC,它只是把你本来要手写的样板代码自动化了。
开源地址
- 核心框架:https://gitee.com/gao_zhenzhong/simple-dao
- 系统底座:https://gitee.com/gao_zhenzhong/simple-dao-starter
- 代码生成器:https://gitee.com/gao_zhenzhong/simple-dao-coder
- 实战案例:https://gitee.com/gao_zhenzhong/simple-dao-demo
写在最后
MyBatis 的“收益”,加引号是认真的。因为你学了一大堆东西,得到的只是在 XML 里拼一个字符串。
SimpleDAO 的收益,不加引号也是认真的。因为它什么都没让你多学,只是帮你省掉了重复代码。
去 Gitee 拉一个 Demo,跑起来,看看代码量差多少,然后自己算账。
更多推荐




所有评论(0)