JPA getSingleResult() или null

У меня есть метод insertOrUpdate, который вставляет Entity, если он не существует, или обновляет его, если он это делает. Чтобы включить это, я должен findByIdAndForeignKey, если он вернул null вставить, если потом не обновить. Проблема в том, как проверить, существует ли она? Поэтому я попробовал getSingleResult. Но он генерирует исключение, если

public Profile findByUserNameAndPropertyName(String userName, String propertyName) {
    String namedQuery = Profile.class.getSimpleName() + ".findByUserNameAndPropertyName";
    Query query = entityManager.createNamedQuery(namedQuery);
    query.setParameter("name", userName);
    query.setParameter("propName", propertyName);
    Object result = query.getSingleResult();
    if (result == null) return null;
    return (Profile) result;
}

но getSingleResult выбрасывает Exception.

Спасибо

Ответ 1

Выбрасывание исключения заключается в том, как getSingleResult() указывает, что его невозможно найти. Лично я не могу выдержать такой API. Это создает ложную обработку исключений без реальной выгоды. Вам просто нужно обернуть код в блок try-catch.

В качестве альтернативы вы можете запросить список и посмотреть, пуст ли он. Это не исключение. Фактически, поскольку вы не выполняете первичный поиск по ключевым словам технически, может быть несколько результатов (даже если один, оба или сочетание ваших внешних ключей или ограничений делают это невозможным на практике), поэтому это, вероятно, более подходящее решение.

Ответ 2

Я инкапсулировал логику в следующий вспомогательный метод.

public class JpaResultHelper {
    public static Object getSingleResultOrNull(Query query){
        List results = query.getResultList();
        if (results.isEmpty()) return null;
        else if (results.size() == 1) return results.get(0);
        throw new NonUniqueResultException();
    }
}

Ответ 3

Вот хороший вариант для этого:

public static <T> T getSingleResult(TypedQuery<T> query) {
    query.setMaxResults(1);
    List<T> list = query.getResultList();
    if (list == null || list.isEmpty()) {
        return null;
    }

    return list.get(0);
}

Ответ 4

Попробуйте это на Java 8:

Optional first = query.getResultList().stream().findFirst();

Ответ 5

Spring имеет метод утилиты для этого:

TypedQuery<Profile> query = em.createNamedQuery(namedQuery, Profile.class);
...
return org.springframework.dao.support.DataAccessUtils.singleResult(query.getResultList());

Ответ 6

Я сделал (в Java 8):

query.getResultList().stream().findFirst().orElse(null);

Ответ 7

Если вы хотите использовать механизм try/catch для решения этой проблемы, тогда он может использоваться для работы как if/else. Я использовал try/catch, чтобы добавить новую запись, когда я не нашел существующий.

try {  //if part

    record = query.getSingleResult();   
    //use the record from the fetched result.
}
catch(NoResultException e){ //else part
    //create a new record.
    record = new Record();
    //.........
    entityManager.persist(record); 
}

Ответ 8

Здесь типизированная версия /generics, основанная на реализации Rodrigo IronMan:

 public static <T> T getSingleResultOrNull(TypedQuery<T> query) {
    query.setMaxResults(1);
    List<T> list = query.getResultList();
    if (list.isEmpty()) {
        return null;
    }
    return list.get(0);
}

Ответ 9

Есть альтернатива, которую я бы рекомендовал:

Query query = em.createQuery("your query");
List<Element> elementList = query.getResultList();
return CollectionUtils.isEmpty(elementList ) ? null : elementList.get(0);

Эта защита от Null Pointer Exception гарантирует, что возвращается только 1 результат.

Ответ 10

Так что не делай этого!

У вас есть два варианта:

  • Запустите выбор, чтобы получить COUNT вашего результирующего набора, и только извлекайте данные, если этот счет не равен нулю; или

  • Используйте другой тип запроса (который получает набор результатов) и проверьте, имеет ли он 0 или более результатов. У него должно быть 1, так что вытащите его из своего набора результатов, и все готово.

Я бы пошел со вторым предложением, соглашаясь с Cletus. Это дает лучшую производительность, чем (потенциально) 2 запроса. Также меньше работы.

Ответ 11

Недокументированный метод uniqueResultOptional в org.hibernate.query.Query должен сделать трюк. Вместо того, чтобы поймать NoResultException, вы можете просто вызвать query.uniqueResultOptional().orElse(null).

