MyBatis-Plus(简称 MP)是一个基于 MyBatis 的增强工具,它在 MyBatis 的基础上封装了许多便捷功能,比如自动生成 SQL、条件构造器(Wrapper)等,让开发者可以更高效地完成数据库操作。本文将由浅入深,带你了解 MyBatis-Plus 的常用功能、底层原理以及核心实现。
一、MyBatis-Plus 的常用 Wrapper 和 API
在 MyBatis-Plus 中,Wrapper
是条件构造器的核心,用于动态生成 SQL 的 WHERE 条件。它分为几种类型,常用的是 QueryWrapper
和 UpdateWrapper
。下面我们通过例子来介绍它们的用法。
1. QueryWrapper:查询条件构造器
QueryWrapper
用于构建查询条件,比如筛选、排序等。
java
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.eq("age", 25) // WHERE age = 25
.like("name", "张") // WHERE name LIKE '%张%'
.orderByDesc("id"); // ORDER BY id DESC
List<User> users = userMapper.selectList(wrapper);
eq
:等于(=)like
:模糊查询(LIKE)orderByDesc
:降序排序
2. UpdateWrapper:更新条件构造器
UpdateWrapper
用于构建更新语句的条件和字段。
java
UpdateWrapper<User> wrapper = new UpdateWrapper<>();
wrapper.eq("id", 1) // WHERE id = 1
.set("age", 26); // SET age = 26
userMapper.update(null, wrapper);
set
:设置要更新的字段值
3. 其他常用 API
selectOne
:查询单条记录selectPage
:分页查询(需要配置分页插件)deleteById
:根据 ID 删除
这些 API 简单易用,背后却隐藏着复杂的实现逻辑。接下来,我们深入分析 MyBatis-Plus 的原理。
二、MyBatis-Plus 的原理:如何基于 MyBatis 封装?
MyBatis-Plus 并不是从零开始写的,它是站在 MyBatis 的肩膀上,通过扩展和增强实现的。那么,它具体利用了 MyBatis 的哪些内容呢?
1. MyBatis 的核心流程回顾
MyBatis 的核心是基于 XML 或注解定义 SQL,通过 SqlSession
执行。它的执行流程如下:
- 解析配置 :加载
mybatis-config.xml
和 Mapper 文件,生成Configuration
对象。 - 创建 SqlSession :通过
SqlSessionFactory
创建会话。 - 执行 SQL:通过 Mapper 接口的代理对象(基于 JDK 动态代理)调用 SQL。
2. MyBatis-Plus 的增强点
MyBatis-Plus 在 MyBatis 的基础上做了以下封装:
- 自动注入 SQL:通过拦截器和反射,动态生成增删改查的 SQL。
- Mapper 增强 :提供通用 Mapper 接口(如
BaseMapper
),无需手动写 SQL。 - 条件构造器:通过 Wrapper 类动态拼接 WHERE 条件。
核心依赖:
- SqlSessionFactory:MyBatis-Plus 重用了 MyBatis 的会话工厂。
- Mapper 代理:利用 MyBatis 的动态代理机制,增强了 Mapper 的功能。
- 拦截器 :通过 MyBatis 的
Interceptor
机制,注入自定义逻辑。
源码分析(以 BaseMapper
为例):
java
public interface BaseMapper<T> {
List<T> selectList(@Param("ew") Wrapper<T> queryWrapper);
}
BaseMapper
是一个通用接口,MyBatis-Plus 通过 MapperProxy
(MyBatis 的代理类)拦截方法调用,然后根据方法名和参数生成 SQL。
三、MyBatis-Plus 是怎么封装的?
MyBatis-Plus 的封装主要依赖以下技术:
1. 动态 SQL 注入
MyBatis-Plus 通过 ISqlInjector
接口实现 SQL 的自动生成。以 Insert
方法为例:
java
public class DefaultSqlInjector extends AbstractSqlInjector {
@Override
public List<AbstractMethod> getMethodList(Class<?> mapperClass) {
return Arrays.asList(
new Insert(), // 插入方法
new Delete(), // 删除方法
new Update(), // 更新方法
new SelectList() // 查询方法
);
}
}
Insert
类会根据实体类的字段,生成INSERT INTO table (...) VALUES (...)
。- 这些方法在启动时被注入到 Mapper 中。
2. 拦截器机制
MyBatis-Plus 使用 MyBatis 的拦截器(如 ExecutorInterceptor
)在 SQL 执行前动态修改。例如,分页插件会拦截 Executor
,在 SQL 中加入 LIMIT
。
源码片段:
java
@Intercepts({@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})})
public class PaginationInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
// 修改 SQL,添加分页逻辑
}
}
3. 依赖 MyBatis 的 Configuration
MyBatis-Plus 重用了 MyBatis 的 Configuration
对象,存储 Mapper 和 SQL 的映射关系。
四、Wrapper 是怎么实现的?
Wrapper
是 MyBatis-Plus 的核心功能之一,它通过链式调用生成 SQL 条件。让我们结合源码看看它的实现。
1. Wrapper 的类结构
Wrapper
是一个抽象类,QueryWrapper
和 UpdateWrapper
继承自 AbstractWrapper
。
java
public abstract class Wrapper<T> {
public abstract String getSqlSegment(); // 获取生成的 SQL 片段
}
2. 条件拼接逻辑
以 eq
方法为例:
java
public class QueryWrapper<T> extends AbstractWrapper<T, String, QueryWrapper<T>> {
@Override
public QueryWrapper<T> eq(String column, Object val) {
super.addCondition(true, column, SqlKeyword.EQ, val);
return this;
}
}
public abstract class AbstractWrapper<T, R, C extends AbstractWrapper<T, R, C>> {
protected final List<String> sqlSegments = new ArrayList<>();
protected void addCondition(boolean condition, R column, SqlKeyword sqlKeyword, Object val) {
if (condition) {
sqlSegments.add(column + " " + sqlKeyword.getSqlSegment() + " " + val);
}
}
}
eq("age", 25)
会生成age = 25
。sqlSegments
是一个 List,存储所有条件,最后通过getSqlSegment()
拼接成完整的 WHERE 子句。
3. SQL 生成
当调用 selectList(wrapper)
时,MyBatis-Plus 会:
- 获取
Wrapper
的sqlSegments
。 - 拼接成
WHERE age = 25 AND name LIKE '%张%'
。 - 通过 MyBatis 的
MappedStatement
执行。
源码片段:
java
public class SelectList extends AbstractMethod {
@Override
public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
String sql = "SELECT * FROM " + tableInfo.getTableName() + " ${ew.sqlSegment}";
return addSelectMappedStatement(mapperClass, "selectList", sql, modelClass);
}
}
${ew.sqlSegment}
是 Wrapper 生成的条件,动态嵌入 SQL。
五、总结
MyBatis-Plus 通过以下方式增强了 MyBatis:
- 通用 Mapper:减少重复代码。
- 动态 SQL:通过反射和注入实现。
- Wrapper:链式调用生成条件,简单又强大。
从源码看,它充分利用了 MyBatis 的代理、拦截器和配置体系,既保留了灵活性,又提升了开发效率。希望这篇文章能帮你从入门到深入理解 MyBatis-Plus!