首页 > 其他分享 >你真的懂Mybatis分页原理吗?

你真的懂Mybatis分页原理吗?

时间:2024-08-16 11:24:12浏览次数:8  
标签:分页 PageHelper limit SQL Mybatis 原理 查询 RowBounds

目录

一、Mybatis如何实现分页?

1.1 在SQL中添加limit语句

1.2 基于PageHelper分页插件,实现分页

1.3 基于RowBounds实现分页

1.4 基于MyBatis-Plus实现分页

二、RowBounds的分页原理

三、PageHelper的分页原理

四、Mybatis-Plus的分页原理

五、Mybatis-Plus实现分页

5.1 添加分页插件

5.2 编写 Mapper 接口

5.3 执行分页查询


一、Mybatis如何实现分页?

MyBatis可以通过两种方式来实现分页:基于物理分页和基于逻辑分页

所谓物理分页,指的是最终执行的SQL中进行分页,即SQL语句中带limit,这样SQL语句执行之后返回的内容就是分页后的结果。

所谓逻辑分页,就是在SQL语句中不进行分页,照常全部查询,在查询到的结果集中,再进行分页。

在MyBatis中,想要实现分页通常有四种做法:

1.1 在SQL中添加limit语句

<select id="getUsers" resultType="User">
    select * from user 
    <where>
        <if test="name != null">
            and name like CONCAT('%',#{name},'%')
        </if>
    </where>
    limit #{offset}, #{limit}
</select>

1.2 基于PageHelper分页插件,实现分页

在使用PageHelper时,只需要在查询语句前调用PageHelper.startPage()方法,然后再进行查询操作。PageHelper会自动将查询结果封装到一个PageInfo对象中,包含了分页信息和查询结果。

// Java代码中使用 PageHelper
PageHelper.startPage(1, 10);
List<User> userList = userMapper.getUsers();
PageInfo<User> pageInfo = new PageInfo<>(userList);

使用PageHelper插件,不需要在mapper.xml文件中使用limit语句

1.3 基于RowBounds实现分页

RowBounds是MyBatis中提供的一个分页查询工具,其中可以设置offset和limit用于分页。

int offset = 10; // 偏移量
int limit = 5; // 每页数据条数
RowBounds rowBounds = new RowBounds(offset, limit);
List<User> userList = sqlSession.selectList("getUsers", null, rowBounds);

1.4 基于MyBatis-Plus实现分页

MyBatis-Plus提供了分页插件,可实现简单易用的分页功能,可以根据传入的分页参数自动计算出分页信息,无需手动编写分页SQL语句。

public interface UserMapper extends BaseMapper<User> {
    List<User> selectUserPage(@Param("page") Page<User> page, @Param("name") String name);
}

以上四种做法中,能实现逻辑分页的是RowBounds和MyBatis-Plus,能实现物理分页的是手动添加limit、PageHelper以及MyBatis-Plus。

物理分页和逻辑分页,工作中推荐使用那种分页呢? 数据小的话无所谓,逻辑分页更简单点,数据量大的话,一定是物理分页,避免查询慢,也避免内存被撑爆。

二、RowBounds的分页原理

MyBatis的RowBounds是一个用于分页查询的简单POJO类,它包含两个属性offset和limit,分别表示分页查询的偏移量和每页查询的数据条数。

在使用RowBounds进行逻辑分页的时候,我们的SQL语句中是不需要指定分页参数的。就正常的查询即可,如:

<select id="getUsers" resultType="User">
  select * from user
  <where>
    <if test="name != null">
      and name like CONCAT('%',#{name},'%')
    </if>
  </where>
  order by id
</select>

然后,在查询的时候,将RowBounds当做一个参数传递:

int offset = 10; // 偏移量
int limit = 5;   // 每页数据条数
RowBounds rowBounds = new RowBounds(offset, limit);
List<User> userList = sqlSession.selectList("getUserList", null, rowBounds);

这样,实际上在查询的时候,将会先所有符合条件的记录返回,然后再在内存中进行分页,分页的方式是根据RowBounds中指定的offset和limit进行数据保留,即抛弃掉不需要的数据再返回。

三、PageHelper的分页原理

PageHelper是MyBatis中提供的分页插件,主要是用来做物理分页的。

当我们在代码中使用 PageHelper.startPage(int pageNum, int pageSize) 设置分页参数之后,其实PageHelper会把它们存储到ThreadLocal中。

PageHelper会在执行翻的query方法执行之前,会从ThreadLocal中获取取分页参数信息,页码和页大小,然后执行分页查询,计算需要返回的数据的起始位置和大小。最后,PageHelper会通过修改SQL语句的方式,在SQL后面加上带有limit语句,限定查询的数据范围,从而实现物理分页的效果。而且在查询结束后再清除ThreadLocal中的分页参数。

四、Mybatis-Plus的分页原理

MyBatis-Plus支持分页插件——PaginationInnerInterceptor

PaginationInnerInterceptor采用的是物理分页方式,物理分页是在数据库中进行分页,即直接在SQL语句中加入LIMIT语句,只查询所需的部分数据。

物理分页的优点是可以减少内存占用,减轻数据库的负载,缺点是无法对结果进行任意操作,比如说在分页过程中做二次过滤、字段映射、json解析等。

PaginationInnerInterceptor这个分页插件就会自动拦截所有的SQL查询请求,计算分页查询的起始位置和记录数,并在 SQL语句中加入LIMIT语句。

核心的操作在beforeQuery中:

@Override
public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
    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);
    IDialect dialect = findDialect(executor);

    final Configuration configuration = ms.getConfiguration();
    DialectModel model = dialect.buildPaginationSql(buildSql, page.offset(), page.getSize(), _limit);
    PluginUtils.MPBoundSql mpBoundSql = PluginUtils.mpBoundSql(boundSql);

    List<ParameterMapping> mappings = mpBoundSql.parameterMappings();
    Map<String, Object> additionalParameter = mpBoundSql.additionalParameters();
    model.consumers(mappings, configuration, additionalParameter);
    mpBoundSql.sql(model.getDialectSql());
    mpBoundSql.parameterMappings(mappings);
}

