Spring 使用 PostgreSQL JSONB 进行 JPA 排序和分页

Spring JPA Sorting and Paging with PostgreSQL JSONB

我正在使用 Spring JPA 来管理我的 PostgreSQL 数据。此数据大量使用了 PostgreSQL 9.4.

中的 jsonb 数据类型

我的 table(称为 jobtable),简化后的样子是这样的:

id, bigint | data, jsonb
--------------------------------
1          | {"name": "Hello"}
2          | {"name": "Testing"}

使用 Spring JPA,我正在定义一个 CrudRepository 接口,以便对此 table 进行一些查询。对于 jsonb 特定的事情,我正在使用 nativeQuery = true 以便我可以使用此 PostgreSQL 类型。

例如,我可以这样查询我的属性:

@Query(
 value = "select * from jobtable where data ->> 'name' = ?1",
 nativeQuery = true)
JobEntity getJobByName(String name);

这个查询工作得很好。

当我尝试将分页与使用 jsonb 的本机查询一起使用时,我的问题出现了。我的查询是这样的:

@Query(
 value = "select * from jobtable \n#pageable\n", 
 countQuery = "select count(*) from jobtable", 
 nativeQuery = true)
Page<JobEntity> getJobList(Pageable pageable);

我包含 Pageable 参数并这样调用函数:

Pageable pageable = new PageRequest(0, 10, Direction.DESC, "data ->> 'name'");
Page<JobEntity> results = myDao.getJobList(pageable);

此代码不起作用,并产生以下错误:

org.springframework.dao.InvalidDataAccessApiUsageException: 
 Sort expression 'data ->> 'name': DESC' must only contain property references or aliases used in the select clause. If you really want to use something other than that for sorting, please use JpaSort.unsafe(…)!

我不知道这个错误是怎么回事。我认为这与不正确地理解 PageRequest 对象中的 sortBy 参数有关,但我不确定当我打算对键进行排序时如何构建 PageRequest 对象在我的 jsonb 对象中。

我可以构建 Raw SQL 到 PostgreSQL 看起来像 select * from job order by data ->> 'jobId' desc limit 10 但我宁愿使用 Pageable 接口和 Spring JPA 所以我可以使用 @Query 符号,而不必自己在代码中明确定义任何内容。

尝试按如下方式创建 Pageable:

Pageable p = PageRequest.of(1,10,
            JpaSort.unsafe(Direction.DESC, "data->>'name'"));

这应该可以消除异常。

对于那些在你使用 org.springframework.data.jpa.domain.Specification 的地方发现这个问题的人,这里是我如何让它在 toPredicate 方法中工作的方法

Expression exp = criteriaBuilder.function("jsonb_extract_path_text", 
    String.class, 
    root.get("data"), 
    criteriaBuilder.literal("name"));

switch (direction){
    case ASC:
        criteriaQuery.orderBy(criteriaBuilder.asc(exp));
        break;
    case DESC:
    default:
        criteriaQuery.orderBy(criteriaBuilder.desc(exp));
        break;

}