Spring Boot原生查询分页抛出NumberFormatException问题排查
解决Spring Boot 1.5.x原生查询中Pageable排序导致的NumberFormatException问题
这个问题的根源在于Spring Boot 1.5.x对应的Spring Data JPA版本(Ingalls系列)不支持在原生查询中直接用?#{#pageable}解析排序条件——它会把整个Pageable对象序列化成二进制字符串(就是你看到的aced开头的内容)传入SQL,H2数据库自然无法将其解析为合法的排序字段,从而抛出NumberFormatException。
下面给你几个可行的解决方案,按推荐程度排序:
方案一:通过SpEL手动构建ORDER BY子句
直接在原生查询中用SpEL表达式提取Pageable中的排序属性和方向,动态生成合法的ORDER BY语句。这种方式不需要修改项目依赖或自定义Repository实现,最直接:
@Repository public interface VacancyRepository extends PagingAndSortingRepository<Vacancy, Long> { @Query(value = "SELECT * FROM vacancies WHERE location ILIKE %:location% AND CONCAT(title, ' ', description) ILIKE %:keyword% " + "ORDER BY #{#pageable.sort.isEmpty ? 'id ASC' : #pageable.sort.map(s -> s.property || ' ' || s.direction).join(', ')}", countQuery = "SELECT COUNT(*) FROM vacancies WHERE location ILIKE %:location% AND CONCAT(title, ' ', description) ILIKE %:keyword%", nativeQuery = true) Page<Vacancy> search(@Param("location") String location, @Param("keyword") String keyword, Pageable pageable); }
关键细节:
#{#pageable.sort.isEmpty ? 'id ASC' : ...}:处理排序参数为空的情况,默认按id升序排序,避免SQL语法错误#pageable.sort.map(s -> s.property || ' ' || s.direction).join(', '):遍历Sort中的每个排序项,拼接成字段名 方向的格式,多个排序项用逗号分隔(比如传入sort=id,title,desc会生成id ASC, title DESC)- 注意这里用
||做字符串拼接,因为H2数据库支持这个语法;如果是其他数据库(比如MySQL)要换成+或者CONCAT函数
方案二:自定义Repository实现类
如果SpEL的写法在你的版本中不支持(比如某些旧版Spring Data对SpEL集合操作支持有限),可以自定义Repository的实现类,手动拼接SQL的排序部分:
- 先定义一个自定义接口:
public interface VacancyRepositoryCustom { Page<Vacancy> search(String location, String keyword, Pageable pageable); }
- 实现这个接口:
@Repository public class VacancyRepositoryImpl implements VacancyRepositoryCustom { @PersistenceContext private EntityManager entityManager; @Override public Page<Vacancy> search(String location, String keyword, Pageable pageable) { // 基础查询SQL String baseSql = "SELECT * FROM vacancies WHERE location ILIKE :location AND CONCAT(title, ' ', description) ILIKE :keyword"; // 拼接排序条件 StringBuilder orderBy = new StringBuilder(); if (!pageable.getSort().isEmpty()) { orderBy.append(" ORDER BY "); pageable.getSort().forEach(order -> { orderBy.append(order.getProperty()).append(" ").append(order.getDirection()).append(", "); }); // 移除最后一个逗号 orderBy.delete(orderBy.length() - 2, orderBy.length()); } else { // 默认排序 orderBy.append(" ORDER BY id ASC"); } String querySql = baseSql + orderBy.toString(); // 创建原生查询 Query query = entityManager.createNativeQuery(querySql, Vacancy.class); query.setParameter("location", "%" + location + "%"); query.setParameter("keyword", "%" + keyword + "%"); // 设置分页参数 query.setFirstResult((int) pageable.getOffset()); query.setMaxResults(pageable.getPageSize()); // 执行查询获取结果 List<Vacancy> content = query.getResultList(); // 执行count查询 String countSql = "SELECT COUNT(*) FROM vacancies WHERE location ILIKE :location AND CONCAT(title, ' ', description) ILIKE :keyword"; Query countQuery = entityManager.createNativeQuery(countSql); countQuery.setParameter("location", "%" + location + "%"); countQuery.setParameter("keyword", "%" + keyword + "%"); long total = ((Number) countQuery.getSingleResult()).longValue(); return new PageImpl<>(content, pageable, total); } }
- 修改原Repository接口,继承自定义接口:
@Repository public interface VacancyRepository extends PagingAndSortingRepository<Vacancy, Long>, VacancyRepositoryCustom { }
这种方式更灵活,适合复杂的查询场景,但代码量会多一些。
方案三:升级Spring Boot版本(如果允许)
Spring Boot 2.x及以上对应的Spring Data JPA版本(Kay系列及以后)已经支持在原生查询中直接使用?#{#pageable}来解析分页和排序条件,如果你能升级项目的Spring Boot版本,这个问题会自动解决。不过升级需要考虑兼容性问题,比如依赖的其他库是否支持新版本。
内容的提问来源于stack exchange,提问作者Mironor