其中比较关键的就是第31行,buildPaginationSql方法。这里不同的数据库有不同的实现,我们看一下MySQL的实现:

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).append(COMMA);
        if (offset != 0L) {
            return new DialectModel(sql.toString(), offset, limit).setConsumerChain();
        } else {
            return new DialectModel(sql.toString(), limit).setConsumer(true);
        }
    }
}

这段代码就比较好理解了,其实就是在原来的SQL后面拼上 LIMIT ?,?,这样在后续执行的过程中,就可以把 offset和limit值给这两个占位符,实现分页查询了。

五、Mybatis-Plus实现分页

5.1 添加分页插件

在 Spring Boot 应用中,可以这样配置:

@Configuration
@MapperScan("scan.your.mapper.package")
public class MybatisPlusConfig {

    /**
     * 添加分页插件
     */
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); // 加入分页拦截器
        return interceptor;
    }
}

5.2 编写 Mapper 接口

定义一个 Mapper 接口,用于执行数据库操作。这个接口不需要特别指定分页相关的方法,MyBatis-Plus 会自动处理。

public interface UserMapper extends BaseMapper<User> {
    // 这里可以添加其他数据库操作的方法
}

5.3 执行分页查询

在服务层或者控制器层,使用 MyBatis-Plus 提供的 Page 类来执行分页查询。例如,要查询第 1 页的数据,每页显示 10 条记录,可以这样写:

@Autowired
private UserMapper userMapper;

public IPage<User> selectUserPage(int currentPage, int pageSize) {
    Page<User> page = new Page<>(currentPage, pageSize);
    IPage<User> userPage = userMapper.selectPage(page, null);
    return userPage;
}

selectPage 方法是 MyBatis-Plus 提供的内置方法,用于执行分页查询。null 作为第二个参数表示没有查询条件,即查询所有记录。

selectPage 方法返回的 IPage 对象包含了分页信息(如当前页码、总页数、每页记录数、总记录数等)和查询结果。

标签:分页,PageHelper,limit,SQL,Mybatis,原理,查询,RowBounds
From: https://blog.csdn.net/qq_38196449/article/details/141165800

