带筛选功能的分页服务实现失败,附代码寻求解决方案
问题排查与解决方案
核心问题点
1. 动态字段参数绑定错误(筛选完全失效)
原生SQL中,:sortField会被Spring Data JPA当作字符串参数处理,而非数据库字段名。实际执行的SQL会变成:
SELECT * FROM HISTORY_BANK WHERE '字段名' LIKE '%xxx%' ...
这是在判断字符串字面量(比如'name')是否匹配筛选关键词,而非对应字段的实际值,导致筛选条件无效,直接返回全表数据。
2. 动态排序语法错误
:sortField和:sortOrder作为字符串参数代入后,ORDER BY子句会变成:
ORDER BY '字段名' 'ASC'
不符合SQL语法规范,数据库无法正确解析排序规则,导致排序失效或直接报错。
3. 统计查询未关联筛选条件
countQuery直接查询全表总数,不包含筛选逻辑,导致分页组件的总页数、总条数都是全表数据,和实际筛选后的结果集完全不匹配。
4. 页码兼容性潜在问题
PageRequest.of(page, size)的页码是从0开始计数,如果前端传入的页码是从1开始(多数前端分页组件默认第一页为1),会导致分页数据偏移,出现第一页为空或数据错位的情况。
修复方案
方案一:使用Spring Data JPA Specification(推荐,无SQL注入风险)
通过动态构建查询条件和排序规则,避免直接拼接SQL:
Repository层修改
public interface HistoryBankRepository extends JpaRepository<HistoryBank, Long>, JpaSpecificationExecutor<HistoryBank> { }
Service层修改
public PaginationList<HistoryBankWrapper, HistoryBank> findByFilter(String keyfilter, String sortField, String sortOrder, int page, int size) { // 构建筛选条件 Specification<HistoryBank> spec = (root, query, cb) -> { if (StringUtils.isBlank(keyfilter)) { return cb.conjunction(); // 无筛选条件时返回全部数据 } // 匹配指定字段的内容 return cb.like(root.get(sortField), "%" + keyfilter + "%"); }; // 构建排序规则 Sort sort = Sort.by(Sort.Direction.fromString(sortOrder), sortField); // 若前端页码从1开始,需转换为0开始:PageRequest.of(page - 1, size, sort) Pageable paging = PageRequest.of(page, size, sort); Page<HistoryBank> historyPage = historyBankRepository.findAll(spec, paging); List<HistoryBank> historyList = historyPage.getContent(); List<HistoryBankWrapper> historyWrapperList = toWrapperList(historyList); return new PaginationList<>(historyWrapperList, historyPage); }
方案二:修正原生SQL(需防范SQL注入)
如果坚持使用原生SQL,需用SPEL表达式动态插入字段名,同时同步countQuery的筛选逻辑:
Repository层修改
@Query(value = "SELECT * FROM HISTORY_BANK WHERE #{#sortField} LIKE '%' || :keyFilter || '%' ORDER BY #{#sortField} #{#sortOrder}", countQuery = "SELECT count(*) FROM HISTORY_BANK WHERE #{#sortField} LIKE '%' || :keyFilter || '%'", nativeQuery = true) Page<HistoryBank> findAllFilter(@Param("keyFilter") String keyfilter, @Param("sortOrder") String sortOrder, @Param("sortField") String sortField, Pageable paging);
注意:此方式存在SQL注入风险,必须在Service层对
sortField做白名单校验(比如限制只能传入数据库已存在的字段名),拦截恶意输入。
额外适配:页码转换
如果前端传入的页码从1开始,Service层需要转换为JPA要求的0起始页码:
Pageable paging = PageRequest.of(page - 1, size, sort);
内容的提问来源于stack exchange,提问作者Juan Kalyzta
相关产品推荐
相关产品推荐

