【问题标题】:Building a dynamic select query using Spring Data Specification使用 Spring Data Specification 构建动态选择查询
【发布时间】:2022-08-14 22:34:33
【问题描述】:

我正在尝试使用 Spring Data Specification 构建选择查询。有问题的查询如下:

SELECT * FROM product WHERE id IN (SELECT product_id FROM product_tags WHERE tags IN (\'GRADUATION\', \'BIRTHDAY\'));

用户应该提供一组标签以与子查询中的IN 运算符匹配,BIRTHDAY 和 GRADUATION 是一些示例。我已经尝试根据this 答案构建我的解决方案,但遇到了一些麻烦。

    public static Specification<Product> withTags(Set<Tags> tags) {
        return tags == null ?
                null :
                (root, query, criteriaBuilder) -> {
            List<Predicate> predicates = new ArrayList<>();
                    Subquery<Tags> subquery = query.subquery(Tags.class);
                    Root<Tags> subqueryRoot = subquery.from(Tags.class);
                    subquery.select(subqueryRoot.get(\"product_tags\").get(\"product_id\"));
                    subquery.where(criteriaBuilder.trim(subqueryRoot.get(\"product\").get(\"id\")).in(tags));

                    predicates.add(subqueryRoot.get(\"*\").in(subquery));
                    return criteriaBuilder.and(predicates.toArray(new Predicate[0]));

                };
    }

这里的问题是我试图从Tags 创建一个子查询,它没有注册为实体,而是一个枚举。因此,执行代码会给我一个错误(这是我迄今为止遇到的唯一错误,请指出可能导致其他错误的部分代码)。

public enum Tags {

    BIRTHDAY(\"birthday\"),
    GRADUATION(\"graduation\"),
    GET_WELL_SOON(\"get well soon\"),
    RIBBON(\"ribbon\"),
    WRAPPING_PAPER(\"wrapping paper\");

    final String tagName;

    private Tags(String tagName) {
        this.tagName = tagName;
    }

    public String getTagName() {
        return tagName;
    }
}

不确定这是否有帮助,但在Product 类中有一个字段tags@ElementCollection 表示。 Spring 自动创建一个名为 \'product_tags\' 的表,子查询从这个表中选择。

    @ElementCollection(fetch = FetchType.EAGER)
    @Enumerated(EnumType.STRING)
    private Set<Tags> tags;

如果可能的话,我想翻译这个查询而不是第一个

SELECT * FROM product WHERE id IN (SELECT product_id FROM product_tags WHERE tags = ANY(ARRAY[\'GRADUATION\', \'GET_WELL_SOON\']));

更新

我已经编辑了我的代码

    public static Specification<Product> withTags(Set<Tags> tags) {
        return tags == null ?
                null :
                (root, query, criteriaBuilder) -> {

            List<Predicate> predicates = new ArrayList<>();
            Subquery<Long> subquery = query.subquery(Long.class);
            Root<Product> subroot = subquery.from(Product.class);

            subquery.select(subroot.get(\"id\").get(\"tags\"));

            subquery.where(criteriaBuilder.trim(subroot.join(\"tags\").get(\"id\")).in(tags));

            predicates.add(root.get(\"id\").in(subquery));

            return criteriaBuilder.and(predicates.toArray(new Predicate[0]));
        };
    }

但是现在我收到了这个错误

java.lang.IllegalStateException: Illegal attempt to dereference path source [null.id] of basic type

作为参考,我的表是这样定义的

产品:

   Column    |          Type          | Collation | Nullable | Default
-------------+------------------------+-----------+----------+---------
 id          | bigint                 |           | not null |
 category    | character varying(255) |           |          |
 date_added  | date                   |           |          |
 description | character varying(255) |           |          |
 name        | character varying(255) |           |          |
 price       | double precision       |           | not null |

产品标签:

   Column   |          Type          | Collation | Nullable | Default
------------+------------------------+-----------+----------+---------
 product_id | bigint                 |           | not null |
 tags       | character varying(255) |           |          |
  • 嗯...也许您可以尝试使用从标签 getTagName() 构建的 Set<String>?
  • 那是一个很好的选择,但如果可能的话,我更喜欢使用标签。当我们讨论集合的主题时,url 参数是否可以接受对象集合?我收到了这个新错误:Parameter value [] did not match expected type [java.util.Set (n/a)];
  • 如果我要使用一组字符串,你会建议我如何构建查询?

标签: java sql spring-data-jpa


【解决方案1】:
    public static Specification<Product> withTags(Set<Tags> tags) {
        return tags.isEmpty() ?
                null:
                (root, query, criteriaBuilder) -> {
                    Subquery<Tags> subquery = query.subquery(Tags.class);
                    Root<Product> subroot = subquery.from(Product.class);

                    subquery.select(subroot.get("id")).where(subroot.join("tags").in(tags));
                    Predicate predicate = root.get("id").in(subquery);

                    return criteriaBuilder.and(predicate);
        };

我似乎找到了答案。 Tags.class 显然有效,从那里我只需要调整我的查询以成为连接选择。不是我最初希望完成的,但它确实有效。

【讨论】:

    猜你喜欢
    • 2018-11-25
    • 2014-08-25
    • 2017-07-20
    • 1970-01-01
    • 2020-07-31
    • 2022-12-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多