相关文章

  • PD快充协议芯片原理
    PD诱骗芯片的原理工作如下:当设备插入type-C接口时,芯片会自动检测接入电压和电流,并根据设备的实际需求调整输出电压和电流,这使得小家电设备能够实现多种充电方式的管理,如9V、12V、15V、20V等,从而确保设备能够达到最佳充电的体验。这种芯片通过内置的通讯模块与供电端进行握手通......
  • pd协议的工作原理和应用
    PD协议通过type-C接口的CC线进行通信,协商电压、电流及供电方向。通信过程需要按照特定的数据包格式进行,存在相互认证的过程,当电缆接通后PD协议的SOP通信在CC线上进行从此来选择电源传输的规格。PD协议的优势在于其通用性和智能控制方式,使得一个充电器可以配置多个设备。XSP08Q......
  • 混合策略改进的蜣螂算法(IDBO)优化长短期记忆神经网络原理及matlab代码
    目录0引言1数学模型2模型对比3matlab代码3.1改进的主代码3.2IDBO-LSTM4视频讲解0引言针对DBO算法全局探索能力不足、易陷入局部最优以及收敛精度不理想等问题,多为学者提出了混合多策略改进的蜣螂优化算法(IDBO)。主要混合策略改进首先是采用混沌映射结合随机反......
  • 混合策略改进的蜣螂算法(IDBO)优化支持向量机原理及matlab代码
    目录0引言1数学模型2模型对比3matlab代码3.1改进的主代码3.2IDBO-SVM4视频讲解0引言针对DBO算法全局探索能力不足、易陷入局部最优以及收敛精度不理想等问题,多为学者提出了混合多策略改进的蜣螂优化算法(IDBO)。主要混合策略改进首先是采用混沌映射结合随机反向......
  • [Java并发]Synchronized底层原理
    synchronized底层语义原理Java虚拟机中的同步(Synchronization)基于进入和退出管程(Monitor)对象实现。在Java语言中,同步用的最多的地方可能是被synchronized修饰的同步方法。同步方法并不是由monitorenter和monitorexit指令来实现同步的,而是由方法调用指令读取运行时......
  • (路由卷1)-12-DUAL原理_SIA原因
    企业中部署eigrp/以及带来的问题1.私有2.本身算法问题dual算法,查询报文,回复报文。限制范围路由条目路由跳数底太路径fd最小的时successor,fs路由器为successor的路由器的fd>ad,没有fs时候就会往外发查询包。当一条路由丢失的时候eigrp路由器会发送查询报文,而且没有fs的时候......
  • 大模型微调实战演练:使用代码剖析 Transformers Pipelines工作原理
    在自然语言处理(NLP)领域,Transformers模型已经成为了主流技术之一。无论是文本分类、情感分析,还是机器翻译,Transformers都展现了强大的性能。今天,我们来详细解析一下TransformersPipelines的运行原理,帮助大家更好地理解其内部机制。一、基本流程TransformersPipeline......
  • 脑裂:原理、危害与应对策略
    Redis脑裂是指Redis服务器在运行过程中出现的一种异常情况,即一个Redis实例分裂成了两个或多个独立运行的部分,它们之间失去了通信和数据同步。一、原因Redis脑裂是指在Redis服务器的运行过程中,一个Redis实例意外地分裂成两个或多个独立运行的部分,导致它们之间失去了通......
  • 深入浅出 CDN 链接:从原理到应用,全面解析内容分发网络
    深入浅出CDN链接:从原理到应用,全面解析内容分发网络CDN(ContentDeliveryNetwork,内容分发网络)是近年来互联网发展的重要基石,它通过将内容缓存到全球各地的服务器上,实现内容的快速、高效分发,提升用户体验,降低网站运营成本。本文将从CDN链接的原理、工作机制、应用场景、......
  • VUE DIFF算法原理
    Vue的Diff算法是虚拟DOM实现中的核心部分,它在视图更新时比较新旧虚拟DOM树并高效更新实际DOM。一、什么是Diff算法?Diff算法用于在虚拟DOM更新时,通过比较新旧两棵虚拟DOM树,找出最小差异并将这些变化应用到实际DOM上。Vue采用了一种高效的算法,只对同层级节点进......