MybatisPlus的分页插件

2023-12-14 23:33:42

PaginationInnerInterceptor

此插件是核心插件,目前代理了 Executor#query 和 Executor#update 和 StatementHandler#prepare 方法。

在SpringBoot环境中配置方式如下:

/**
 * @author giserDev
 * @description 配置分页插件、方言、mapper包扫描等
 * @date 2023-12-13 23:23:35
 */
@Configuration
@MapperScan("com.giser.mybatisplus.mapper")
public class MyBatisPlusConfig {

    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor(){
        MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
        mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return mybatisPlusInterceptor;
    }

}

测试分页功能:


import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.giser.mybatisplus.mapper.UserMapper;
import com.giser.mybatisplus.pojo.User;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

/**
 * @author giserDev
 * @description
 * @date 2023-12-13 23:28:43
 */
@Slf4j
@SpringBootTest
public class MyBatisPlusPluginsTest {

    @Autowired
    private UserMapper userMapper;

    @Test
    public void testMyBatisPlusPage(){
        Page<User> page = new Page<>(1,3);
        userMapper.selectPage(page, null);
        log.info("分页查询结果为:{}", page);
    }

}

如何自定义分页?

# mybatis-plus配置
mybatis-plus:
  configuration:
    # 引入日志
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  global-config:
    db-config:
      # 配置MybatisPlus操作的表的前缀
      table-prefix: t_
      # 配置MybatisPlus的主键生成策略
      id-type: assign_id
  # 指定类型别名
  type-aliases-package: com.giser.mybatisplus.pojo

在Mapper接口中定义分页方法:

@Repository
public interface UserMapper extends BaseMapper<User> {

  /**
   * 利用MybatisPlus的分页功能
   * @param page 分页对象,必须在第一个参数位置,传递参数 Page 即自动分页
   * @param userName 查询条件
   * @return 结果
   */
  Page<User> selectPageByName(@Param("page") Page<User> page, @Param("userName") String userName);

}

在Mapper.xml文件中实现分页语句:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.giser.mybatisplus.mapper.UserMapper">

    <select id="selectPageByName" resultType="User">
        select * from user where user_name = #{userName}
    </select>

</mapper>

分页测试:


import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.giser.mybatisplus.mapper.UserMapper;
import com.giser.mybatisplus.pojo.User;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@Slf4j
@SpringBootTest
public class MyBatisPlusPluginsTest {

    @Autowired
    private UserMapper userMapper;

    /**
     * ==>  Preparing: SELECT COUNT(*) AS total FROM user WHERE is_deleted = 0
     * ==> Parameters:
     * <==    Columns: total
     * <==        Row: 25
     * <==      Total: 1
     * ==>  Preparing: SELECT id,user_name AS name,age,email,is_deleted FROM user WHERE is_deleted=0 LIMIT ?
     * ==> Parameters: 3(Long)
     * <==    Columns: id, name, age, email, is_deleted
     * <==        Row: 1, Jone, 18, test1@baomidou.com, 0
     * <==        Row: 2, 窃听风云, 23, giserDev@163.com, 0
     * <==        Row: 3, Tom, 28, test3@baomidou.com, 0
     * <==      Total: 3
     * Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@3a109ff7]
     * 分页查询结果为:com.baomidou.mybatisplus.extension.plugins.pagination.Page@5ee77baf
     * 分页查询记录结果为:[User(id=1, name=Jone, age=18, email=test1@baomidou.com, isDeleted=0), User(id=2, name=窃听风云, age=23, email=giserDev@163.com, isDeleted=0), User(id=3, name=Tom, age=28, email=test3@baomidou.com, isDeleted=0)]
     * 分页查询记录条数为:25
     * 分页查询当前页码为:1
     * 分页查询每页大小为:3
     */
    @Test
    public void testMyBatisPlusPage(){
        Page<User> page = new Page<>(1,3);
        userMapper.selectPage(page, null);
        log.info("分页查询结果为:{}", page);
        log.info("分页查询记录结果为:{}", page.getRecords());
        log.info("分页查询记录条数为:{}", page.getTotal());
        log.info("分页查询当前页码为:{}", page.getCurrent());
        log.info("分页查询每页大小为:{}", page.getSize());
    }

    /**
     * ==>  Preparing: SELECT COUNT(*) AS total FROM user WHERE user_name = ?
     * ==> Parameters: 李雷(String)
     * <==    Columns: total
     * <==        Row: 1
     * <==      Total: 1
     * ==>  Preparing: select * from user where user_name = ? LIMIT ?
     * ==> Parameters: 李雷(String), 3(Long)
     * <==    Columns: id, user_name, age, email, is_deleted
     * <==        Row: 1734579107187970050, 李雷, 12, null, 1
     * <==      Total: 1
     * Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@a316f6b]
     * 分页查询结果为:com.baomidou.mybatisplus.extension.plugins.pagination.Page@48368a08
     * 分页查询记录结果为:[User(id=1734579107187970050, name=null, age=12, email=null, isDeleted=1)]
     * 分页查询记录条数为:1
     * 分页查询当前页码为:1
     * 分页查询每页大小为:3
     */
    @Test
    void testAutoPage(){
        Page<User> page = new Page<>(1,3);
        userMapper.selectPageByName(page, "李雷");
        log.info("分页查询结果为:{}", page);
        log.info("分页查询记录结果为:{}", page.getRecords());
        log.info("分页查询记录条数为:{}", page.getTotal());
        log.info("分页查询当前页码为:{}", page.getCurrent());
        log.info("分页查询每页大小为:{}", page.getSize());
    }

}
问题:分页参数处理原理

