API критериев JPA - объединение ElementCollection с условием WHERE для внешнего ключа ElementCollection - PullRequest
1 голос
/ 24 марта 2020

У меня есть сущность Document и enum Label (не мой реальный случай, я использую аналогию). Документ может иметь набор меток. Отображение меток следующее:

@Entity
public class Document {
    ...

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

    ...
}

Это означает, что метки отображаются в отдельную таблицу с двумя столбцами (document_id, value), но в Java это просто enum

Мне нужно выберите документы, которые НЕ имеют ни одной из перечисленных меток. В 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. Я не знаю, как express внешний ключ в таблице меток. Предикат JPA должен выглядеть примерно так:

CriteriaBuilder cd = ...
SetJoin<Object, Object> labelsJoin = root.joinSet("labels", JoinType.LEFT);
cb.and(labelsJoin .in("label1","label2"), cb.isNull(...???...)));

Вот мой связанный SQL вопрос

Заранее спасибо за ваши предложения. Lukas

Ответы [ 2 ]

2 голосов
/ 24 марта 2020

Это должно вернуть ожидаемый результат, но оператор запроса немного отличается

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();
0 голосов
/ 24 марта 2020

Вы легко сможете перевести опцию 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 хотя.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...