Как сделать поиск с несколькими ElementCollections
Дано следующее юридическое лицо:
@Entity
@Table(name = "subscription")
public class Subscription implements Serializable {
private static final long serialVersionUID = 1L;
@ElementCollection
@CollectionTable(joinColumns= @JoinColumn(name="subscription"))
private Set<Code> mainCodes = new HashSet<>();
@ElementCollection
@CollectionTable(joinColumns= @JoinColumn(name="subscription"))
private Set<Code> otherCodes = new HashSet<>();
}
Таким образом, подписка может иметь ноль или более mainCodes или otherCodes, в которых она заинтересована. Я могу получить mainCode и otherCode определенного проходящего мимо объекта. Сами коды являются встраиваемыми с только одними строковыми полями.
Как мне создать JPA Query (или CriteriaBuilder), который выполняет поиск в этих коллекциях с помощью механизма "ИЛИ"?
В общем, я ищу такой запрос:
select s from subscription s where :myMainCode IN s.mainCodes OR :otherCode IN s.otherCodes
Это как-то возможно с CriteriaBuilder или мне нужно использовать более явный запрос? Если да, то как выглядит запрос?
РЕДАКТИРОВАТЬ: попробовал это с CriteriaBuilder:
final CriteriaBuilder cb = this.entityManager.getCriteriaBuilder();
final CriteriaQuery<Subscription> cq = cb.createQuery(Subscription.class);
final Root<Subscription> root = cq.from(Subscription.class);
final Expression<Collection<Code>> mainCodes = root.get("mainCodes");
final Predicate containsMainCode = cb.isMember(obj.getClassCode(), mainCodes);
final Expression<Collection<Code>> otherCodes = root.get("otherCodes");
final Predicate containsOtherCode = cb.isMember(obj.getOtherCode(), otherCodes);
final Predicate searchPredicate = cb.or(containsMainCode, containsOtherCode);
cq.select(root).where(searchPredicate);
Однако это создает внутреннее объединение обеих участвующих коллекций, а это означает, что он не будет возвращать результатов, если в базе данных есть строка для mainCode, но не для otherCode, он генерирует этот запрос:
SELECT t0.ID
FROM Subscription_OTHERCODES t2, Subscription_MAINCODES t1, subscription t0
WHERE ((t1.CODESYSTEM = ?) AND (t1.CODE = ?)) OR ((t2.CODESYSTEM = ?) AND (t2.CODE = ?))) AND ((t1.subscription = t0.ID) AND (t2.subscription = t0.ID))
Таким образом, даже если он находит соответствующий mainCode, он завершается ошибкой, если у него нет другого кода.
1 ответ
Это другой путь в вашем примере. Например, если код имеет свойство name):
select s from Subscription s left join s.mainCodes m left join s.otherCodes o
where m.name IN :myMainCode or o.name IN :myOtherCode