mybatis vs mybatis-plus
核心关系: MyBatis-Plus 不是 MyBatis 的替代品,而是构建在 MyBatis 之上的一个强大的增强工具包。它完全兼容原生 MyBatis,并在其基础上提供了大量开箱即用的功能,极大地简化了开发,尤其是单表操作。
一、 MyBatis 核心原理
MyBatis 的核心思想是将 SQL 语句从 Java 代码中解耦,通过配置或注解来管理 SQL,并提供灵活的 ORM 映射。其核心原理围绕以下几个关键组件和流程:
SqlSessionFactoryBuilder
&SqlSessionFactory
:- 作用: 应用启动时,读取 MyBatis 的全局配置文件 (
mybatis-config.xml
) 和所有的 Mapper XML 文件(或注解配置)。 - 原理:
SqlSessionFactoryBuilder
解析这些配置文件,构建出包含所有配置信息(数据源、事务管理器、类型处理器、插件、Mapper 注册信息等)的Configuration
对象,并最终创建出SqlSessionFactory
。SqlSessionFactory
是生产SqlSession
的工厂。
- 作用: 应用启动时,读取 MyBatis 的全局配置文件 (
SqlSession
:- 作用: 代表一次与数据库的会话。它是 MyBatis 工作的核心入口。
- 原理: 通过
SqlSessionFactory.openSession()
获取。它提供了执行 SQL 命令(selectOne
,selectList
,insert
,update
,delete
)、获取 Mapper 接口代理对象、管理事务等方法。每个SqlSession
通常对应一个数据库连接(或连接池中的一个连接)。
Executor
:- 作用:
SqlSession
内部真正执行 SQL 操作的组件。 - 原理:
SqlSession
将请求委托给Executor
。Executor
负责维护一级缓存(SqlSession
级别)、处理延迟加载、调用StatementHandler
等。常见的实现有SimpleExecutor
(每次执行创建新Statement
)、ReuseExecutor
(重用Statement
)、BatchExecutor
(批处理)。
- 作用:
StatementHandler
:- 作用: 负责创建 JDBC
Statement
对象(PreparedStatement
,CallableStatement
,Statement
),并设置参数、执行 SQL。 - 原理:
Executor
调用StatementHandler
。它使用ParameterHandler
将 Java 对象参数设置到 SQL 的占位符 (?
) 上。
- 作用: 负责创建 JDBC
ParameterHandler
:- 作用: 负责将用户传入的 Java 参数,按照 SQL 语句中的占位符 (
?
) 进行类型转换和设置。 - 原理: 利用注册的
TypeHandler
完成 Java 类型到 JDBC 类型的转换。
- 作用: 负责将用户传入的 Java 参数,按照 SQL 语句中的占位符 (
TypeHandler
:- 作用: 负责 Java 类型和 JDBC 类型之间的相互转换。
- 原理: MyBatis 内置了常用类型(如 String, Integer, Date 等)的处理器。用户可以自定义处理器处理特殊类型(如枚举)。
ResultSetHandler
:- 作用: 负责将 JDBC
ResultSet
返回的结果集转换为指定的 Java 对象(单个对象、List、Map 等)。 - 原理: 根据 Mapper 方法定义的返回类型(或
resultMap
配置),利用TypeHandler
和反射,将结果集的每一行数据映射成 Java 对象。
- 作用: 负责将 JDBC
MappedStatement
:- 作用: 代表一个映射的 SQL 语句。它是 MyBatis 配置的核心单元。
- 原理: 存储了 SQL 语句本身(
<select>
,<insert>
等标签的内容)、参数映射信息 (<parameterMap>
或@Param
)、结果映射信息 (<resultMap>
或@Result
)、SQL 命令类型(SELECT/INSERT 等)、缓存配置等。一个 Mapper 接口方法对应一个MappedStatement
。
配置方式:
- XML 配置: 主要方式。通过
mybatis-config.xml
配置全局设置,通过Mapper.xml
文件定义 SQL 和映射关系。 - 注解配置: 在 Mapper 接口方法上使用
@Select
,@Insert
,@Update
,@Delete
,@Results
等注解直接编写 SQL 和映射规则。通常用于简单 SQL。
- XML 配置: 主要方式。通过
MyBatis 工作流程总结:
- 应用启动:解析配置 -> 构建
Configuration
-> 创建SqlSessionFactory
。 - 业务请求:
SqlSessionFactory
-> 创建SqlSession
。 - 执行操作:
- 通过
SqlSession
直接执行 SQL 方法 (selectOne
,update
等)。 - 或通过
SqlSession.getMapper()
获取 Mapper 接口的动态代理对象。
- 通过
- 代理对象调用:代理对象根据方法名找到对应的
MappedStatement
。 Executor
执行:Executor
根据MappedStatement
信息,调用StatementHandler
创建Statement
。ParameterHandler
设置参数。StatementHandler
执行 SQL。ResultSetHandler
处理结果集,映射为 Java 对象。- 返回结果。
优点: 灵活、SQL 可控性强、与 JDBC 解耦、支持复杂映射、插件扩展性强。
缺点: 需要编写大量 SQL 和 XML/注解(尤其是简单 CRUD)、配置繁琐。
二、 MyBatis-Plus (MP) 核心原理
MP 的核心原理是在 MyBatis 原有架构和机制的基础上,通过一系列封装、扩展和自动化技术,极大地简化单表操作和常用功能开发。它没有改变 MyBatis 的核心流程,而是增强了它。
核心增强点:
BaseMapper
与SqlInjector
/AbstractMethod
- 原理: MP 的核心创新。用户 Mapper 接口只需继承
BaseMapper
。在应用启动时,MP 的SqlInjector
(如DefaultSqlInjector
) 会扫描这些 Mapper。 - 动态 SQL 生成: 对于
BaseMapper
中的每个方法(insert
,selectById
,update
等),MP 都有一个对应的AbstractMethod
子类(如Insert
,SelectById
,Update
)。这些子类在启动时,利用实体类的元数据 (TableInfo
,由@TableName
,@TableId
等注解解析而来) 和预定义的 SQL 模板 (SqlMethod
),动态生成该方法的完整 SQL 语句和MappedStatement
对象,并注册到 MyBatis 的Configuration
中。 - 结果: 开发者无需为这些通用方法编写任何 SQL 或 XML,MP 自动提供实现。
- 原理: MP 的核心创新。用户 Mapper 接口只需继承
强大的条件构造器 (
Wrapper
)- 原理: 提供
QueryWrapper
,UpdateWrapper
,LambdaQueryWrapper
等。用户通过链式调用构建复杂的查询/更新条件(eq
,like
,between
,set
等)。 - SQL 拼接:
Wrapper
内部将条件表达式存储为结构化的数据。当Wrapper
被用于查询或更新方法时,MP 的 SQL 生成引擎会安全地(防止 SQL 注入)将这些条件解析并拼接到自动生成的基础 SQL 的WHERE
子句或SET
部分。
- 原理: 提供
自动配置与 Starter (Spring Boot)
- 原理: 提供
mybatis-plus-boot-starter
。利用 Spring Boot 自动配置机制,自动创建和配置DataSource
,SqlSessionFactoryBean
(注入 MP 的核心组件如GlobalConfig
,Interceptor
),MapperScannerConfigurer
等,大大简化了集成步骤。
- 原理: 提供
插件体系增强
- 原理: 继承并扩展 MyBatis 的
Interceptor
接口,提供功能更强大、开箱即用的内置插件:PaginationInnerInterceptor
: 自动处理物理分页(生成 COUNT 查询和分页 SQL)。OptimisticLockerInnerInterceptor
: 自动实现乐观锁(基于@Version
注解)。BlockAttackInnerInterceptor
: 防止全表更新/删除(安全防护)。DynamicTableNameInnerInterceptor
: 动态表名。IllegalSQLInnerInterceptor
: SQL 性能分析/阻止恶意 SQL。
- 工作方式: 这些插件在 MyBatis 的执行流程(主要是
Executor
阶段)进行拦截,添加额外功能。
- 原理: 继承并扩展 MyBatis 的
全局配置与元数据处理
GlobalConfig
: 存储全局配置(表前缀、主键生成器策略、元对象处理器等)。MetaObjectHandler
: 实现自动填充功能(@TableField(fill = ...)
)。在插入或更新时,通过反射自动填充字段(如createTime
,updateTime
)。
注解驱动增强
- 原理: 在 MyBatis 注解基础上,提供更丰富的 ORM 和功能注解:
@TableName
: 指定表名。@TableId
: 指定主键及策略(自增、UUID、雪花ID等)。@TableField
: 指定字段映射、自动填充策略、是否存在等。@Version
: 乐观锁版本字段。@EnumValue
: 枚举值映射。@TableLogic
: 逻辑删除标记。@SqlParser
: 过滤 SQL 解析(用于跳过某些拦截器)。
- 这些注解信息在启动时被解析并存储在
TableInfo
等元数据对象中,供 SQL 生成、条件构造、插件等使用。
- 原理: 在 MyBatis 注解基础上,提供更丰富的 ORM 和功能注解:
MP 工作流程总结:
- 启动阶段:
- 自动配置(Spring Boot Starter)。
- 扫描实体类注解,构建
TableInfo
等元数据。 - 扫描 Mapper 接口(继承
BaseMapper
)。 SqlInjector
为BaseMapper
的每个方法注入对应的AbstractMethod
实现。AbstractMethod
子类利用元数据和 SQL 模板,动态生成MappedStatement
并注册。- 配置并添加 MP 的内置插件到
InterceptorChain
。
- 运行时阶段:
- 与原生 MyBatis 流程基本相同(
SqlSession
->Executor
-> ...)。 - 当调用
BaseMapper
方法时,执行的是 MP 动态生成的MappedStatement
。 - 当使用
Wrapper
时,MP 在生成 SQL 时会解析并拼接Wrapper
的条件。 - MP 的插件在相应执行点进行拦截,提供分页、乐观锁等功能。
- 与原生 MyBatis 流程基本相同(
优点: 极大简化 CRUD、减少 SQL/XML 编写、内置强大功能(分页、代码生成器、乐观锁、逻辑删除等)、增强条件构造、提高开发效率。
缺点: 对复杂 SQL 和多表关联查询的支持不如直接手写 SQL/XML 灵活(虽然也能用),需要学习 MP 特有的 API 和注解。
三、 MyBatis vs MyBatis-Plus 核心对比总结
特性 | MyBatis | MyBatis-Plus (MP) | 说明 |
---|---|---|---|
定位 | 灵活的 SQL 映射框架 | MyBatis 的增强工具包 | MP 基于 MyBatis,不是替代品。 |
核心目标 | SQL 与代码解耦,灵活控制 SQL | 简化开发,尤其是单表 CRUD | MP 专注于减少样板代码。 |
SQL 编写 | 必需 (XML 或注解) | 可选 (对单表 CRUD 自动生成) | MP 的 BaseMapper 自动提供通用方法实现。复杂 SQL 仍需手写。 |
CRUD 实现 | 手动编写每个 CRUD 方法的 SQL 和映射 | 自动实现 BaseMapper 中的通用 CRUD 方法 | MP 的核心优势,省去大量简单 SQL 编写。 |
条件构造 | 手动拼接 SQL 条件字符串 (易错,不安全) | 强大的 Wrapper API (链式调用,类型安全 - LambdaWrapper,防注入) | MP 提供面向对象的、安全的条件构建方式。 |
分页 | 需手动编写分页 SQL (不同数据库语法不同) | 内置 PaginationInterceptor (自动物理分页) | MP 的分页插件自动处理不同数据库方言、COUNT 查询和数据获取。 |
主键生成 | 需在 SQL 或配置中处理 | 支持多种策略 (@TableId ),内置分配器 (雪花ID等) | MP 简化了主键管理。 |
逻辑删除 | 需手动在 SQL 中添加条件 | 内置支持 (@TableLogic ),自动添加过滤条件 | MP 自动在查询/更新中处理逻辑删除字段。 |
乐观锁 | 需手动实现版本控制逻辑 | 内置支持 (@Version ),通过插件自动处理 | MP 简化了乐观锁实现。 |
自动填充 | 需手动在代码中设置 | 内置支持 (@TableField(fill) + MetaObjectHandler ) | MP 自动填充如创建时间、更新时间等字段。 |
代码生成器 | 无官方提供,需用第三方或自研 | 提供功能强大的 代码生成器 | MP 可快速生成 Entity, Mapper, Service, Controller 等基础代码。 |
安全防护 | 无内置 | 提供 BlockAttackInnerInterceptor 等安全插件 | MP 防止全表更新/删除等危险操作。 |
配置复杂度 | 相对较高 (需配置较多 XML/注解) | 显著降低 (尤其结合 Spring Boot Starter) | MP 的自动配置和默认行为减少了大量配置。 |
学习曲线 | 掌握 SQL/XML 映射和核心组件即可 | 需额外学习 MP 特有的 API (Wrapper , BaseMapper ), 注解和插件 | MP 增加了自己的抽象层。 |
灵活性 | 极高,完全掌控 SQL | 对单表操作灵活,对复杂 SQL/多表 灵活性相对降低 | MP 处理复杂场景时,有时仍需回退到原生 MyBatis 方式 (手写 SQL/XML)。 |
适用场景 | 需要极致 SQL 控制、复杂查询、存储过程调用 | 快速开发、大量单表操作、需要内置功能 (分页/乐观锁等) | 两者并非互斥,可在同一项目中结合使用。 |
总结:
- MyBatis: 是基础,提供了灵活、强大的 SQL 映射能力,将开发者从 JDBC 的繁琐中解放出来,但需要编写大量 SQL 和配置。
- MyBatis-Plus: 是增强,在 MyBatis 的基础上,通过自动化生成、强大的条件构造器、丰富的内置功能(分页、代码生成、乐观锁等)和便捷的配置(特别是 Spring Boot),极大地提升了开发效率,尤其擅长处理单表操作。它保留了 MyBatis 的灵活性,对于复杂场景,你仍然可以像使用原生 MyBatis 一样编写自定义 SQL。
选择建议:
- 如果你的项目有大量简单的单表 CRUD 操作,追求开发效率,且需要分页、乐观锁等常见功能,MyBatis-Plus 是绝佳选择。
- 如果你的项目极其复杂,涉及大量定制化 SQL、存储过程、复杂的多表关联和嵌套查询,或者你对 SQL 有绝对的控制欲,原生 MyBatis 可能更合适(或者结合使用,复杂部分用原生)。
- 实际项目中,两者结合使用非常常见: 简单 CRUD 用 MP 的
BaseMapper
和Wrapper
,复杂查询或存储过程调用则使用原生 MyBatis 的 XML 或注解方式编写 SQL。MP 的@SqlParser(filter=true)
可以让你在同一个 Mapper 中混合使用这两种方式。