Java干货丨手撕MybatisPlus分页原理

0076e9d7e98b2b257493b34c1b108404.gif

在日常开发中经常会使用分页查询操作,而分页语句以及分页对象的处理,对于程序员来说是一个绕不开的小难题,虽然有很多Mybatis分页插件可以简化部分步骤,但是使用起来依旧比较繁琐。

MybatisPlus的出现,进一步减低了进行分页操作的门槛。本文带着大家学会使用MybatisPlus是分页插件,并对其原理进行一定的分析。接下来我们主要在Spring boot环境下看看如何使用MybatisPlus进行分页查询。

8ae61b39c3a86afcd7c05b5af20abff0.gif

关于分页插件,我们还需要知道以下两点:

内置分页插件:MybatisPlus基于 MyBatis 物理分页,开发者无需关心具体操作,配置好插件之后,写分页等同于普通 List 查询。

分页插件支持多种数据库:支持 MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、Postgre、SQLServer 等多种数据库。

 1.MybatisPlus分页快速入门 

1.1准备操作

我们将通过一个简单的 Demo 来阐述 MyBatis-Plus 的强大功能,在此之前,我们假设您已经:

- 拥有 Java 开发环境以及相应 IDE

- 初始化 Spring Boot项目

- 熟悉 Maven

- 已经导入mybatisplus依赖,并完成相关配置信息。

现在有一张表 t_user 结构如下:

8c89a827675e816de2eec6d12d2857cc.jpeg

编写实体类User:(使用lombok简化)

@Data
@TableName("tb_user")
public class User {
    //告知id是主键  采用的自增形式
    @TableId(type= IdType.AUTO)
    private Long id;
    @TableField("user_name")
    private String userName;


    private String password;


    private String name;


    private Integer age;


    private String email;
}

 编写 Mapper 包下的 `UserMapper`接口

public interface UserMapper extends BaseMapper<User> {
}

1.2 完成分页查询需求

1.2.1 导入核心插件MybatisPlusInterceptor

由于mp分页是基于插件产生,所以我们需要先 导入核心插件到springboot中。

@Configuration
@MapperScan("com.itheima.mapper")
public class MybatisPlusConfig {
    /**
     * 新的分页插件,一缓和二缓遵循mybatis的规则,需要设置 MybatisConfiguration#useDeprecatedExecutor = false 避免缓存出现问题(该属性会在旧插件移除后一同移除)
     */
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
                                        // 配置分页插件
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }
    @Bean
    public ConfigurationCustomizer configurationCustomizer() {
        return configuration -> configuration.setUseDeprecatedExecutor(false);
    }
}

1.2.2 使用Mpper分页查询接口

