spring-data-jpa 使用Spring Data 规范构建选择查询

vdgimpew  于 2022-11-10  发布在  Spring
关注(0)|答案(1)|浏览(141)

我正在尝试使用Spring Data 规范构建一个选择查询。所涉及的查询如下:
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类中有一个用@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 |

产品标签(_T):

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

3npbholx1#

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显然可以工作,从那里我只需要调整我的查询成为一个连接选择。虽然不是我最初希望完成的,但它工作了。

相关问题