我有一个 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()
强制您在没有值的情况下使用异常处理,即使没有值是一种常见且自然的情况。最佳实践是异常应该只用于异常情况,而没有值则不是。由于这个原因,很多人不喜欢getSingleResult()
。甚至 JPA 诞生的 Hibernate 的作者也批评 getSingleResult()
。如果您也不喜欢它,请点赞:github.com/eclipse-ee4j/jpa-api/issues/298
抛出异常是 getSingleResult()
表示找不到它的方式。我个人无法忍受这种 API。它强制进行虚假异常处理而没有真正的好处。您只需将代码包装在 try-catch 块中。
或者,您可以查询列表并查看其是否为空。这不会引发异常。实际上,由于您在技术上没有进行主键查找,因此可能会有多个结果(即使一个、两个或您的外键或约束的组合使这在实践中变得不可能)所以这可能是更合适的解决方案。
在 Java 8 中试试这个:
Optional first = query.getResultList().stream().findFirst();
.orElse(null)
来摆脱 Optional
em.createQuery("...").getResultStream().findFirst().orElse(null);
。
我将逻辑封装在以下辅助方法中。
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();
}
}
这是一个很好的选择:
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/661414
setMaxResults()
具有流畅的界面,因此您可以编写 query.setMaxResults(1).getResultList().stream().findFirst().orElse(null)
。这应该是 Java 8+ 中最有效的调用方案。
Spring为此提供了a utility method:
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);
From JPA 2.2,而不是 .getResultList()
并检查列表是否为空或创建流,您可以返回流并获取第一个元素。
.getResultStream()
.findFirst()
.orElse(null);
.getResultStream().findFirst()
表示 Optional
如果你想使用 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);
}
这是基于 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);
}
我会推荐一个替代方案:
Query query = em.createQuery("your query");
List<Element> elementList = query.getResultList();
return CollectionUtils.isEmpty(elementList ) ? null : elementList.get(0);
这可以防止空指针异常,保证只返回 1 个结果。
所以不要那样做!
你有两个选择:
运行选择以获取结果集的 COUNT,并且仅当此计数不为零时才拉入数据;或使用其他类型的查询(获取结果集)并检查它是否有 0 个或更多结果。它应该有 1,所以把它从你的结果集合中拉出来,你就完成了。
我同意第二个建议,与 Cletus 一致。它比(可能)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);
}
org.hibernate.query.Query 中未记录的方法 uniqueResultOptional
应该可以解决问题。您可以调用 query.uniqueResultOptional().orElse(null)
而不必捕获 NoResultException
。
我通过使用 List<?> myList = query.getResultList();
并检查 myList.size()
是否等于 0 解决了这个问题。
看这段代码:
return query.getResultList().stream().findFirst().orElse(null);
调用 findFirst()
时可能会引发 NullPointerException。
最好的方法是:
return query.getResultList().stream().filter(Objects::nonNull).findFirst().orElse(null);
这与其他人建议的逻辑相同(获取结果列表,返回其唯一元素或 null),使用 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;
}
如果列表中有 0 个元素,则返回 null,如果列表中有不同的元素,则返回 nonunique,但当您的选择之一设计不正确并返回同一对象不止一次时,它不会返回 nonunique。
随意发表评论。
getResultList().stream().distinct().reduce((a, b) -> {throw new NonUniqueResultException();}).orElse(null);
我通过获取结果列表然后检查它是否为空来实现这一点
public boolean exist(String value) {
List<Object> options = getEntityManager().createNamedQuery("AppUsers.findByEmail").setParameter('email', value).getResultList();
return !options.isEmpty();
}
getSingleResult()
抛出异常太烦人了
抛出:
NoResultException - 如果没有结果 NonUniqueResultException - 如果有多个结果和一些其他异常,您可以从他们的文档中获得更多信息
如果您可以使用新的 JPA 功能,我更喜欢@Serafins 的回答,但这是一种相当直接的方法,我很惊讶之前没有在这里提到过:
try {
return (Profile) query.getSingleResult();
} catch (NoResultException ignore) {
return null;
}
`public Example validate(String param1) {
// TODO Auto-generated method stub
Example example = new Example();
Query query =null;
Object[] myResult =null;
try {
query = sessionFactory.getCurrentSession()
.createQuery("select column from table where
column=:p_param1");
query.setParameter("p_param1",param1);
}
myResult = (Object[])query.getSingleResult();//As your problem occurs here where the query has no records it is throwing an exception
String obj1 = (String) myResult[0];
String obj2 = (String) myResult[1];
example.setobj1(ISSUtil.convertNullToSpace(obj1)) example.setobj2(ISSUtil.convertNullToSpace(obj2));
return example;
}catch(Exception e) {
e.printStackTrace();
example.setobj1(ISSUtil.convertNullToSpace(""));//setting
objects to "" in exception block
example.setobj1(ISSUtil.convertNullToSpace(""));
}
return example;
}`
答案:很明显,当没有记录时,getsingleresult 会抛出一个异常,我已经通过在异常块中将对象设置为“”来处理它,即使它进入了异常,您的 JSON 对象将设置为“”/empty 希望这不是一个完美的回答,但它可能会有所帮助如果有人需要更精确地修改我的代码并纠正我总是受欢迎的。
这对我有用:
Optional<Object> opt = Optional.ofNullable(nativeQuery.getSingleResult());
return opt.isPresent() ? opt.get() : null;
不定期副业成功案例分享
getSingleResult()
用于以下情况:“我完全确定这条记录存在。如果它不存在,就杀了我”。我不想每次使用此方法时都测试null
,因为我确定它不会返回它。否则会导致大量的样板和防御性编程。如果记录确实不存在(与我们假设的相反),与几行后的NullPointerException
相比,使用NoResultException
要好得多。当然有两个版本的getSingleResult()
会很棒,但如果我必须选择一个......