// 根据 entity 条件,查询全部记录(并翻页)
IPage<T> selectPage(IPage<T> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
// 根据 Wrapper 条件,查询全部记录(并翻页)
IPage<Map<String, Object>> selectMapsPage(IPage<T> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

实现基本分页查询测试

@SpringBootTest
class PageQueryTests {
    @Autowired
    private UserMapper userMapper;


    @Test
    void testSelectPage() {
        //当前页码
        int current = 2;
        //每页条数
        int size = 2;
        //构建 分页构造器
        IPage<User> page = new Page(current, size);
        //构建 条件构造器
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper.lt("age",23);
        //执行查询 
        userMapper.selectPage(page, wrapper);
        //它会自动完成 数据的封装 并把查询出来的数据 存储到page对象的一个属性中
        // setRecords  把数据存到 Records 里面了
        // 总条数  总页数 --page也有封装
        //获取page的 Records属性
        List<User> records = page.getRecords();//当前页数据
        long total = page.getTotal();//总条数
        long pages = page.getPages();//总页数


        System.out.println("当前数据总共有:"+total);
        System.out.println("共"+pages+"页");
        System.out.println("当前页数据:"+records);
    }

查询结果如下:

a15091397a9a1aec7037347c53e2a1ba.png

1.3 代码套路总结

* 构建分页构造器(需要传递分页条件 current,size)

* 构建条件构造器(支持条件分页查询)

* 执行查询方法,完成查询

* 解析查询后结果

 2.MybatisPlus原理分析 

2.1 mybatisplus插件介绍

MybatisPlus核心插件 MybatisPlusInterceptor,基于该插件mp实现了丰富的特性,该插件是核心插件,目前代理了 `Executor#query` 和 `Executor#update` 和 `StatementHandler#prepare` 方法。也就是说该插件可以对查询的执行,增删改的执行以及预处理对象进行功能性的增强。

那么,是如何对sql实现拦截增强的呢,我们就要研究一下该分页插件的拦截器集合属性。

private List<InnerInterceptor> interceptors = new ArrayList<>();

InnerInterceptor

我们提供的插件都将基于此接口来实现功能。

目前已有的功能:

- 自动分页:

PaginationInnerInterceptor

- 多租户:

TenantLineInnerInterceptor

- 动态表名:

DynamicTableNameInnerInterceptor

- 乐观锁: 

OptimisticLockerInnerInterceptor

- sql 性能规范: 

IllegalSQLInnerInterceptor

- 防止全表更新与删除:

BlockAttackInnerInterceptor

由上可知,如果想要研究分页的实现原理就要研究分页拦截器"<font color='red'>PaginationInnerInterceptor</font>"

2.2 PaginationInnerInterceptor 运行原理

当我们执行该语句时,会在执行sql之前被拦截器拦截

userMapper.selectPage(page, wrapper);

先从我们在mybatis-plus的配置说起:

75f5c98fcd9c745d83b6bc0a0a1a673a.jpeg

我们对分页插件进行拦截会发现,当我们执行sql的时候mybatis-plus会对所有SQL语句进行拦截并做各种判断与附加操作,会进入到Mybatis-Plus全局拦截器。

67565f4996308570c192b3a84af8a256.jpeg

下图中是针对分页情况下的特定操作:

0ebb16d985649105d7a7109b852cc7cf.jpeg

由82行可知,当前sql执行时,被拦截器拦截,发现是查询语句,就会先执行winllDoQuery方法,其次做完在执行 beforeQuery。

因为在配置中new出来的是 PaginationInnerInterceptor 对象,所以这里的方法就会走该对象中的方法。

0b422aa0acca82622f6679b855b71aad.jpeg

0428c3aa48884d81068de7a354df8832.jpeg

从源码中不难看出,此处对查询参数做了提取并通过`ParameterUtils.findPage()`方法进行了转换判断,继续往里看:

7a19a7ee1b1563504c308ba0e21c4100.jpeg

可以看到,方法中是提取`Map`类型参数中的`IPage`类型参数或者是直接传入`IPage`类型的参数进行提取,如果有则直接返回`IPage`类型的参数,如果为空则返回null不进行count查询。

上面就是我们在看到的count查询。

d5aa52ebca21dcf04244e742d7883c59.jpeg

那么在什么时候实现的分页查询呢? 刚才在追踪源码的时候也发现了 winllDoQuery方法执行完调用了 beforeQuery().`beforeQuery()`方法对分页查询进行了拦截。

1d6bc69710bb5262325588ed2355ffab.jpeg

c2ccd4ba928bda5771f315450be17901.jpeg

 3.结束语 

其实我们发现,mybatisplus的分页实现其实是借助了拦截器的拦截功能,在查询之前进行了两次拦截,最终完成封装操作,通过本文的介绍,你是否比之前更加清晰了呢~

f7ed3614980051baba06caf46664eade.png505eac4c78ba8b80e80f1ef668fbab89.png11e312dfdb25832ac22871e4bb8d8597.png

以上就是播妞为大家分享的干货内容

希望对你有所帮助

 黑马程序员 

累计培养30余万名优质IT人才

8大学科火热报名中

基础班仅需   28   元,扫码免费咨询

eabaa23b6c3821ce92d69af5608a466a.png

咨询线上课程

可以直接添加播妞1对1解答哦

16bbab8c7d58d8738429b696315d2865.jpeg

e0433a1cca1d5fb75f12d6c325c3dc90.png

丨热门教程资源免费领丨

回复 【 领取资源 】 领《黑马8学科汇总教程》

回复 【 python 】 领《最新python入门教程》

回复 【 瑞吉外卖 】 领《瑞吉外卖项目教程》

回复 【 mysql1 】 领《Mysql入门到精通》

回复 【 毕业设计 】 领《Java毕业设计项目》

更多教程加播妞领取: heiniu526

(在下方公众号回复对应关键词,即可领取哦)

↓↓↓

b84656e1d43c04d6ef7527fe22cde5a2.gif

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值