使用 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)
方法路由到executor
的findAll(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> {
[...]
}
哎呀!但它有效...
我正在使用 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)
方法路由到executor
的findAll(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> {
[...]
}
哎呀!但它有效...