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!

相关推荐
Asthenia04126 分钟前
如何设计实现一个定时任务执行器 - SpringBoot环境下的最佳实践
后端
兔子的洋葱圈24 分钟前
【django】1-2 django项目的请求处理流程(详细)
后端·python·django
Asthenia041233 分钟前
如何为这条sql语句建立索引:select * from table where x = 1 and y < 1 order by z;
后端
ihgry35 分钟前
SpringBoot+Mybatis实现Mysql分表
后端
Asthenia041236 分钟前
令牌桶算法与惰性机制的应用
后端
lamdaxu38 分钟前
02Tomcat 线程模型详解&性能调优
后端
lamdaxu41 分钟前
03Tomcat类加载机制&热加载和热部署
后端
程序猿chen1 小时前
《JVM考古现场(十五):熵火燎原——从量子递归到热寂晶壁的代码涅槃》
java·jvm·git·后端·java-ee·区块链·量子计算
Asthenia04121 小时前
Pandas全面操作指南与电商销售数据分析
后端