Ограничение Hibernate вызывает ошибку, если список пуст

Если мой список пуст, я получаю следующую ошибку:

You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ')'

Ниже мой метод, связанный с гибернацией:

  @Override
    public List<SomeThing> findByIds(List<Integer> someIds) {
        return sessionFactory.getCurrentSession().createCriteria(SomeClass.class)
                .add(Restrictions.in("id", someIds))
                .list();
    }

Что делать, чтобы предотвратить эту ошибку?

Я знаю, что я мог бы закоротить вызов и вернуть пустой список, например:

if(someIds == null || someIds.size() == 0) {
  return new List<SomeThing>();
}

Но есть ли более элегантный способ сделать это?

Ответ 1

НЕТ. Если вы выполняете запрос с пустыми параметрами для предложения in, он будет терпеть неудачу (вы можете проверить это, выполнив простой SQL). Лучше не выполнять запрос, если входной параметр имеет значение null/empty.

Единственное, что я могу посоветовать, - использовать функцию isEmpty() и != null в if и небольшую реструктуризацию как:

@Override
public List<SomeThing> findByIds(List<Integer> someIds) {
   List<Something> result = null; //you may initialize with empty list
   if(someIds != null || !someIds.isEmpty() {
       result = sessionFactory.getCurrentSession().createCriteria(SomeClass.class)
            .add(Restrictions.in("id", someIds))
            .list();
   } 
   return result;
}

Ответ 2

Я бы сказал, что Hibernate должен решить эту проблему и дать содержательное сообщение.

Я думаю, что его ответственность провайдера/спящего режима - проверить пустой/нулевой список.

Можно представить причину, она пытается построить where where, что-то вроде id in(), где-то в org.hibernate.loader.criteria.CriteriaQueryTranslator или аналогично. Но поскольку здесь Список пуст, он будет метать исключение. Но они уже создали запрос с помощью (и не могут завершиться из-за исключения/пустого списка.

Ответ 3

(Это в основном основано на ответе @Yogendra Singh с твистом, чтобы сделать его более приемлемым для часто встречающейся ситуации с несколькими необязательными аргументами)

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

Обычно мы делаем необязательные критерии:

@Override
public List<SomeThing> findBySearchParams(SearchParam searchParam) {
   // create criteria with mandatory search criteria
   Criteria criteria = sessionFactory.getCurrentSession()
                           .createCriteria(SomeClass.class);
                           .add(Restriction("someField", searchParam.getSomeField()));


   // add "id" only if "someId" contains value
   if(searchParam.getSomeIds() != null && !searchParam.getSomeIds().empty()) {
       criteria.add(Restrictions.in("id", searchParam.getSomeIds()));
   } 

   // add "anotherField" only if "anOptionalField" is not null
   if(searchParam.getAnOptionalField() != null) {
       criteria.add(Restrictions.in("anotherField", searchParam.getAnOptionalField()));
   } 

   return criteria.list();
}

Edit:

Хотя Hibernate еще не обеспечивает более элегантный способ для этого, вы можете написать что-то сами, чтобы оно выглядело более элегантным:

class SmartCriteriaBuilder {
  private Criteria criteria;
  SmartCriteriaBuilder (Criteria criteria) { this.criteria = criteria;}

  SmartCriteriaBuilder in(String field, Collection values) {
    if (!empty(values)) {
      this.criteria.add(Restrictions.in(field,values));
    }
  }
  // all other kind of restrictions ....

  Criteria toCriteria() {
    return this.criteria;
  }
}

Тогда вы можете сделать что-то более умное:

SmartCriteriaBuilder criteriaBuilder = 
    new SmartCriteriaBuilder(sessionFactory.getCurrentSession().createCriteria());

criteriaBuilder .in("someField", listPossiblyNullOrEmpty);


return criteriaBuilder .toCriteria().list();