MyBatis分页插件 MyBatis Plus 分页查询优化

好记忆不如烂笔头,能记下点东西,就记下点,有时间拿出来看看,也会发觉不一样的感受.

目录

一、基础概念

二、常见分页问题及优化策略

(一)单表大分页问题

1. 利用主键索引优化

2. 子查询优化

3. 限制最大分页页数

(二)多表关联分页问题

1. 先分页再关联

(三)分页与排序结合的问题

1. 添加唯一字段辅助排序

三、分页插件配置与优化

(一)插件配置

(二)自定义分页参数

(三)分页插件的局限性

四、实战案例 —— 电商订单列表分页优化

(一)问题分析

(二)优化方案

(三)代码实现

五、面试应答技巧

六、总结


一、基础概念

MyBatis Plus 是一个增强版的 MyBatis 框架,提供了强大的分页功能,通过内置的分页插件可以轻松实现分页查询。其分页功能的核心是通过拦截器 PaginationInterceptor 实现的,该拦截器会在执行 SQL 之前对 SQL 进行解析和改写,为查询语句添加 LIMITOFFSET 子句,并生成统计总记录数的 SQL。

二、常见分页问题及优化策略

(一)单表大分页问题

当数据量较大时,分页查询效率会显著下降,尤其是当 offset 值较大时,数据库需要扫描大量数据才能返回结果。

1. 利用主键索引优化

如果表有主键索引,可以先查询主键,再根据主键查询具体数据,减少数据扫描量。
 

Page<Long> idPage = new Page<>(2, 10);
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.select("id");
userMapper.selectPage(idPage, wrapper);
List<Long> ids = idPage.getRecords();
QueryWrapper<User> dataWrapper = new QueryWrapper<>();
dataWrapper.in("id", ids);
List<User> users = userMapper.selectList(dataWrapper);
2. 子查询优化

通过子查询获取主键范围,避免大量 offset 扫描。

SELECT u.* FROM user u
WHERE u.id > (SELECT id FROM user ORDER BY id LIMIT 9990, 1)
LIMIT 10;
3. 限制最大分页页数

在业务层面限制用户能够查询的最大页数,避免出现过大的 offset

(二)多表关联分页问题

多表关联查询时,分页的复杂性会增加,不同的关联方式会对分页结果产生不同影响。

1. 先分页再关联

如果需要对主表进行分页,应先对主表进行分页,再关联子表数据,避免关联后数据量过大导致的分页效率问题。

Page<User> userPage = new Page<>(2, 10);
userMapper.selectPage(userPage, null);
List<User> users = userPage.getRecords();
List<Order> orders = orderMapper.selectList(new QueryWrapper<Order>().in("user_id", users.stream().map(User::getId).collect(Collectors.toList())));
users.forEach(user -> user.setOrders(orders.stream().filter(order -> order.getUserId().equals(user.getId())).collect(Collectors.toList())));

(三)分页与排序结合的问题

当分页查询中包含排序时,如果排序字段存在重复值,可能会导致分页结果不一致。

1. 添加唯一字段辅助排序

在排序时加上一个唯一的字段(如主键),确保分页结果的稳定性。

QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.orderByDesc("create_time", "id");
Page<User> page = new Page<>(2, 10);
userMapper.selectPage(page, wrapper);

三、分页插件配置与优化

(一)插件配置

MyBatis Plus 的分页插件提供了多种可配置项,可以根据业务需求进行调整。

@Configuration
public class MyBatisPlusConfig {
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
        paginationInterceptor.setOverflow(false);
        paginationInterceptor.setReasonable(true);
        interceptor.addInnerInterceptor(paginationInterceptor);
        return interceptor;
    }
}

(二)自定义分页参数

可以通过实现 Pagination 接口来自定义分页参数,例如限制每页的最大数据量。

public class CustomPage<T> extends Page<T> {
    private static final long MAX_PAGE_SIZE = 100;
    public CustomPage(long current, long size) {
        super(current, size);
        if (size > MAX_PAGE_SIZE) {
            this.setSize(MAX_PAGE_SIZE);
        }
    }
}

