使用JPA规范时如何在排序中应用合并表达式

时间:2019-03-14 20:33:45

标签: java hibernate jpa spring-data-jpa spring-data

我正在使用规范使用JPA构建查询解决方案。

我使用Spring Data进行了如下排序和分页,并且使用Spring存储库样式方法进行查询。

//Building specification with parameters
MySpecification specification=new MySpecification(List<SearchParameters>);
//Setting sort order and pagination
Sort sortOrder = new Sort(Direction.ASC,"<field_to_sort>");
PageRequest pageRequest = PageRequest.of(pageIndex, pageSize, sortOrder);
repository.findAll(specification, pageRequest);

现在我可以根据 coalesce表达式对字段进行排序。 我了解spring数据Sort只能具有用于排序的属性名称,如上所示。

我尝试在JPA规范中设置排序顺序,但这也无济于事。

在坚持JPA规范的同时,是否可以将排序顺序设置为合并表达式?

任何帮助将不胜感激。

1 个答案:

答案 0 :(得分:0)

从Spring Data 2.2开始,它仍然无法使用自己的类SortOrder来调用SQL函数,这使得使用Specification和从存储库编码中完全抽象变得更加困难。 >

有一种变通办法是仅将手工制作的Specification用于排序目的,这将使用JPA的CriteriaBuilder功能将排序设置为结果查询。

此类Specification的示例(为了简洁起见,使用基于字符串的属性名称,也可以使用JPA元模型类):

    public static Specification<Author> orderByPseudonymOrFullName() {
        return (user, cq, cb) -> {
            Expression<?> functionBasedAttributePath =
                    cb.lower(cb.coalesce(user.get("pseudonym"), user.get("fullName")));
            Expression<Author> pathToUserId = user.get("id");

            List<Order> orderByList = new ArrayList<>();
            orderByList.add(cb.asc(functionBasedAttributePath));
            // adding second sorting attribute to ensure stable order of users with same pseudonym
            orderByList.add(cb.asc(pathToUserId));

            cq.orderBy(orderByList);

            Specification<Author> emptySpec = Specification.where(null);//empty no-op specification
            return emptySpec.toPredicate(user, cq, cb);
        };
    }

然后像往常一样将其与Sort.unsorted()一起使用,Spring Data不会将其考虑在内:

authorRepository.findAll(AuthorSpecifications.orderByPseudonymOrFullName(),
                             PageRequest.of(page, size, Sort.unsorted()));

通过包含测试以证明其有效的示例的示例链接到GitHub存储库:https://github.com/zzz3bra/customSortingWithSpringData

为什么起作用?

SimpleJpaRepository类是默认的存储库实现,包含下一个相关的代码片段:

    protected <S extends T> TypedQuery<S> getQuery(@Nullable Specification<S> spec, Class<S> domainClass, Sort sort) {

        CriteriaBuilder builder = em.getCriteriaBuilder();
        CriteriaQuery<S> query = builder.createQuery(domainClass);

        Root<S> root = applySpecificationToCriteria(spec, domainClass, query);
        query.select(root);

        if (sort.isSorted()) {
            query.orderBy(toOrders(sort, root, builder));
        }

        return applyRepositoryMethodMetadata(em.createQuery(query));
    }

因此,如果sort未排序,Spring Data将不会调用query的{​​{1}}方法来覆盖orderBy已设置的排序。