拦截执行的语句
org.apache.ibatis.plugin.Plugin#invoke

if (methods != null && methods.contains(method)) {
return interceptor.intercept(new Invocation(target, method, args));
}

com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor#intercept

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
    // ...
   for (InnerInterceptor query : interceptors) {
   // com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor#willDoQuery
   // willDoQuery(),先判断总条数,决定是否执行sql语句
   if (!query.willDoQuery(executor, ms, parameter, rowBounds, resultHandler, boundSql)) {
       return Collections.emptyList();
   }
   // beforeQuery(),处理排序、分页方言
   query.beforeQuery(executor, ms, parameter, rowBounds, resultHandler, boundSql);
}
	// ...
    @Override
    public boolean willDoQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
    // 查找分页参数
        IPage<?> page = ParameterUtils.findPage(parameter).orElse(null);
        if (page == null || page.getSize() < 0 || !page.searchCount()) {
            return true;
        }
		// ...
		// 组装统计语句,判断总条数
        long total = 0;
        if (CollectionUtils.isNotEmpty(result)) {
            // 个别数据库 count 没数据不会返回 0
            Object o = result.get(0);
            if (o != null) {
                total = Long.parseLong(o.toString());
            }
        }
        page.setTotal(total);
        // protected boolean continuePage(IPage<?> page)
        // 返回是否继续执行后面的查询语句,执行则返回true,否则返回false
        return continuePage(page);
    }

willDoQuery()方法中包含查询分页参数的逻辑,如下:

/**
 * 查找分页参数
 *
 * @param parameterObject 参数对象
 * @return 分页参数
 */
public static Optional<IPage> findPage(Object parameterObject) {
    if (parameterObject != null) {
    	// 若参数类型为Map,则从map中查找类型为IPage的参数,作为分页参数
        if (parameterObject instanceof Map) {
            Map<?, ?> parameterMap = (Map<?, ?>) parameterObject;
            for (Map.Entry entry : parameterMap.entrySet()) {
                if (entry.getValue() != null && entry.getValue() instanceof IPage) {
                    return Optional.of((IPage) entry.getValue());
                }
            }
        } else if (parameterObject instanceof IPage) {
        	// 若参数类型为IPage,则直接返回,作为分页参数
            return Optional.of((IPage) parameterObject);
        }
    }
    return Optional.empty();
}

com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor#beforeQuery

@Override
    public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
        IPage<?> page = ParameterUtils.findPage(parameter).orElse(null);
        if (null == page) {
            return;
        }

        // 处理 orderBy 拼接
        boolean addOrdered = false;
        String buildSql = boundSql.getSql();
        List<OrderItem> orders = page.orders();
        if (CollectionUtils.isNotEmpty(orders)) {
            addOrdered = true;
            buildSql = this.concatOrderBy(buildSql, orders);
        }

        // size 小于 0 且不限制返回值则不构造分页sql
        Long _limit = page.maxLimit() != null ? page.maxLimit() : maxLimit;
        if (page.getSize() < 0 && null == _limit) {
            if (addOrdered) {
                PluginUtils.mpBoundSql(boundSql).sql(buildSql);
            }
            return;
        }

        handlerLimit(page, _limit);
        // 查找方言,在配置分页插件时,设置了DbType,此时可确定方言,如此时返回的是com.baomidou.mybatisplus.extension.plugins.pagination.dialects.MySqlDialect
        IDialect dialect = findIDialect(executor);

        final Configuration configuration = ms.getConfiguration();
	// 构造分页语句!!!com.baomidou.mybatisplus.extension.plugins.pagination.dialects.IDialect#buildPaginationSql
	// 此时dialect为MySqlDialect,会调用com.baomidou.mybatisplus.extension.plugins.pagination.dialects.MySqlDialect#buildPaginationSql来构造sql
        DialectModel model = dialect.buildPaginationSql(buildSql, page.offset(), page.getSize());
		// ...
		// 此处绑定执行的sql语句model.getDialectSql()
        mpBoundSql.sql(model.getDialectSql());
        mpBoundSql.parameterMappings(mappings);
    }

com.baomidou.mybatisplus.extension.plugins.pagination.dialects.MySqlDialect#buildPaginationSql

public class MySqlDialect implements IDialect {

    @Override
    public DialectModel buildPaginationSql(String originalSql, long offset, long limit) {
        StringBuilder sql = new StringBuilder(originalSql).append(" LIMIT ").append(FIRST_MARK);
        if (offset != 0L) {
            sql.append(StringPool.COMMA).append(SECOND_MARK);
            return new DialectModel(sql.toString(), offset, limit).setConsumerChain();
        } else {
            return new DialectModel(sql.toString(), limit).setConsumer(true);
        }
    }
}

文章来源:https://blog.csdn.net/SUNBOYmxbsH/article/details/135005285
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。