当前位置: 首页 > 知识库问答 >
问题:

地图字段关键字的QueryDsl web查询

颛孙建业
2023-03-14
    null
    扩展 QueryDSLPredicateExecutor

web查询

GET /api/meetup?properties[aKey]=aValue

返回具有指定键和值的属性条目的Meetups:AKEY=AVALUE。

GET /api/meetup?name=whatever

收集字段工作,如参与者:

GET /api/meetup?participants.name=whatever

但不是这个地图字段。

我尝试通过使用存储库来定制绑定

extend QuerydslBinderCustomizer<QMeetup>
customize(QuerydslBindings bindings, QMeetup meetup)
@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<>();

编辑:见上文;事实证明,这对我的代码没有任何帮助。

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()尝试两种方法返回路径,以便生成querydslwareRootResourceInformationHandlerMethodArgumentResolver.PostProcess()可以使用的谓词。
    • 1是在自定义绑定中查找。我看不到任何表达地图查询的方法
    • 2默认为Spring的bean路径。同样的表达问题。你如何表示地图?因此,似乎不可能获得querydslpredicateBuilder.getpredicate()来自动创建谓词。很好-如果我能挂接到QuerydsLavareRootResourceInformationHandlerMethodArgumentResolver.PostProcess()
    • ,我就可以手动完成

    如何重写该类或替换bean?它在RepositoryRestMVCConfiguration.repoRequestArgumentResolver()bean声明中被实例化并作为bean返回。

    • 它由repositoryrestMVCconfigurations重写。我不能通过设置@primary@order(highest_preasence)来强制它。
    • 我可以通过显式组件扫描repositoryrestmvcconfiguration.class来强制执行,但这也会扰乱Spring Boot的自动配置,因为它会导致在运行任何自动配置之前处理repositoryrestmvcconfiguration的bean声明。除其他外,这会导致Jackson以不需要的方式序列化响应。

    看来我所期望的支持并不存在。

    因此,问题变成了:如何正确重写RepoRequestArgumentResolverbean?

共有1个答案

方英耀
2023-03-14

这就是我在应用程序上下文中替换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);
    }
}

这些代码片段根据http查询参数创建了我自己的地图搜索谓词。再说一遍--很想知道更好的方法。

        predicate = addCustomMapPredicates(parameterMap, predicate, domainType).getValue();
    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();
    }
    /**
     * disallow a . or ] from preceding a [
     */
    private static final Pattern PATTERN = Pattern.compile(".*[^.]\\[.*[^\\[]");
 类似资料:
  • 问题内容: 给定 Spring Data JPA,Spring Data Rest,QueryDsl 一个实体 有田野 坚持作为一个表 一个 延伸 我希望 网络查询 仅返回具有指定键和值的属性条目的Meetups:aKey = aValue。 但是,这对我不起作用。我想念什么? 试过了 简单字段 简单的字段可以工作,例如名称和描述: 收集字段像参与者一样工作: 但不是此Map字段。 自定义Quer

  • 问题内容: 例如,我有两个实体:雇员和地址。在这些实体中,Employee有一个外键AddressID引用了Address上的ID列。在Java域对象中,Hibernate很好地将forgein键整数字段与Address对象字段包装在一起。但是现在,如何查询具有特定AddressID的Employee? 我试图创建一个表别名。这似乎可行,但相当尴尬。 我也曾尝试做这样的事情: 它有时会起作用,但并

  • 问题内容: 如果我在一个类中有以下声明,其中是抽象类型: 是否能让我仍然能够更改中的对象的状态,但阻止我将新对象添加到列表中?如果我写错了,请您解释一下正在做什么以及何时应该使用关键字。 问题答案: 不,final关键字不会使列表或其内容不可变。如果您想要一个不可变的列表,则应使用: final关键字的作用是防止您为’synapses’变量分配新值。即,您不能写: 但是,您可以编写:

  • 如果我在一个类中有以下语句,其中Synapse是一个抽象类型: 最终是否允许我仍然能够更改列表中Synapse对象的状态,但阻止我向列表中添加新的Synapse对象?如果我错了,请您解释一下final正在做什么,以及我应该在什么时候使用关键字final。

  • 下面是 Java 里面的关键字。不能使用以下任一作为您的程序标识符。关键字 const 和 goto 语句被保留,即使他们目前尚未使用。true, false, 和 null 似乎是关键字,但它们实际上是字面值;你不能使用它们作为你的程序标识符。 abstract continue for new switch assert*** default goto*

  • 关键字是不能做为变量的。Lua 的关键字不多,就以下几个: and break do else elseif end false for function if in local nil not or repeat return then true until while