You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

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的排序部分:

  1. 先定义一个自定义接口:
public interface VacancyRepositoryCustom {
    Page<Vacancy> search(String location, String keyword, Pageable pageable);
}
  1. 实现这个接口:
@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);
    }
}
  1. 修改原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

火山引擎 最新活动