如何使用Spring Data Pageable跨两个Repository实现分页查询
跨两个Spring Data JPA Repository实现联合分页解决方案
问题根源
你的代码在处理第二页时,错误地将当前请求的pageNumber直接传递给第二个Repository,导致从第二个Repository的第10条数据(对应page=1, size=10)开始查询,但实际上第一页仅从第二个Repository取了5条数据,第二页应该从第二个Repository的第5条数据开始获取。
解决方案思路
要实现正确的联合分页,需要基于全局偏移量来分别计算两个Repository的查询参数:
- 先获取两个Repository符合条件的总数据量,用于构造最终的分页结果
- 计算当前请求的全局偏移量(
pageNumber * pageSize),即当前页第一条数据在全局数据集的位置 - 根据全局偏移量与第一个Repository总条数的关系,分两种场景查询数据:
- 全局偏移量小于第一个Repository总条数:先从第一个Repository取部分或全部数据,再从第二个Repository补充剩余所需数据
- 全局偏移量大于等于第一个Repository总条数:直接从第二个Repository计算对应的相对偏移量和分页参数,获取正确区间的数据
代码实现
// 1. 先获取两个数据源的符合条件的总条数(需确保Repository有对应的count方法) long totalFirst = firstRepository.countByCondition(); long totalSecond = secondRepository.countByCondition(); long total = totalFirst + totalSecond; int pageSize = page.getPageSize(); int pageNumber = page.getPageNumber(); long globalOffset = (long) pageNumber * pageSize; List<Order> combinedContent = new ArrayList<>(); if (globalOffset < totalFirst) { // 场景1:当前页包含第一个数据源的部分数据 // 计算从第一个数据源需要获取的数量 int firstFetchCount = (int) Math.min(pageSize, totalFirst - globalOffset); // 构造第一个数据源的分页参数:偏移量对应page,取需要的数量 PageRequest firstPageReq = PageRequest.of((int) (globalOffset / pageSize), firstFetchCount); Page<Order> firstPage = firstRepository.findByCondition(firstPageReq) .map(mapper::entityToPojo); combinedContent.addAll(firstPage.getContent()); // 如果还需要从第二个数据源补充数据 int remainingCount = pageSize - combinedContent.size(); if (remainingCount > 0 && totalSecond > 0) { PageRequest secondPageReq = PageRequest.of(0, remainingCount); Page<Order> secondPage = secondRepository.findByCondition(secondPageReq) .map(mapper::otherEntityToPojo); combinedContent.addAll(secondPage.getContent()); } } else { // 场景2:当前页数据全部来自第二个数据源 // 计算相对于第二个数据源的偏移量 long secondOffset = globalOffset - totalFirst; // 转换为第二个数据源的pageNumber int secondPageNum = (int) (secondOffset / pageSize); // 计算需要获取的数量(处理最后一页不足pageSize的情况) int secondFetchCount = (int) Math.min(pageSize, totalSecond - secondOffset); PageRequest secondPageReq = PageRequest.of(secondPageNum, secondFetchCount); Page<Order> secondPage = secondRepository.findByCondition(secondPageReq) .map(mapper::otherEntityToPojo); combinedContent.addAll(secondPage.getContent()); } // 构造正确的分页结果,传入原始Pageable和总条数 return new PageImpl<>(combinedContent, page, total);
关键说明
- 总条数计算:必须提前获取两个数据源的总条数,才能让前端正确展示总页数、总数据量等分页信息
- 全局偏移量:是分页逻辑的核心,确保每一页的数据区间在全局数据集中不重叠、不遗漏
- 分场景处理:精准控制两个数据源的查询范围,避免出现像原代码中跨页查询错误的问题
内容的提问来源于stack exchange,提问作者Saron Ciupe
相关产品推荐
相关产品推荐

