Как сделать поиск с несколькими 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
Другие вопросы по тегам