mybatis-plus的分页/插件分析

19 篇文章 0 订阅

使用mybatis-plus的分页方法。

一. 使用默认分页方法

查看mybatis-plus(以下简称mp)源码中的BaseMapper接口,我们可以发现一下两个分页方法:

<E extends IPage<T>> E selectPage(E page, @Param("ew") Wrapper<T> queryWrapper);

<E extends IPage<Map<String, Object>>> E selectMapsPage(E page, @Param("ew") Wrapper<T> queryWrapper);

使用mp写法,我们只需要在传入queryWrapper的基础上,再额外传入一个泛型限定为继承IPage接口的page属性即可。

而实际上,mp里已经给我们提供了一个默认实现Page类:

public class Page<T> implements IPage<T> {
    private static final long serialVersionUID = 8545996863226528798L;
    private List<T> records;
    private long total;
    private long size;
    private long current;
    private List<OrderItem> orders;
    private boolean optimizeCountSql;
    private boolean isSearchCount;
 	// ...   
}

即我们可以使用如下方法进行mp的分页查询:

Page<User> page = new Page();
// 当前页
page.setCurrent(1L);
// 最大条数
page.setSize(10L);
this.getBaseMapper().selectPage(page, new QueryWrapper<User>());

二. 使用分页插件

第一种情况是调用mp接口的时候可以快速实现分页,但是如果我们面临一些复杂的多表查询不能直接调用mp接口,而是需要写一些xml的mapper文件来进行实现,这个时候我们仍然想要使用mp的分页方法该如何实现呢。

2.1 分页插件配置

实现mp分页扩展必须使用PaginationInnerInterceptor插件,即新建一个MybatisPlusConfig配置类,并通过@Bean进行注入:

@Configuration
@Slf4j
public class MybatisPlusConfig {

    @Bean
    public PaginationInterceptor paginationInterceptor() {
        PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
        paginationInterceptor.setLimit(-1);
        paginationInterceptor.setCountSqlParser(new JsqlParserCountOptimize(true));
        return paginationInterceptor;
    }
}

2.2 创建分页对象

当我们在处理复杂sql的分页查询的时候,在原有的入参上再加上一个实现IPage接口的类即可,这里我们可以仿照mp默认的Page创建一个我们自己的BasePage:

public class BasePage<T> implements IPage<T> {

    private static final long serialVersionUID = 8545996863226528798L;

    /**
     * 查询数据列表
     */
    private List<T> list = new ArrayList<>();
    /**
     * 总数
     */
    private long totalRow = 0;

    /**
     * 每页显示条数,默认 15
     */
    private long pageSize = 15;

    /**
     * 当前页
     */
    private long pageNumber = 1;

    /**
     * 排序字段信息
     */
    private List<OrderItem> orders = new ArrayList<>();

    /**
     * 自动优化 COUNT SQL
     */
    private boolean optimizeCountSql = true;
    /**
     * 是否进行 count 查询
     */
    private boolean isSearchCount = true;

    /**
     * 额外数据
     */
    private Object extraData;


    public BasePage() {

    }
}

2.3 使用示例

于是我们就可以在自定义mapper的sql写法的时候将分页对象传入即可自动进行分页,而在xml里无需写任何的limit等分页关键词,mp会自动根据分页对象的参数自动帮我们进行分页。

public interface UserMapper extends BaseMapper<User> {

    // 测试分页插件
    BasePage<User> selectcUserPage(BasePage<Object> parse, @Param("label") Integer label);

}
<select id="selectcUserPage" resultType="com.vainycos.entity.PO.User">
    select * from user
    where
    <choose>
        <when test="label != null and label == 0">
            label = 0
        </when>
        <otherwise>
            label != 0
        </otherwise>
    </choose>
</select>

三. 分页插件分析

以上分页具体是如何实现的呢。我们就需要观察PaginationInterceptor插件的源码,实际上它本身就是一个拦截器。

分析PaginationInterceptor#intercept方法:

  • 只有查询才需要分页实现
if (SqlCommandType.SELECT == mappedStatement.getSqlCommandType() && StatementType.CALLABLE != mappedStatement.getStatementType()) {
    BoundSql boundSql = (BoundSql)metaObject.getValue("delegate.boundSql");
    Object paramObj = boundSql.getParameterObject();
    // ...
}
  • 判断入参是否有IPage属性,只有IPage属性入参才执行分页

    BoundSql boundSql = (BoundSql)metaObject.getValue("delegate.boundSql");
    Object paramObj = boundSql.getParameterObject();
    IPage<?> page = null;
    if (paramObj instanceof IPage) {
        page = (IPage)paramObj;
    } else if (paramObj instanceof Map) {
        Iterator var8 = ((Map)paramObj).values().iterator();
        while(var8.hasNext()) {
            Object arg = var8.next();
            if (arg instanceof IPage) {
                page = (IPage)arg;
                break;
            }
        }
    }
    if (null != page && page.getSize() >= 0L) {
    	// 执行分页拦截方法
    } else {
        // 否则放行
        return invocation.proceed();
    }
    
    • 处理分页逻辑

      // 有省略
      String originalSql = boundSql.getSql();
      String buildSql = concatOrderBy(originalSql, page);
      DialectModel model = DialectFactory.buildPaginationSql(page, buildSql, dbType, this.dialectClazz);
      
    • 处理查询总数

      if (page.isSearchCount()) {
          SqlInfo sqlInfo = SqlParserUtils.getOptimizeCountSql(page.optimizeCountSql(), this.countSqlParser, originalSql);
          this.queryTotal(sqlInfo.getSql(), mappedStatement, boundSql, page, connection);
          if (page.getTotal() <= 0L) {
              return null;
          }
      }
      

四. 总结

正常sql的limit分页逻辑我们已经司空见惯,但是对于mp里的默认分页方法进行深入分析有助于我们了解mp框架,实际上也是使用了拦截器的拦截功能,并进行相关的增强作用。

参考资料:

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
MyBatis-Plus 是一个 MyBatis 的增强工具包,提供了很多方便的功能和特性,其中包括分页查询插件。使用 MyBatis-Plus分页查询插件可以简化分页查询的操作。 在 MyBatis-Plus 中,分页查询可以通过 `Page` 对象和 `PageHelper` 工具类来实现。下面是使用 MyBatis-Plus 进行分页查询的示例代码: 1. 首先,添加 MyBatis-Plus分页插件的依赖到你的项目中。你可以在项目的 pom.xml 文件中添加以下依赖: ```xml <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>最新版本</version> </dependency> ``` 2. 创建一个 `Page` 对象,并设置分页参数: ```java // 创建一个 Page 对象 Page<User> page = new Page<>(current, size); // 设置分页参数 page.setPages(current); // 当前页码 page.setSize(size); // 每页显示的记录数 ``` 3. 在 MyBatis 的 Mapper 接口中使用 `@Param` 注解传递 `Page` 对象,并在 SQL 中使用 MyBatis-Plus 提供的分页查询方法: ```java @Mapper public interface UserMapper extends BaseMapper<User> { List<User> selectUserPage(@Param("page") Page<User> page, @Param("name") String name); } ``` 4. 在 Service 层中调用分页查询方法: ```java @Service public class UserServiceImpl implements UserService { @Autowired private UserMapper userMapper; @Override public IPage<User> getUserPage(long current, long size, String name) { Page<User> page = new Page<>(current, size); return userMapper.selectUserPage(page, name); } } ``` 这样,你就可以使用 MyBatis-Plus分页查询插件进行分页查询了。记得在你的 SQL 语句中使用 MyBatis-Plus 提供的分页查询方法,而不是传统的 LIMIT 语句。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值