У меня есть 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
.
Благодарность
getSingleResult()
используется в ситуациях типа: « Я полностью уверен, что эта запись существует. Стреляйте в меня, если нет ». Я не хочуnull
каждый раз тестировать этот метод, потому что уверен, что он его не вернет. В противном случае это вызовет много шаблонного и защитного программирования. И если запись действительно не существует (вNoResultException
отличие от того, что мы предполагали), гораздо лучше сравнить ее сNullPointerException
несколькими строками позже. Конечно,getSingleResult()
было бы здорово иметь две версии , но если мне нужно выбрать одну ...Я инкапсулировал логику в следующем вспомогательном методе.
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(); } }
источник
Попробуйте это в Java 8:
источник
.orElse(null)
Вот хороший вариант для этого:
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); }
источник
TypedQuery<T>
, и в этом случаеgetResultList()
файл уже правильно набран какList<T>
.fetch()
сущностью может быть заполнено не полностью. См stackoverflow.com/a/39235828/661414setMaxResults()
него свободный интерфейс, поэтому вы можете писатьquery.setMaxResults(1).getResultList().stream().findFirst().orElse(null)
. Это должна быть самая эффективная схема вызова в Java 8+.В Spring есть служебный метод для этого:
TypedQuery<Profile> query = em.createNamedQuery(namedQuery, Profile.class); ... return org.springframework.dao.support.DataAccessUtils.singleResult(query.getResultList());
источник
Я сделал (на Java 8):
query.getResultList().stream().findFirst().orElse(null);
источник
Из JPA 2.2 вместо
.getResultList()
проверки того, пуст ли список или создания потока, вы можете вернуть поток и взять первый элемент..getResultStream() .findFirst() .orElse(null);
источник
Если вы хотите использовать механизм 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); }
источник
Вот типизированная / обобщенная версия, основанная на реализации Родриго Айронмана:
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); }
источник
Есть альтернатива, которую я бы порекомендовал:
Query query = em.createQuery("your query"); List<Element> elementList = query.getResultList(); return CollectionUtils.isEmpty(elementList ) ? null : elementList.get(0);
Это защищает от исключения Null Pointer Exception, гарантирует, что будет возвращен только 1 результат.
источник
Так что не делай этого!
У вас есть два варианта:
Выполните выборку, чтобы получить COUNT вашего набора результатов, и извлеките данные только в том случае, если это число не равно нулю; или
Используйте другой тип запроса (который получает набор результатов) и проверьте, дает ли он 0 или более результатов. У него должно быть 1, поэтому вытащите его из своей коллекции результатов, и все готово.
Я бы согласился со вторым предложением, согласившись с Клетусом. Это дает лучшую производительность, чем (потенциально) 2 запроса. Также меньше работы.
источник
Комбинируя полезные биты существующих ответов (ограничение количества результатов, проверка уникальности результата) и использование установленного имени метода (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); }
источник
Недокументированный метод
uniqueResultOptional
в org.hibernate.query.Query должен помочь. Вместо того, чтобы поймать,NoResultException
вы можете просто позвонитьquery.uniqueResultOptional().orElse(null)
.источник
Я решил это, используя
List<?> myList = query.getResultList();
и проверяя,myList.size()
равно ли нулю.источник
Здесь та же логика, что предлагали другие (получить список результатов, вернуть его единственный элемент или ноль), используя Google Guava и TypedQuery.
public static <T> getSingleResultOrNull(final TypedQuery<T> query) { return Iterables.getOnlyElement(query.getResultList(), null); }
Обратите внимание, что Guava вернет неинтуитивное исключение IllegalArgumentException, если набор результатов имеет более одного результата. (Исключение имеет смысл для клиентов getOnlyElement (), поскольку оно принимает список результатов в качестве аргумента, но менее понятно клиентам getSingleResultOrNull ().)
источник
Вот еще одно расширение, на этот раз на 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) }
источник
Таким образом, все решения «попытаться переписать без исключения» на этой странице имеют небольшую проблему. Либо он не выбрасывает исключение 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; }
Он возвращает значение null, если в списке есть 0 элементов, возвращает неуникальный, если в списке есть разные элементы, но не возвращает неуникальный, когда один из ваших выбранных элементов не спроектирован должным образом и возвращает один и тот же объект более одного раза.
Не стесняйтесь комментировать.
источник
getResultList().stream().distinct().reduce((a, b) -> {throw new NonUniqueResultException();}).orElse(null);
Посмотрите этот код:
return query.getResultList().stream().findFirst().orElse(null);
Когда
findFirst()
вызывается, возможно, может возникнуть исключение NullPointerException.лучший подход:
return query.getResultList().stream().filter(Objects::nonNull).findFirst().orElse(null);
источник
Я добился этого, получив список результатов и проверив, пуст ли он
public boolean exist(String value) { List<Object> options = getEntityManager().createNamedQuery("AppUsers.findByEmail").setParameter('email', value).getResultList(); return !options.isEmpty(); }
Это так раздражает, что
getSingleResult()
выкидывает исключенияБроски:
источник
Это работает для меня:
Optional<Object> opt = Optional.ofNullable(nativeQuery.getSingleResult()); return opt.isPresent() ? opt.get() : null;
источник