为何Pageable始终返回last为true?实际并非最后一页
请求URL:
{{MaterialBaseURI}}/materials/pagination?confType={{confType}}&pageNumber=0&pageSize=5&title=link
我的分页实现代码:
Optional<Pageable> optionalPageable = pageNumber.map(integer -> PageRequest.of(integer, pageSize.get(), Sort.by(Sort.Order.asc(TITLE)))); Page<MaterialMetadata> materialMetadata = materialRepository.findAll((Specification<MaterialMetadata>) (root, criteriaQuery, criteriaBuilder) -> { Predicate predicate = criteriaBuilder.conjunction(); predicate = criteriaBuilder.and(predicate, criteriaBuilder.equal(root.get("conferenceType"), com.logmein.gotomaterialservice.entity.enums.ConferenceType.valueOf(String.valueOf(conferenceType)))); if (!StringUtils.isEmpty(title)) { predicate = criteriaBuilder.and(predicate, criteriaBuilder.like(root.get("title"), "%" + title + "%"));} if (startDateTime != null && endDateTime != null && startDateTime != 0 && endDateTime != 0) { predicate = criteriaBuilder.and(predicate, criteriaBuilder.between(root.get("createdTime"), startDateTime, endDateTime));} else if (startDateTime != null && startDateTime != 0) { predicate = criteriaBuilder.and(predicate, criteriaBuilder.greaterThanOrEqualTo(root.get("createdTime"), startDateTime));} else if (endDateTime != null && endDateTime != 0) { predicate = criteriaBuilder.and(predicate, criteriaBuilder.lessThanOrEqualTo(root.get("createdTime"), endDateTime)); } return predicate; }, optionalPageable.get());
接口返回结果:
{ "content": [ ... ], "pageable": { "sort": { "empty": false, "unsorted": false, "sorted": true }, "offset": 0, "pageNumber": 0, "pageSize": 5, "paged": true, "unpaged": false }, "totalPages": 1, "totalElements": 5, "last": true, "size": 5, "number": 0, "sort": { "empty": false, "unsorted": false, "sorted": true }, "numberOfElements": 5, "first": true, "empty": false }
实际符合条件的记录有15条,pageSize设为5时,last应该为false,但始终返回true,排查不到问题所在。
问题原因
Spring Data JPA的Page对象中last、totalPages、totalElements的值完全依赖于查询时对符合条件的总记录数的统计。出现这个问题的核心是你的查询条件实际匹配的记录数只有5条,而非预期的15条,或者分页查询的统计逻辑出现异常。
排查与修复步骤
验证
like查询的匹配结果
直接在数据库执行SQL验证匹配总数,替换?为实际的confType值:SELECT COUNT(*) FROM material_metadata WHERE conference_type = ? AND title LIKE '%link%';如果返回结果是5,说明
title的匹配逻辑确实只筛选出5条数据,检查数据库中记录的title是否真的包含"link",或参数title是否被意外修改。检查
conferenceType的转换正确性
代码中用ConferenceType.valueOf(String.valueOf(conferenceType))转换枚举,添加日志输出转换后的枚举值,确认是否与数据库中conference_type字段的存储值一致。如果枚举转换错误,会过滤掉大量不匹配的数据。排查日期过滤条件的影响
暂时注释掉所有日期相关的过滤代码,重新调用接口查看totalElements是否变为15。如果是,说明startDateTime或endDateTime参数的取值范围过窄,过滤了大部分符合条件的记录。手动统计总记录数验证
在代码中添加手动统计逻辑,确认查询条件的实际匹配数:long totalCount = materialRepository.count((Specification<MaterialMetadata>) (root, criteriaQuery, criteriaBuilder) -> { Predicate predicate = criteriaBuilder.conjunction(); predicate = criteriaBuilder.and(predicate, criteriaBuilder.equal(root.get("conferenceType"), com.logmein.gotomaterialservice.entity.enums.ConferenceType.valueOf(String.valueOf(conferenceType)))); if (!StringUtils.isEmpty(title)) { predicate = criteriaBuilder.and(predicate, criteriaBuilder.like(root.get("title"), "%" + title + "%")); } if (startDateTime != null && endDateTime != null && startDateTime != 0 && endDateTime != 0) { predicate = criteriaBuilder.and(predicate, criteriaBuilder.between(root.get("createdTime"), startDateTime, endDateTime)); } else if (startDateTime != null && startDateTime != 0) { predicate = criteriaBuilder.and(predicate, criteriaBuilder.greaterThanOrEqualTo(root.get("createdTime"), startDateTime)); } else if (endDateTime != null && endDateTime != 0) { predicate = criteriaBuilder.and(predicate, criteriaBuilder.lessThanOrEqualTo(root.get("createdTime"), endDateTime)); } return predicate; }); System.out.println("Total matching records: " + totalCount);如果输出的
totalCount是5,说明查询条件确实只匹配5条数据;如果是15,说明Spring Data JPA分页的统计逻辑存在异常,需检查依赖版本或配置是否有问题。
内容的提问来源于stack exchange,提问作者Yashika Chandra

