使用弹簧数据规范构建动态选择查询

发布于 2025-01-19 05:04:45 字数 4127 浏览 0 评论 0原文

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

SELECT * FROM Product WHERE id IN (SELECT Product_id FROM Product_tags WHERE Tags IN ('GRADUATION', 'BIRTHDAY'));

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

    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 类中,有一个用 @ElementCollection 表示的字段 tags。 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 |

product_tags:

   Column   |          Type          | Collation | Nullable | Default
------------+------------------------+-----------+----------+---------
 product_id | bigint                 |           | not null |
 tags       | character varying(255) |           |          |

I'm trying to build a select query with Spring Data Specification. The query in question is the following:

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

The user is supposed to provide a set of tags to be matched with the IN operator in the subquery, BIRTHDAY and GRADUATION are some examples. I've tried building my solution off this answer but ran into some trouble.

    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]));

                };
    }

The problem here is that I'm trying to create a subquery from Tags which is not registered as an entity but it is rather an enum. Thus, executing the code gives me an error (This is the only error I've encountered so far, please point out parts of the code that may potentially cause other errors).

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;
    }
}

Not sure if this will help, but in the Product class there is a field tags denoted with @ElementCollection. Spring automatically creates a table named 'product_tags' with this, and the subquery selects from this table.

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

If possible, I would like to translate this query instead of the first one

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

UPDATE

I have edited my code

    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]));
        };
    }

But now I'm getting this error

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

For reference, my tables are defined as such

product:

   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 |

product_tags:

   Column   |          Type          | Collation | Nullable | Default
------------+------------------------+-----------+----------+---------
 product_id | bigint                 |           | not null |
 tags       | character varying(255) |           |          |

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

伪装你 2025-01-26 05:04:45
    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 显然可以工作,从那里我只需将查询调整为连接选择。这不是我最初希望实现的目标,但它确实有效。

    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);
        };

I seemed to have found an answer. Tags.class works apparently, and from there I just had to tweak my query to be a join select. Not what I initially hoped to accomplish, but it works.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文