API критериев считывает данные порциями с помощью оператора NOT IN - PullRequest
2 голосов
/ 22 октября 2019

Я использую EclipseLink 2.6.4, Java 8 и DB2 для базы данных. Мне нужно написать запрос для чтения данных с оператором NOT IN, обеспечивающим большой набор значений для оператора NOT IN.

В основном у меня есть набор внешних идентификаторов с более чем 10000 значениями:

Set<Integer> externalIDs = new HashSet<>(Arrays.asList("ExternalID1", "ExternalID2", "ExternalID3",....)); //externalIDs.size() == 10k+

Примечание: я знаю, что предел для оператора NOT IN с DB2 составляет 1000 значений, поэтому я создаю в запросе разделенные операторы NOT IN, и он выглядит следующим образом:

public List<UserEntity> findNotReferencedToRemove2(Set<String> externalIds) {

  CriteriaBuilder cb = entityManager.getCriteriaBuilder();
  CriteriaQuery<UserEntity> cq = cb.createQuery(UserEntity.class);
  Root<UserEntity> root = cq.from(UserEntity.class);

  Path<String> externalId1 = root.get(UserEntity_.relation1).get(RelationEntity1_.externalId);
  Path<String> externalId2 = root.get(UserEntity_.relation2).get(RelationEntity2_.externalId);
  Path<String> externalId3 = root.get(UserEntity_.relation3).get(RelationEntity3_.externalId);
  Path<String> externalId4 = root.get(UserEntity_.relation4).get(RelationEntity4_.externalId);
  Path<String> externalId5 = root.get(UserEntity_.relation5).get(RelationEntity5_.externalId);

  Predicate predicate = cb.and(
      partitionedNotIn(cb, externalId1, externalIds),
      partitionedNotIn(cb, externalId2, externalIds),
      partitionedNotIn(cb, externalId3, externalIds),
      partitionedNotIn(cb, externalId4, externalIds),
      partitionedNotIn(cb, externalId5, externalIds)
  );

  return entityManager.createQuery(cq.where(predicate)).getResultList();
}

//creates NOT IN statement splited in chunks of 999 values connected with AND 
private<C> Predicate partitionedNotIn(CriteriaBuilder cb, Path<C> path, Collection<C> ids) {
    if (ids.isEmpty()) {
      return cb.and();
    }
    return cb.and(partition(ids).stream().map(path::in).map(cb::not).toArray(Predicate[]::new));
  }

  private <C> Collection<List<C>> partition(Collection<C> list) {
    final AtomicInteger counter = new AtomicInteger(0);
    return list.stream()
        .collect(Collectors.groupingBy(it -> counter.getAndIncrement() / 999))
        .values();
  }

Закрытый методpartionedNotIn() просто разбивает операторы NOT IN на куски из 999 значений, чтобы не достигнуть максимума 1000 значений.

Но, как вы можете видеть, у меня есть 5 NOT IN операторов и для каждых 10000 предоставленных значений, и всего50000, и я достигаю предела в БД длины размещаемой переменной.

В любом случае, цель состоит в том, чтобы разбить это на куски, чтобы у меня не было 50k + значений для операторов NOT IN и в настоящее время нетесть идеи как этого добиться. С помощью IN заявления это будет легко.

Любое предложение будет полезным. Спасибо.

1 Ответ

1 голос
/ 22 октября 2019

Где вы получаете значения для не в? Если это файл, то вы можете использовать внешнюю таблицу. Или создайте временную таблицу и вставьте значения в эту таблицу и используйте ее в своем запросе.

...