(三)分页插件的局限性

分页插件在处理复杂 SQL(如包含子查询、UNION 等操作)时可能无法正确解析和改写,需要手动处理分页或优化 SQL。

四、实战案例 —— 电商订单列表分页优化

(一)问题分析

  1. 数据量庞大,分页查询效率可能下降。

  2. 多表关联复杂,关联方式和顺序影响查询效率。

  3. 排序和筛选条件多样,需要考虑各种组合情况。

(二)优化方案

  1. 索引优化:为订单表的 statuscreate_time 等常用筛选和排序字段添加索引。

  2. 分页策略:先对主表(订单表)进行分页,再关联其他表数据。

  3. 结果集优化:只查询需要的字段,减少数据传输量。

  4. 缓存机制:使用缓存(如 Redis)减少数据库访问压力。

(三)代码实现

public Page<OrderVO> getOrderPage(OrderQueryParam param) {
    QueryWrapper<Order> orderWrapper = new QueryWrapper<>();
    orderWrapper.eq(param.getStatus() != null, "status", param.getStatus());
    orderWrapper.between(param.getStartTime() != null && param.getEndTime() != null, "create_time", param.getStartTime(), param.getEndTime());
    orderWrapper.orderByDesc("create_time", "id");

    Page<Order> orderPage = new Page<>(param.getCurrentPage(), param.getPageSize());
    userMapper.selectPage(orderPage, orderWrapper);

    List<Long> orderIds = orderPage.getRecords().stream().map(Order::getId).collect(Collectors.toList());
    List<User> users = userMapper.selectList(new QueryWrapper<User>().in("id", orderPage.getRecords().stream().map(Order::getUserId).collect(Collectors.toList())));
    List<Product> products = productMapper.selectList(new QueryWrapper<Product>().in("id", orderPage.getRecords().stream().map(Order::getProductId).collect(Collectors.toList())));

    List<OrderVO> orderVOList = orderPage.getRecords().stream().map(order -> {
        OrderVO orderVO = new OrderVO();
        BeanUtils.copyProperties(order, orderVO);
        User user = users.stream().filter(u -> u.getId().equals(order.getUserId())).findFirst().orElse(null);
        if (user != null) {
            orderVO.setUserName(user.getUserName());
        }
        Product product = products.stream().filter(p -> p.getId().equals(order.getProductId())).findFirst().orElse(null);
        if (product != null) {
            orderVO.setProductName(product.getProductName());
        }
        return orderVO;
    }).collect(Collectors.toList());

    Page<OrderVO> resultPage = new Page<>(orderPage.getCurrent(), orderPage.getSize(), orderPage.getTotal());
    resultPage.setRecords(orderVOList);
    return resultPage;
}

五、面试应答技巧

当面试官问到 MyBatis Plus 分页优化问题时,我们可以按照以下思路来回答:

  1. 基础用法:先简要说明 MyBatis Plus 分页的基本使用方法,展示自己对基础的掌握。

  2. 原理分析:解释分页插件的实现原理,比如拦截器的作用,SQL 的改写过程,体现自己对底层原理的理解。

  3. 常见问题及解决方案:针对单表大分页、多表关联分页、分页与排序结合等问题,分别阐述遇到的问题、原因分析和解决方法,展示自己的实战经验。

  4. 深入优化:介绍分页插件的配置、自定义分页参数等内容,体现自己对框架的深入理解和扩展能力。

  5. 实战案例:结合实际项目中的案例,说明如何进行分页优化,展示自己的问题解决能力和项目经验。

总之,回答时要条理清晰,层次分明,既有理论分析,又有实战经验,让面试官感受到你对这个问题的深入理解和扎实的技术功底。

六、总结

MyBatis Plus 的分页功能虽然强大,但在面对大数据量和复杂查询时仍需优化。通过合理利用主键索引、优化 SQL 查询、合理配置分页插件以及引入缓存机制,可以有效提升分页查询的效率。在实际开发中,应根据具体业务需求灵活选择优化策略,确保系统的高性能和稳定性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值