JPA Criteria API - 使用 ElementCollection 外键上的 WHERE 条件加入 ElementCollection
JPA Criteria API - joining ElementCollection with WHERE condition on ElementCollection foreign key
我有一个实体文档和枚举标签(不是我的真实情况,我在类比)。文档可以有一组标签。
标签映射如下:
@Entity
public class Document {
...
@ElementCollection(fetch = FetchType.EAGER)
@Enumerated(EnumType.STRING)
@Column(name = "labels")
private Set<Label> labels = new HashSet<>();
...
}
这意味着标签被映射到具有两列(document_id,值)的分隔 table 但在 Java 中它只是枚举
我需要 select 没有任何列出的标签的文档。
在 SQL 中它看起来像这样:
select D.id
from document D left join label L
on D.id = L.document_id and L.value in('label1','label2',...)
where L.document_id is null
但是我不知道在JPA Criteria中怎么写API。我不知道如何在标签 table 中表达外键。 JPA 谓词应该是这样的
CriteriaBuilder cd = ...
SetJoin<Object, Object> labelsJoin = root.joinSet("labels", JoinType.LEFT);
cb.and(labelsJoin .in("label1","label2"), cb.isNull(...???...)));
提前感谢您的建议。
卢卡斯
您应该能够轻松翻译 NOT IN 选项
select *
from document
where document.id not in (
select document_id
from label
where value in ('label1', 'label2')
)
进入标准API
CriteriaQuery<Document> query = cb.createQuery(Document.class);
Root<Document> root = query.from(Document.class);
query.select(root);
Subquery<Long> subquery = query.subquery(Long.class);
Root<Document> subRoot = subquery.from(Document.class);
subquery.select(subRoot.<Long>get("id"));
Join<Document, Label> labelsJoin = subRoot.join("labels");
subquery.where(labelsJoin.get("value").in("label1", "label2"));
query.where(cb.not(cb.in(root.get("id")).value(subquery)));
您可能需要调整 Document
和 Label
之间的连接。
这应该是return预期的结果,但查询语句有点不同
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Document> query = cb.createQuery(Document.class);
Root<Document> root = query.from(Document.class);
Subquery<Long> subquery = query.subquery(Long.class);
Root<Document> subRoot = subquery.from(Document.class);
Join<Document, Label> label = subRoot.join("labels", JoinType.INNER);
List<Label> labels = Arrays.asList(Label.LABEL_1, Label.LABEL_2);
subquery.select(subRoot.get("id")).where(
cb.equal(root.get("id"), subRoot.get("id")),
label.in(labels)
);
query.select(root).where(cb.exists(subquery).not());
List<Document> result = entityManager.creteQuery(query).getResultList();
我有一个实体文档和枚举标签(不是我的真实情况,我在类比)。文档可以有一组标签。 标签映射如下:
@Entity
public class Document {
...
@ElementCollection(fetch = FetchType.EAGER)
@Enumerated(EnumType.STRING)
@Column(name = "labels")
private Set<Label> labels = new HashSet<>();
...
}
这意味着标签被映射到具有两列(document_id,值)的分隔 table 但在 Java 中它只是枚举
我需要 select 没有任何列出的标签的文档。 在 SQL 中它看起来像这样:
select D.id
from document D left join label L
on D.id = L.document_id and L.value in('label1','label2',...)
where L.document_id is null
但是我不知道在JPA Criteria中怎么写API。我不知道如何在标签 table 中表达外键。 JPA 谓词应该是这样的
CriteriaBuilder cd = ...
SetJoin<Object, Object> labelsJoin = root.joinSet("labels", JoinType.LEFT);
cb.and(labelsJoin .in("label1","label2"), cb.isNull(...???...)));
提前感谢您的建议。 卢卡斯
您应该能够轻松翻译 NOT IN 选项
select *
from document
where document.id not in (
select document_id
from label
where value in ('label1', 'label2')
)
进入标准API
CriteriaQuery<Document> query = cb.createQuery(Document.class);
Root<Document> root = query.from(Document.class);
query.select(root);
Subquery<Long> subquery = query.subquery(Long.class);
Root<Document> subRoot = subquery.from(Document.class);
subquery.select(subRoot.<Long>get("id"));
Join<Document, Label> labelsJoin = subRoot.join("labels");
subquery.where(labelsJoin.get("value").in("label1", "label2"));
query.where(cb.not(cb.in(root.get("id")).value(subquery)));
您可能需要调整 Document
和 Label
之间的连接。
这应该是return预期的结果,但查询语句有点不同
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Document> query = cb.createQuery(Document.class);
Root<Document> root = query.from(Document.class);
Subquery<Long> subquery = query.subquery(Long.class);
Root<Document> subRoot = subquery.from(Document.class);
Join<Document, Label> label = subRoot.join("labels", JoinType.INNER);
List<Label> labels = Arrays.asList(Label.LABEL_1, Label.LABEL_2);
subquery.select(subRoot.get("id")).where(
cb.equal(root.get("id"), subRoot.get("id")),
label.in(labels)
);
query.select(root).where(cb.exists(subquery).not());
List<Document> result = entityManager.creteQuery(query).getResultList();