Обзор
Учитывая
- Spring Данные JPA, Spring Data Rest, QueryDsl
- a
Meetup
объект- с полем
Map<String,String> properties
- сохранялся в таблице
MEETUP_PROPERTY
как@ElementCollection
- сохранялся в таблице
- с полем
- a
MeetupRepository
- который расширяет
QueryDslPredicateExecutor<Meetup>
- который расширяет
Я бы ожидал
Веб-запрос
GET /api/meetup?properties[aKey]=aValue
чтобы возвращать только Meetups с записью свойства, которая имеет указанный ключ и значение: aKey = aValue.
Однако это не работает для меня. Что мне не хватает?
Пробовал
Простые поля
Простые поля работают, например, имя и описание:
GET /api/meetup?name=whatever
Поля коллекции работают, как и участники:
GET /api/meetup?participants.name=whatever
Но не это поле Карты.
Настроить привязки QueryDsl
Я пробовал настроить привязку, имея репозиторий
extend QuerydslBinderCustomizer<QMeetup>
и переопределяя
customize(QuerydslBindings bindings, QMeetup meetup)
но при использовании метода customize()
код привязки внутри лямбда не является.
EDIT: узнал, что, поскольку QuerydslBindings
средство оценки параметра запроса не позволяет ему сопоставляться с картой pathSpecs
, которую она внутренне держит, - которая имеет свои пользовательские привязки в ней.
Некоторые особенности
Поле Meetup.properties
@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<>();
настраиваемая привязка querydsl
EDIT: См. выше; оказывается, это ничего не делало для моего кода.
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;
});
}
Дополнительные выводы
-
QuerydslPredicateBuilder.getPredicate()
проситQuerydslBindings.getPropertyPath()
попробовать два способа вернуть путь, чтобы он мог использовать предикат, который может использоватьQuerydslAwareRootResourceInformationHandlerMethodArgumentResolver.postProcess()
.- 1 - посмотреть в пользовательских привязках. Я не вижу способа выразить запрос карты там.
- 2 по умолчанию - Spring bean. Там же проблема с выражением. Как вы выражаете карту?
Таким образом, невозможно получить
QuerydslPredicateBuilder.getPredicate()
для автоматического создания предиката. Fine - я могу сделать это вручную, если я могу подключиться кQuerydslAwareRootResourceInformationHandlerMethodArgumentResolver.postProcess()
Как я могу переопределить этот класс или заменить bean? Он был создан и возвращен как bean в объявлении RepositoryRestMvcConfiguration.repoRequestArgumentResolver()
bean.
- я может переопределить bean, объявив мой собственный
repoRequestArgumentResolver
bean, но он не будет использоваться.- Он переопределяется
RepositoryRestMvcConfiguration
s. Я не могу заставить его, установив его@Primary
или@Ordered(HIGHEST_PRECEDENCE)
. - Я могу принудительно выполнить его с помощью явного сканирования компонентов
RepositoryRestMvcConfiguration.class
, но это также испортит Spring автоконфигурацию загрузки, поскольку это вызываетRepositoryRestMvcConfiguration's
bean декларации, подлежащие обработке перед выполнением любой автоматической настройки. Среди прочего, это приводит к ответам, которые Джексону сериализуют нежелательно.
- Он переопределяется
Вопрос
Хорошо - похоже, поддержка, которую я ожидал, просто не существует.
Итак, вопрос становится следующим:
КАК правильно ли переопределить repoRequestArgumentResolver
bean?
BTW - QuerydslAwareRootResourceInformationHandlerMethodArgumentResolver
неловко не является общедоступным.:/