Eri*_*ley 15 java spring querydsl spring-data-jpa spring-data-rest
Meetup
实体
Map<String,String> properties
领域
MEETUP_PROPERTY
作为一个坚持在一张桌子@ElementCollection
MeetupRepository
QueryDslPredicateExecutor<Meetup>
一个Web查询
GET /api/meetup?properties[aKey]=aValue
Run Code Online (Sandbox Code Playgroud)
仅返回具有指定键和值的属性条目的Meetup:aKey = aValue.
但是,这对我不起作用.我错过了什么?
简单字段起作用,如名称和描述:
GET /api/meetup?name=whatever
Run Code Online (Sandbox Code Playgroud)
与参与者一样,收集字段起作用
GET /api/meetup?participants.name=whatever
Run Code Online (Sandbox Code Playgroud)
但不是这个Map字段.
我已经尝试通过拥有存储库来自定义绑定
extend QuerydslBinderCustomizer<QMeetup>
Run Code Online (Sandbox Code Playgroud)
并凌驾于
customize(QuerydslBindings bindings, QMeetup meetup)
Run Code Online (Sandbox Code Playgroud)
方法,但是当customize()
方法被命中时,lambda内的绑定代码不是.
编辑:了解这是因为QuerydslBindings
评估查询参数的方法不会让它与pathSpecs
它内部持有的地图相匹配- 它有自定义绑定.
@ElementCollection(fetch = FetchType.EAGER)
@CollectionTable(name = "MEETUP_PROPERTY", joinColumns = @JoinColumn(name = "MEETUP_ID"))
@MapKeyColumn(name = "KEY")
@Column(name = "VALUE", length = 2048)
private Map<String, String> properties = new HashMap<>();
Run Code Online (Sandbox Code Playgroud)
编辑:见上文; 事实证明,这对我的代码没有任何作用.
public interface MeetupRepository extends PagingAndSortingRepository<Meetup, Long>,
QueryDslPredicateExecutor<Meetup>,
QuerydslBinderCustomizer<QMeetup> {
@Override
default void customize(QuerydslBindings bindings, QMeetup meetup) {
bindings.bind(meetup.properties).first((path, value) -> {
BooleanBuilder builder = new BooleanBuilder();
for (String key : value.keySet()) {
builder.and(path.containsKey(key).and(path.get(key).eq(value.get(key))));
}
return builder;
});
}
Run Code Online (Sandbox Code Playgroud)
QuerydslPredicateBuilder.getPredicate()
要求QuerydslBindings.getPropertyPath()
尝试两种方法来返回一个路径,以便它可以创建一个QuerydslAwareRootResourceInformationHandlerMethodArgumentResolver.postProcess()
可以使用的谓词.
QuerydslPredicateBuilder.getPredicate()
自动创建谓词.很好 - 我可以手动完成,如果我可以挂钩QuerydslAwareRootResourceInformationHandlerMethodArgumentResolver.postProcess()
我该如何覆盖该类,或者替换bean?它被实例化并作为RepositoryRestMvcConfiguration.repoRequestArgumentResolver()
bean声明中的bean 返回.
repoRequestArgumentResolver
bean 来覆盖该bean ,但它不会被使用.
RepositoryRestMvcConfiguration
s 覆盖.我不能强制设置它,它@Primary
还是@Ordered(HIGHEST_PRECEDENCE)
.RepositoryRestMvcConfiguration.class
,但这也会混淆Spring Boot的自动配置,因为它会导致
RepositoryRestMvcConfiguration's
在任何自动配置运行之前处理bean声明.除此之外,这导致杰克逊以不必要的方式序列化的回应.嗯 - 看起来像我预期的支持不存在.
所以问题就变成了:
我如何正确地覆盖repoRequestArgumentResolver
bean?
顺便说一句 - QuerydslAwareRootResourceInformationHandlerMethodArgumentResolver
尴尬地非公开.:/
这就是我在应用程序上下文中替换 bean 的方法。
感觉有点老套。我很想听到更好的方法来做到这一点。
@Configuration
public class CustomQuerydslHandlerMethodArgumentResolverConfig implements ApplicationContextAware {
/**
* This class is originally the class that instantiated QuerydslAwareRootResourceInformationHandlerMethodArgumentResolver and placed it into the Spring Application Context
* as a {@link RootResourceInformationHandlerMethodArgumentResolver} by the name of 'repoRequestArgumentResolver'.<br/>
* By injecting this bean, we can let {@link #meetupApiRepoRequestArgumentResolver} delegate as much as possible to the original code in that bean.
*/
private final RepositoryRestMvcConfiguration repositoryRestMvcConfiguration;
@Autowired
public CustomQuerydslHandlerMethodArgumentResolverConfig(RepositoryRestMvcConfiguration repositoryRestMvcConfiguration) {
this.repositoryRestMvcConfiguration = repositoryRestMvcConfiguration;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) ((GenericApplicationContext) applicationContext).getBeanFactory();
beanFactory.destroySingleton(REPO_REQUEST_ARGUMENT_RESOLVER_BEAN_NAME);
beanFactory.registerSingleton(REPO_REQUEST_ARGUMENT_RESOLVER_BEAN_NAME,
meetupApiRepoRequestArgumentResolver(applicationContext, repositoryRestMvcConfiguration));
}
/**
* This code is mostly copied from {@link RepositoryRestMvcConfiguration#repoRequestArgumentResolver()}, except the if clause checking if the QueryDsl library is
* present has been removed, since we're counting on it anyway.<br/>
* That means that if that code changes in the future, we're going to need to alter this code... :/
*/
@Bean
public RootResourceInformationHandlerMethodArgumentResolver meetupApiRepoRequestArgumentResolver(ApplicationContext applicationContext,
RepositoryRestMvcConfiguration repositoryRestMvcConfiguration) {
QuerydslBindingsFactory factory = applicationContext.getBean(QuerydslBindingsFactory.class);
QuerydslPredicateBuilder predicateBuilder = new QuerydslPredicateBuilder(repositoryRestMvcConfiguration.defaultConversionService(),
factory.getEntityPathResolver());
return new CustomQuerydslHandlerMethodArgumentResolver(repositoryRestMvcConfiguration.repositories(),
repositoryRestMvcConfiguration.repositoryInvokerFactory(repositoryRestMvcConfiguration.defaultConversionService()),
repositoryRestMvcConfiguration.resourceMetadataHandlerMethodArgumentResolver(),
predicateBuilder, factory);
}
}
Run Code Online (Sandbox Code Playgroud)
这些是基于 http 查询参数创建我自己的地图搜索谓词的代码片段。再次 - 很想知道更好的方法。
该postProcess
方法调用:
predicate = addCustomMapPredicates(parameterMap, predicate, domainType).getValue();
Run Code Online (Sandbox Code Playgroud)
就predicate
在引用被传递到构造函数并返回之前QuerydslRepositoryInvokerAdapter
。
这是该addCustomMapPredicates
方法:
private BooleanBuilder addCustomMapPredicates(MultiValueMap<String, String> parameters, Predicate predicate, Class<?> domainType) {
BooleanBuilder booleanBuilder = new BooleanBuilder();
parameters.keySet()
.stream()
.filter(s -> s.contains("[") && matches(s) && s.endsWith("]"))
.collect(Collectors.toList())
.forEach(paramKey -> {
String property = paramKey.substring(0, paramKey.indexOf("["));
if (ReflectionUtils.findField(domainType, property) == null) {
LOGGER.warn("Skipping predicate matching on [%s]. It is not a known field on domainType %s", property, domainType.getName());
return;
}
String key = paramKey.substring(paramKey.indexOf("[") + 1, paramKey.indexOf("]"));
parameters.get(paramKey).forEach(value -> {
if (!StringUtils.hasLength(value)) {
booleanBuilder.or(matchesProperty(key, null));
} else {
booleanBuilder.or(matchesProperty(key, value));
}
});
});
return booleanBuilder.and(predicate);
}
static boolean matches(String key) {
return PATTERN.matcher(key).matches();
}
Run Code Online (Sandbox Code Playgroud)
和模式:
/**
* disallow a . or ] from preceding a [
*/
private static final Pattern PATTERN = Pattern.compile(".*[^.]\\[.*[^\\[]");
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
1181 次 |
最近记录: |