Ответ 12

Объединяя полезные биты существующих ответов (ограничивая количество результатов, проверяя, что результат уникален) и используя имя метода (Hibernate), мы получаем:

/**
 * Return a single instance that matches the query, or null if the query returns no results.
 *
 * @param query query (required)
 * @param <T> result record type
 * @return record or null
 */
public static <T> T uniqueResult(@NotNull TypedQuery<T> query) {
    List<T> results = query.setMaxResults(2).getResultList();
    if (results.size() > 1) throw new NonUniqueResultException();
    return results.isEmpty() ? null : results.get(0);
}

Ответ 13

Я решил это с помощью List<?> myList = query.getResultList(); и проверка, равен ли myList.size() нулю.

Ответ 14

Из JPA 2.2 вместо .getResultList() и проверки, является ли список пустым или создает поток, вы можете вернуть поток и взять первый элемент.

.getResultStream()
.findFirst()
.orElse(null);

Ответ 15

Здесь та же логика, что и другие, предложила (получить resultList, вернуть свой единственный элемент или null), используя Google Guava и TypedQuery.

public static <T> getSingleResultOrNull(final TypedQuery<T> query) {
    return Iterables.getOnlyElement(query.getResultList(), null); 
}

Обратите внимание, что Guava вернет неинтуитивное исключение IllegalArgumentException, если набор результатов имеет более одного результата. (Исключение имеет смысл для клиентов getOnlyElement(), поскольку он принимает список результатов в качестве аргумента, но менее понятен для клиентов getSingleResultOrNull().)

Ответ 16

Здесь другое расширение, на этот раз в Scala.

customerQuery.getSingleOrNone match {
  case Some(c) => // ...
  case None    => // ...
}

С этим сутенером:

import javax.persistence.{NonUniqueResultException, TypedQuery}
import scala.collection.JavaConversions._

object Implicits {

  class RichTypedQuery[T](q: TypedQuery[T]) {

    def getSingleOrNone : Option[T] = {

      val results = q.setMaxResults(2).getResultList

      if (results.isEmpty)
        None
      else if (results.size == 1)
        Some(results.head)
      else
        throw new NonUniqueResultException()
    }
  }

  implicit def query2RichQuery[T](q: TypedQuery[T]) = new RichTypedQuery[T](q)
}

Ответ 17

Таким образом, все проблемы "попробуйте переписать без исключения" на этой странице имеют небольшую проблему. Либо не бросать исключение NonUnique, но и бросать его в некоторые неправильные случаи (см. Ниже).

Я думаю, что правильное решение (возможно):

public static <L> L getSingleResultOrNull(TypedQuery<L> query) {
    List<L> results = query.getResultList();
    L foundEntity = null;
    if(!results.isEmpty()) {
        foundEntity = results.get(0);
    }
    if(results.size() > 1) {
        for(L result : results) {
            if(result != foundEntity) {
                throw new NonUniqueResultException();
            }
        }
    }
    return foundEntity;
}

Возвращение с нулевым значением, если в списке есть 0 элементов, возвращающих nonunique, если в списке есть разные элементы, но не возвращаются в ununique, когда один из ваших вариантов не был правильно спроектирован и возвращает один и тот же объект более одного раза.

Не стесняйтесь комментировать.

Ответ 18

Посмотрите этот код:

return query.getResultList().stream().findFirst().orElse(null);

При findFirst() может быть выдано исключение NullPointerException.

лучший подход это:

return query.getResultList().stream().filter(Objects::nonNull).findFirst().orElse(null);

Ответ 19

Я добился этого, получив список результатов и проверив, пуст ли он

public boolean exist(String value) {
        List<Object> options = getEntityManager().createNamedQuery("AppUsers.findByEmail").setParameter('email', value).getResultList();
        return !options.isEmpty();
    }

Это так раздражает, что getSingleResult() исключения

Броски:

  1. NoResultException - если нет никакого результата
  2. NonUniqueResultException - если больше чем один результат и некоторое другое исключение, о котором вы можете получить больше информации из их документации

Ответ 20

Это работает для меня:

Optional<Object> opt = Optional.ofNullable(nativeQuery.getSingleResult());
return opt.isPresent() ? opt.get() : null;