Java JPA标准API-将ElementCollection与ElementCollection外键上的WHERE条件连接

Java JPA标准API-将ElementCollection与ElementCollection外键上的WHERE条件连接,java,jpa,join,criteria-api,Java,Jpa,Join,Criteria Api,我有一个实体文档和枚举标签(不是我的真实情况,我使用的是类比)。文档可以有一组标签。 标签的映射如下所示: @Entity public class Document { ... @ElementCollection(fetch = FetchType.EAGER) @Enumerated(EnumType.STRING) @Column(name = "labels") private Set<Label> labels = new Has

我有一个实体文档和枚举标签(不是我的真实情况,我使用的是类比)。文档可以有一组标签。 标签的映射如下所示:

@Entity
public class Document {
    ...

    @ElementCollection(fetch = FetchType.EAGER)
    @Enumerated(EnumType.STRING)
    @Column(name = "labels")
    private Set<Label> labels = new HashSet<>();

    ...
}
但我不知道如何在JPA标准API中编写它。我不知道如何在标签表中表示外键。JPA谓词应该是这样的

CriteriaBuilder cd = ...
SetJoin<Object, Object> labelsJoin = root.joinSet("labels", JoinType.LEFT);
cb.and(labelsJoin .in("label1","label2"), cb.isNull(...???...)));
CriteriaBuilder cd=。。。
SetJoin-labeljoin=root.joinSet(“标签”,JoinType.LEFT);
和(labeljoin.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)));
CriteriaQuery=cb.createQuery(Document.class);
Root=query.from(Document.class);
查询。选择(根);
Subquery Subquery=query.Subquery(Long.class);
Root subRoot=subquery.from(Document.class);
subquery.select(subRoot.get(“id”);
Join labeljoin=subRoot.Join(“标签”);
子查询.where(labeljoin.get(“value”).in(“label1”、“label2”);
query.where(cb.not(cb.in(root.get(“id”)).value(子查询));

不过,您可能需要调整
文档
标签
之间的连接。

您应该能够轻松地翻译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)));
CriteriaQuery=cb.createQuery(Document.class);
Root=query.from(Document.class);
查询。选择(根);
Subquery Subquery=query.Subquery(Long.class);
Root subRoot=subquery.from(Document.class);
subquery.select(subRoot.get(“id”);
Join labeljoin=subRoot.Join(“标签”);
子查询.where(labeljoin.get(“value”).in(“label1”、“label2”);
query.where(cb.not(cb.in(root.get(“id”)).value(子查询));

您可能需要调整
文档
标签
之间的连接。

这应该会返回预期的结果,但查询语句有点不同

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();
CriteriaBuilder cb=entityManager.getCriteriaBuilder();
CriteriaQuery=cb.createQuery(Document.class);
Root=query.from(Document.class);
Subquery Subquery=query.Subquery(Long.class);
Root subRoot=subquery.from(Document.class);
Join label=subRoot.Join(“标签”,JoinType.INNER);
列表标签=数组.asList(Label.Label_1,Label.Label_2);
subquery.select(subRoot.get(“id”))。其中(
cb.equal(root.get(“id”)、subRoot.get(“id”),
label.in(标签)
);
query.select(root).where(cb.exists(subquery.not());
List result=entityManager.creteQuery(query.getResultList();

这应该返回预期的结果,但查询语句有点不同

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();
CriteriaBuilder cb=entityManager.getCriteriaBuilder();
CriteriaQuery=cb.createQuery(Document.class);
Root=query.from(Document.class);
Subquery Subquery=query.Subquery(Long.class);
Root subRoot=subquery.from(Document.class);
Join label=subRoot.Join(“标签”,JoinType.INNER);
列表标签=数组.asList(Label.Label_1,Label.Label_2);
subquery.select(subRoot.get(“id”))。其中(
cb.equal(root.get(“id”)、subRoot.get(“id”),
label.in(标签)
);
query.select(root).where(cb.exists(subquery.not());
List result=entityManager.creteQuery(query.getResultList();

最初,您应该提供有效的映射
@ElementCollection
不应与一组实体一起使用。选择
@OneToMany
ManyToMany
。否则,您必须将标签更改为
Set
。这里的更多信息标签是java枚举,所以映射是正确的。对不起,我没有把它说清楚。现在我的描述是固定的和更清晰的。最初你应该提供有效的映射
@ElementCollection
不应与一组实体一起使用。选择
@OneToMany
ManyToMany
。否则,您必须将标签更改为
Set
。这里的更多信息标签是java枚举,所以映射是正确的。对不起,我没有把它说清楚。现在我的描述是固定的,更加清晰。我认为它接近解决方案,但由于labeljoin.get(“value”)而崩溃。但无论如何,谢谢你,我认为它接近解决方案,但因为labelsJoin.get(“value”)而崩溃。不过谢谢你,非常感谢。这个解决方案有效。我还有一个问题。我已经构建了一个框架,将url查询参数映射到我正在使用的JPA查询谓词上。我可以像这样使用query.select(root).where(cb.exists(subquery).not()).getRestriction()从您的解决方案中提取谓词,所以我达到了所需的目标。然而。。。有没有办法不用这些子查询就更容易地构建谓词?也许我要求太多了,但至少我想尝试一下。谢谢你,据我所知,你需要什么?如果我的猜测是正确的,您可以在
规范中编写所需的所有子查询。我在这里也回答了类似的问题,你对你提到的主题的回答与我的框架的工作方式完全相同。我只是想知道是否有一种方法可以为我的案例编写更简单的谓词。到目前为止,这是唯一复杂的一个。所有其他谓词都是非常简单的lambdas。。。但这是一个艰难的过程,我不认为有更简单的方法可以达到我所期望的效果。谢谢你,非常感谢。这个解决方案有效。我还有一个问题。我已经构建了一个框架,将url查询参数映射到我正在使用的JPA查询谓词上。我可以像这样使用query.select(root).where(cb.exists(subquery).not()).getRestriction()从您的解决方案中提取谓词,所以我达到了所需的目标。然而。。。有没有办法不用这些子查询就更容易地构建谓词?也许我要求太多了,但至少我想尝试一下。谢谢你,据我所知,你需要什么?如果我的猜测是正确的,您可以在
规范中编写所需的所有子查询。我在这里也回答了类似的问题,你对你提到的话题的回答与我的f