【问题标题】:How sort by string field as numeric value in JpaSpecificationExecutor#findAll如何在 JpaSpecificationExecutor#findAll 中按字符串字段排序为数值
【发布时间】:2020-07-20 14:28:03
【问题描述】:

我有带有字符串字段的实体:

@Data
@Entity
@Table(name = "document")
public class Document {
  ...
  private String docNumber;
  ...
}

我使用 JpaSpecificationExecutor#findAll 来查找行:

Page<T> findAll(@Nullable Specification<T> spec, Pageable pageable);

我需要按 docNumber 作为数值排序。 我尝试将其设置为 pageable#sortnew JpaSort("length(docNumber)")(就像从这里回答 - https://stackoverflow.com/a/46215275),但得到异常:

org.springframework.data.mapping.PropertyReferenceException: No property (length(docNumber)) found for type Payroll!
    at org.springframework.data.mapping.PropertyPath.<init>(PropertyPath.java:94)
    at org.springframework.data.mapping.PropertyPath.create(PropertyPath.java:358)
    at org.springframework.data.mapping.PropertyPath.create(PropertyPath.java:334)
    at org.springframework.data.mapping.PropertyPath.lambda$from$0(PropertyPath.java:287)
    at java.util.concurrent.ConcurrentMap.computeIfAbsent(ConcurrentMap.java:324)
    at org.springframework.data.mapping.PropertyPath.from(PropertyPath.java:269)
    at org.springframework.data.mapping.PropertyPath.from(PropertyPath.java:252)
    at org.springframework.data.jpa.repository.query.QueryUtils.toJpaOrder(QueryUtils.java:563)
    at org.springframework.data.jpa.repository.query.QueryUtils.toOrders(QueryUtils.java:516)
    at org.springframework.data.jpa.repository.support.SimpleJpaRepository.getQuery(SimpleJpaRepository.java:630)
    at org.springframework.data.jpa.repository.support.SimpleJpaRepository.getQuery(SimpleJpaRepository.java:584)
    at org.springframework.data.jpa.repository.support.SimpleJpaRepository.findAll(SimpleJpaRepository.java:387)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.springframework.data.repository.core.support.RepositoryComposition$RepositoryFragments.invoke(RepositoryComposition.java:377)
    at org.springframework.data.repository.core.support.RepositoryComposition.invoke(RepositoryComposition.java:200)
    at org.springframework.data.repository.core.support.RepositoryFactorySupport$ImplementationMethodExecutionInterceptor.invoke(RepositoryFactorySupport.java:641)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)
    at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.doInvoke(RepositoryFactorySupport.java:605)
    at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:590)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)
    at org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor.invoke(DefaultMethodInvokingMethodInterceptor.java:59)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)
    at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:294)
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:98)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)
    at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:139)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)
    at org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor.invoke(CrudMethodMetadataPostProcessor.java:135)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)
    at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)
    at org.springframework.data.repository.core.support.SurroundingTransactionDetectorMethodInterceptor.invoke(SurroundingTransactionDetectorMethodInterceptor.java:61)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:212)
    at com.sun.proxy.$Proxy285.findAll(Unknown Source)

如何按 docNumber 作为数值排序?

PS:Specification&lt;T&gt; spec 具有非平凡逻辑的参数,我认为不能用 @Query 代替(就像这里的回答 - https://stackoverflow.com/a/46215275

【问题讨论】:

  • length 是一个 Java 函数,在 JPQL 中不存在(也不会将 VARCHAR 转换为传统意义上的 NUMBER。在此处尝试建议的方法:@ 987654323@
  • 是的,length 不会转换为 number,但我们可以根据长度创建所需的排序。示例:select * from document order by length(doc_number), doc_number;
  • 有趣的方法。

标签: spring spring-boot spring-data-jpa


【解决方案1】:

我实现了一个变体,其中初始规范添加了从排序对象转换而来的 orderBy 规范,并从 PageRequest 中删除了排序:

    private Page<E> search(Specification<E> spec, PageRequest pageRequest) {
        spec = addOrderByToSpecification(spec, pageRequest.getSort());
        return repository.findAll(spec, pageRequestWithoutSort(pageRequest));
    }

    private Specification<E> addOrderByToSpecification(Specification<E> specification, Sort sort) {
        if (sort.isUnsorted()) {
            return specification;
        }
        Specification<E> orderSpecification = (root, query, criteriaBuilder) -> {
            List<Order> criteriaOrders = sort.stream()
                    .map(order -> mapSortOrderToCriteriaOrder(order, root, criteriaBuilder))
                    .collect(Collectors.toList());
            query.orderBy(criteriaOrders);
            return criteriaBuilder.and();
        };
        return specification.and(orderSpecification);
    }

    private Order mapSortOrderToCriteriaOrder(Sort.Order sortOrder, Root<?> root, CriteriaBuilder criteriaBuilder) {
        Expression<?> expression = root.get(sortOrder.getProperty());
        if (DOC_NUMBER.equals(sortOrder.getProperty())) {
            expression = expression.as(Integer.class);
        }
        return sortOrder.isAscending() ? criteriaBuilder.asc(expression) : criteriaBuilder.desc(expression);
    }

    private PageRequest pageRequestWithoutSort(PageRequest pageRequest) {
        return PageRequest.of(pageRequest.getPageNumber(), pageRequest.getPageSize());
    }

【讨论】:

  • 完美!我尝试通过JpaSort.unsafesubstring 函数添加到SORT BY,但它不适用于规范。我试图在stackoverflow中找到一个解决方案很长一段时间,只有你的方法能帮助我,谢谢!
猜你喜欢
  • 2013-07-27
  • 2015-12-20
  • 1970-01-01
  • 1970-01-01
  • 2015-06-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多