Я пытаюсь использовать JPA CriteriaBuilder для генерации запроса для объекта под названием "TestContact", который имеет соединение "многие-ко-многим" с другим объектом, называемым "SystemGroup", где атрибут для этого объединения называется "группами". Целью запроса является извлечение записей из объекта "TestContact", где атрибут "groups" находится либо в списке, либо пуст.
Код, который я использую, выглядит следующим образом
public List<TestContact> findWithCriteriaQuery(List<SystemGroup> groups) {
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<TestContact> cq = cb.createQuery(TestContact.class);
Root<TestContact> testContact = cq.from(TestContact.class);
cq.select(testContact);
Path<List<SystemGroup>> groupPath = testContact.get("groups");
// cq.where(groupPath.in(groups));
// cq.where(cb.isEmpty(groupPath));
cq.where(cb.or(cb.isEmpty(groupPath), groupPath.in(groups)));
TypedQuery<TestContact> tq = em.createQuery(cq);
return tq.getResultList();
}
Проблема заключается в том, что этот запрос возвращает результаты, когда группа находится в списке "группы", но по какой-то причине также не возвращает результаты, когда группа пуста (т.е. В таблице соединений нет записи)
Если я изменил предложение cq.where(cb.isEmpty(groupPath));
на cq.where(cb.isEmpty(groupPath));
то запрос правильно возвращает результаты, когда группа пуста.
Если я изменил предложение cq.where(groupPath.in(groups));
на cq.where(groupPath.in(groups));
то запрос правильно возвращает результаты, в которых группа находится в списке "группы".
Я не понимаю, почему, когда я пытаюсь объединить эти два предиката с помощью CriteriaBuilder или метода, результаты не включают записи, в которых группа находится либо в списке, либо пусто.
Атрибут групп в объекте "TestContact" объявляется следующим образом
@ManyToMany(fetch=FetchType.EAGER)
@JoinTable(name = "TEST_CONTACT_GROUPS", joinColumns = { @JoinColumn(name = "CONTACT_ID", referencedColumnName = "CONTACT_ID") }, inverseJoinColumns = { @JoinColumn(name = "GROUP_ID", referencedColumnName = "GROUP_ID") })
private List<SystemGroup> groups;
Поставщиком JPA является EclipseLink 2.5.0, сервер приложений Java EE - GlassFish 4, а база данных - Oracle 11gR2.
Может ли кто-нибудь указать, где я ошибаюсь?
Обновить
Я попробовал предложение от @Chris, но Eclipse возвращает следующую ошибку в Join<List<SystemGroup>> groupPath = testContact.join("groups", JoinType.LEFT)
Некорректное количество аргументов для типа Join; его нельзя параметризовать аргументами>
Глядя на JavaDoc для Join
он говорит, что параметры типа...
Z - тип источника соединения, X - целевой тип соединения
Я пробовал Join<TestContact, SystemGroup> groupPath = testContact.join("groups", JoinType.LEFT);
который затем заставляет Eclipse возвращать следующую ошибку на cb.isEmpty
Связанное несоответствие: общий метод isEmpty (Выражение) типа CriteriaBuilder не применим для аргументов (Join). Выведенный тип SystemGroup не является допустимым заменителем ограниченного параметра>
TestContact.get("groups"); обязывает внутреннее соединение от testContact к группам, которое отфильтровывает testContacts без групп. Вам нужно указать левое внешнее соединение и использовать его в своих предложениях isEmpty и в.
Root<TestContact> testContact = cq.from(TestContact.class);
cq.select(testContact);
Join<TestContact, SystemGroup> groupPath = testContact.join("groups", JoinType.LEFT);
cq.where(cb.or(cb.isEmpty(testContact.get("groups")), groupPath.in(groups)));
Я обычно ссылаюсь на https://en.wikibooks.org/wiki/Java_Persistence/Criteria#Join для примеров
Join<List<SystemGroup>> groupPath = testContact.join("groups", JoinType.LEFT);
, Я обновил оригинальный вопрос с некоторыми дополнительными деталями. Есть идеи?