JPA getSingleResult()或null
我有一个insertOrUpdate
方法插入一个Entity
当它不存在或更新它,如果它。 要启用这个function,我必须要findByIdAndForeignKey
,如果返回null
insert,如果没有的话更新。 问题是如何检查它是否存在? 所以我试过getSingleResult
。 但它会抛出一个exception,如果
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
。
谢谢
抛出一个exception是如何getSingleResult()
表明它不能被发现。 我个人不能忍受这种API。 它强制虚假exception处理没有真正的好处。 你只需要将代码包装在try-catch块中。
或者你可以查询一个列表,看看它是否为空。 这不会引发exception。 事实上,因为你没有在技术上进行主键查找,可能会有多个结果(即使其中一个,两个或者你的外键或约束的组合都使得这在实践中不可能),所以这可能是更合适的解决scheme。
我把逻辑封装在下面的帮助器方法中。
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(); } }
这是一个很好的select:
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); }
Spring有一个实用的方法 :
TypedQuery<Profile> query = em.createNamedQuery(namedQuery, Profile.class); ... return org.springframework.dao.support.DataAccessUtils.singleResult(query.getResultList());
这是一个基于Rodrigo IronMan实现的types化/generics版本:
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); }
有一个替代scheme,我会build议:
Query query = em.createQuery("your query"); List<Element> elementList = query.getResultList(); return CollectionUtils.isEmpty(elementList ) ? null : elementList.get(0);
这个保护空指针exception,只保证1个结果返回。
所以不要这样做!
你有两个select:
-
运行一个select来获得结果集的COUNT,如果这个数不为零,只需要拉入数据; 要么
-
使用其他types的查询(获得一个结果集),并检查它是否有0或更多的结果。 它应该有1,所以拉出你的结果收集,你就完成了。
我会和Cletus一起提出第二个build议。 它比(可能的)2个查询提供更好的性能。 也less工作。
如果你想使用try / catch机制来处理这个问题,那么它可以用来像if / else一样行事。 当我找不到现有的logging时,我使用try / catch来添加新的logging。
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); }
结合现有答案的有用部分(限制结果数量,检查结果是否唯一)和使用已build立的方法名称(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); }
在Java 8中试试这个:
Optional first = query.getResultList().stream().findFirst();
org.hibernate.query.Query中的uniqueResultOptional
方法可以uniqueResultOptional
这个问题。 您可以调用query.uniqueResultOptional().orElse(null)
来代替必须捕获NoResultException
。
这里的逻辑与其他人build议的一样(得到resultList,返回唯一的元素或null),使用Google Guava和TypedQuery。
public static <T> getSingleResultOrNull(final TypedQuery<T> query) { return Iterables.getOnlyElement(query.getResultList(), null); }
请注意,如果结果集有多个结果,Guava将返回不直观的IllegalArgumentException。 (getOnlyElement()的客户端exception是有意义的,因为它将结果列表作为它的参数,但getSingleResultOrNull()的客户端不太理解。
这是另一个扩展,这次是在斯卡拉。
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) }
所以在这个页面中的所有“尝试重写没有例外”的解决scheme都有一个小问题。 要么不抛出NonUniqueexception,也不要扔在一些错误的情况下(见下文)。
我认为正确的解决办法是(也许)这个:
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个元素,则返回null;如果列表中有不同的元素,则返回非唯一,但当您的select中一个选项devise不正确时不返回非唯一,并且多次返回相同的对象。
随意评论。
我已经完成了(在Java 8中):
query.stream().findFirst().orElse(null);