使用 Sort 和 querydsl Predicate 但没有 Pageable 创建集合资源

Create a collection resource with Sort and querydsl Predicate but no Pageable

我正在使用 Spring data Rest 并寻找一种方法来创建具有排序和谓词(使用 querydsl)功能但没有寻呼机的集合(或搜索)资源。

到现在为止,我只需要禁用寻呼机但具有排序功能。使用以下命令并调用 GET /userAccounts/search/noPager :

效果很好
public interface ReportRepository 
    extends JpaRepository<Report, Integer>{

    /**
     * Non paged data with Sort capabilities
     */
    @RestResource(path="noPager")
    List<Report> findAllBy(Sort sort);

现在我需要添加谓词功能。如果我执行以下操作:

public interface UserAccountRepository 
    extends JpaRepository<UserAccount, Integer>,
            QueryDslPredicateExecutor<UserAccount> {

    /**
     * Non paged data with Sort capabilities
     */
    @RestResource(path="noPager")
    List<UserAccount> findAllBy(Predicate predicate, Sort sort);

调用 GET /userAccounts/search/noPager 时出现以下错误:

java.lang.IllegalArgumentException: Unable to detect parameter names for query method fr.texsys.datemplus.dm.domain.data.UserAccountRepository.findAllBy! Use @Param or compile with -parameters on JDK 8. at org.springframework.data.repository.support.ReflectionRepositoryInvoker.prepareParameters(ReflectionRepositoryInvoker.java:235) ~[spring-data-commons-1.12.3.RELEASE.jar!/:na] at org.springframework.data.repository.support.ReflectionRepositoryInvoker.invokeQueryMethod(ReflectionRepositoryInvoker.java:206) ~[spring-data-commons-1.12.3.RELEASE.jar!/:na]

更奇怪的是,我尝试将 QueryDSL 与 CrudRepository 一起使用,它没有寻呼机功能。如果我使用 QueryDslPredicateExecutor<UserAccount> 扩展 CrudRepository,并调用集合资源 GET /userAccounts,则寻呼机被激活。

没有QueryDslPredicateExecutor:

public interface UserAccountRepository 
    extends CrudRepository<UserAccount, Integer> {

没有寻呼机:

{
  "_embedded" : {
    "userAccounts" : [...]
  },
  "_links" : {
    "self" : {
      "href" : "http://localhost:15571/userAccounts"
    },
    "profile" : {
      "href" : "http://localhost:15571/profile/userAccounts"
    }
  }
}

但是 QueryDslPredicateExecutor:

public interface UserAccountRepository 
    extends CrudRepository<UserAccount, Integer>,
            QueryDslPredicateExecutor<UserAccount> {

寻呼机已激活

{
  "_embedded" : {
    "userAccounts" : [ ... ]
  },
  "_links" : {
    "self" : {
      "href" : "http://localhost:15571/userAccounts"
    },
    "profile" : {
      "href" : "http://localhost:15571/profile/userAccounts"
    }
  },
  "page" : {
    "size" : 20,
    "totalElements" : 3,
    "totalPages" : 1,
    "number" : 0
  }
}

扩展 JpaSpecificationExecutor so you can use a Specification 怎么样?

创建您自己的规范实现将允许您创建搜索过滤器并按照您的需要对其进行排序(通过访问 CriteriaQuery)。

希望对您有所帮助...

所以我设法做了我想做的事,但这真的很麻烦,希望有一种我还没有发现的更轻便更方便的方法:

1:创建自定义QuerydslRepositoryInvokerAdapter并将invokeFindAll(Pageable pageable)方法路由到executorfindAll(Sort sort):

public class CustomQuerydslRepositoryInvokerAdapter 
        extends QuerydslRepositoryInvokerAdapter {

    private final QueryDslPredicateExecutor<Object> executor;
    private final Predicate predicate;

    public CustomQuerydslRepositoryInvokerAdapter(
            RepositoryInvoker delegate, 
            QueryDslPredicateExecutor<Object> executor,
            Predicate predicate) {
        super(delegate, executor, predicate);

        Assert.notNull(delegate, "Delegate RepositoryInvoker must not be null!");
        Assert.notNull(executor, "QuerydslPredicateExecutor must not be null!");

        this.executor = executor;
        this.predicate = predicate;
    }

    @Override
    public Iterable<Object> invokeFindAll(Pageable pageable) {
        return executor.findAll(predicate, pageable.getSort());
    }

}

2:创建自定义RootResourceInformationHandlerMethodArgumentResolver

public class CustomQuerydslAwareRootResourceInformationHandlerMethodArgumentResolver 
    extends RootResourceInformationHandlerMethodArgumentResolver {

    private final Repositories repositories;
    private final QuerydslPredicateBuilder predicateBuilder;
    private final QuerydslBindingsFactory factory;

    public CustomQuerydslAwareRootResourceInformationHandlerMethodArgumentResolver(Repositories repositories,
            RepositoryInvokerFactory invokerFactory, ResourceMetadataHandlerMethodArgumentResolver resourceMetadataResolver,
            QuerydslPredicateBuilder predicateBuilder, QuerydslBindingsFactory factory) {

        super(repositories, invokerFactory, resourceMetadataResolver);

        this.repositories = repositories;
        this.predicateBuilder = predicateBuilder;
        this.factory = factory;
    }

    /* 
     * (non-Javadoc)
     * @see org.springframework.data.rest.webmvc.config.RootResourceInformationHandlerMethodArgumentResolver#postProcess(org.springframework.data.repository.support.RepositoryInvoker, java.lang.Class, java.util.Map)
     */
    @Override
    @SuppressWarnings({ "unchecked" })
    protected RepositoryInvoker postProcess(MethodParameter parameter, RepositoryInvoker invoker,
            Class<?> domainType, Map<String, String[]> parameters) {

        Object repository = repositories.getRepositoryFor(domainType);

        if (!CustomQueryDslPredicateExecutor.class.isInstance(repository)
                || !parameter.hasParameterAnnotation(QuerydslPredicate.class)) {
            return invoker;
        }

        ClassTypeInformation<?> type = ClassTypeInformation.from(domainType);

        QuerydslBindings bindings = factory.createBindingsFor(null, type);
        Predicate predicate = predicateBuilder.getPredicate(type, toMultiValueMap(parameters), bindings);

        return new CustomQuerydslRepositoryInvokerAdapter(invoker, (QueryDslPredicateExecutor<Object>) repository, predicate);
    }

    private static MultiValueMap<String, String> toMultiValueMap(Map<String, String[]> source) {

        MultiValueMap<String, String> result = new LinkedMultiValueMap<String, String>();

        for (String key : source.keySet()) {
            result.put(key, Arrays.asList(source.get(key)));
        }

        return result;
    }


}

3:覆盖RootResourceInformationHandlerMethodArgumentResolver Bean定义:

@Configuration
public class CustomRepositoryRestMvcConfiguration extends RepositoryRestMvcConfiguration {

    @Autowired ApplicationContext applicationContext;

    @Override
    @Bean
    public RootResourceInformationHandlerMethodArgumentResolver repoRequestArgumentResolver() {

        if (QueryDslUtils.QUERY_DSL_PRESENT) {

            QuerydslBindingsFactory factory = applicationContext.getBean(QuerydslBindingsFactory.class);
            QuerydslPredicateBuilder predicateBuilder = new QuerydslPredicateBuilder(defaultConversionService(),
                    factory.getEntityPathResolver());

            return new CustomQuerydslAwareRootResourceInformationHandlerMethodArgumentResolver(repositories(),
                    repositoryInvokerFactory(defaultConversionService()), resourceMetadataHandlerMethodArgumentResolver(),
                    predicateBuilder, factory);
        }

        return new RootResourceInformationHandlerMethodArgumentResolver(repositories(),
                repositoryInvokerFactory(defaultConversionService()), resourceMetadataHandlerMethodArgumentResolver());
    }

}

4: 创建自定义 QueryDslPredicateExecutor:

public interface CustomQueryDslPredicateExecutor<T> extends QueryDslPredicateExecutor<T> {

    Iterable<T> findAll(Predicate predicate, Sort sort);

}

5: 然后将其应用到存储库

public interface UserAccountRepository 
    extends CrudRepository<UserAccount, Integer>,
            CustomQueryDslPredicateExecutor<UserAccount> {
    [...] 
}

哎呀!但它有效...