MP:从Wrapper到源码分析

MyBatis-Plus(简称 MP)是一个基于 MyBatis 的增强工具,它在 MyBatis 的基础上封装了许多便捷功能,比如自动生成 SQL、条件构造器(Wrapper)等,让开发者可以更高效地完成数据库操作。本文将由浅入深,带你了解 MyBatis-Plus 的常用功能、底层原理以及核心实现。


一、MyBatis-Plus 的常用 Wrapper 和 API

在 MyBatis-Plus 中,Wrapper 是条件构造器的核心,用于动态生成 SQL 的 WHERE 条件。它分为几种类型,常用的是 QueryWrapperUpdateWrapper。下面我们通过例子来介绍它们的用法。

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 执行。它的执行流程如下:

  1. 解析配置 :加载 mybatis-config.xml 和 Mapper 文件,生成 Configuration 对象。
  2. 创建 SqlSession :通过 SqlSessionFactory 创建会话。
  3. 执行 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 是一个抽象类,QueryWrapperUpdateWrapper 继承自 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 会:

  1. 获取 WrappersqlSegments
  2. 拼接成 WHERE age = 25 AND name LIKE '%张%'
  3. 通过 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:

  1. 通用 Mapper:减少重复代码。
  2. 动态 SQL:通过反射和注入实现。
  3. Wrapper:链式调用生成条件,简单又强大。

从源码看,它充分利用了 MyBatis 的代理、拦截器和配置体系,既保留了灵活性,又提升了开发效率。希望这篇文章能帮你从入门到深入理解 MyBatis-Plus!

相关推荐
自珍JAVA22 分钟前
Gobrs-Async 框架
后端
xdscode28 分钟前
Spring 依赖注入方式全景解析
java·后端·spring
青柠代码录36 分钟前
【Spring】@Component VS @Configuration
后端
喵个咪1 小时前
go-wind-cms 微服务架构设计:为什么基于 Kratos?
后端·微服务·cms
神奇小汤圆1 小时前
百度面试官:Redis 内存满了怎么办?你有想过吗?
后端
喵个咪1 小时前
Headless 架构优势:内容与展示解耦,一套 API 打通全端生态
前端·后端·cms
开心就好20252 小时前
HTTPS超文本传输安全协议全面解析与工作原理
后端·ios
小江的记录本2 小时前
【JEECG Boot】 JEECG Boot——数据字典管理 系统性知识体系全解析
java·前端·spring boot·后端·spring·spring cloud·mybatis
神奇小汤圆2 小时前
Spring Batch实战
后端
喵个咪2 小时前
传统 CMS 太笨重?试试 Headless 架构的 GoWind,轻量又强大
前端·后端·cms