Использование @ElementCollection в CriteriaQuery (или запрос содержимого @ElementCollection)

public enum ReportStatus { 
    SUCCCEED, FAILED;
}

public class Work {
    @ElementCollection
    @Enumerated(EnumType.STRING)
    List<ReportStatus> reportStatuses;
}

Учитывая следующую структуру, я хотел бы выполнить запрос, чтобы найти всю работу, отфильтрованную reportStatuses. Он отлично работает со следующим синтаксисом hql:

public List<Long> queryHQL() {
    final String query = "SELECT w.id FROM Work w JOIN w.reportStatuses s WHERE s in (:rs)";

    final List<ReportStatus> reportStatuses = new ArrayList<ReportStatus>();
    reportStatuses.add(ReportStatus.FAILED);

    return this.entityManager.createQuery(query).setParameter("rs", reportStatuses).getResultList();
}

Но я хотел бы использовать API критериев (jpa2) и не могу понять, как это сделать. Вот моя ближайшая попытка, я думаю:

public List<Long> query() {
    final List<ReportStatus> reportStatuses = new ArrayList<ReportStatus>();
    reportStatuses.add(ReportStatus.FAILED);

    final CriteriaBuilder builder = this.entityManager.getCriteriaBuilder();

    final CriteriaQuery<Long> criteriaQuery = builder.createQuery(Long.class);
    final Root<Work> workModel = criteriaQuery.from(Work.class);

    final ListJoin<Work, ReportStatus> status = workModel.joinList("reportStatuses");

    final Predicate predicate = status.in(reportStatuses);

    criteriaQuery.where(predicate);
    criteriaQuery.select(workModel.<Long> get("id"));

    return this.entityManager.createQuery(criteriaQuery).getResultList();
}

Я также пытался использовать API критериев гибернации, но в качестве jpa2 мне не удалось найти правильный синтаксис.


person Raphaël Brugier    schedule 08.10.2010    source источник


Ответы (3)


Я бы использовал следующий синтаксис CriteriaQuery для выполнения того же самого.

EntityManager em = entityManagerFactory.createEntityManager();

final List<ReportStatus> reportStatuses = new ArrayList<ReportStatus>();
reportStatuses.add(ReportStatus.FAILED);

final CriteriaBuilder builder = em.getCriteriaBuilder();

final CriteriaQuery<Long> criteriaQuery = builder.createQuery(Long.class);
final Root<Work> _work = criteriaQuery.from(Work.class);

/*final ListJoin<Work, ReportStatus> status = _work.joinList("reportStatuses");
final Predicate predicate = status.in(reportStatuses);
criteriaQuery.where(predicate);*/

final Expression<List<ReportStatus>> _status = _work.get(Work_.reportStatuses);
_status.in(reportStatuses);

criteriaQuery.select(_work.get(Work_.id));

List<Long> list =  em.createQuery(criteriaQuery).getResultList();

ОБНОВИТЬ

Спасибо, но мы не используем сгенерированную метамодель. Так что, к сожалению, я не могу попробовать с вашим ответом. :(

Если вы не используете метамодель, просто замените _work.get(Work_.reportStatuses) на _work.get("reportStatuses"). Это сработает. :)

person dira    schedule 09.12.2010
comment
Спасибо, но мы не используем сгенерированную метамодель. Так что, к сожалению, я не могу попробовать с вашим ответом. :( - person Raphaël Brugier; 10.12.2010
comment
@Raphaël Brugier Проверьте часть UPDATE. - person dira; 10.12.2010

Вы можете создать этот HQL.

String query = "SELECT w.id FROM Work w, IN(w.reportStatuses) s WHERE s = :rs";
return this.entityManager.createQuery(query).setParameter("rs", ReportStatus.FAILED).getResultList();
person user904084    schedule 20.08.2011
comment
Я хотел бы отметить, что некоторые инструменты поддержки JPA SQL помечают это как ошибку, когда вы помещаете @ElementCollection в in(~), НЕ доверяйте этим инструментам (в этом случае), это работает плавно и довольно быстро, если вы индексируете правильные поля ( Этот ответ на самом деле сэкономил мне несколько часов работы). - person le-doude; 28.02.2014
comment
Где вы нашли эту языковую особенность? Я порылся в Интернете, пытаясь решить именно эту проблему, и этот ответ — единственное упоминание об использовании предложения IN в качестве псевдотаблицы, которое я нашел. Зато сработало безотказно! - person stevevls; 11.03.2014

Я смущен. Вызов in(..) возвращает предикат, но, похоже, на самом деле не обеспечивает его соблюдение (похоже, он не интегрирован в запрос, по крайней мере, для меня он возвращал всех членов корня, независимо от того, пересекались ли их коллекции с reportStatuses. Журнал отладки показывает простой запрос select distinct work0_.id as id18_ from Work work0_).

Кроме того, зачем помещать reportStatuses в список, если вызывающая сторона заинтересована только в тех, которые соответствуют одному значению? Как бы вы выполнили запрос, просто используя ReportStatus.FAILED вместо создания для него списка?

person DHM    schedule 05.07.2011