ChatGPT解决这个技术问题 Extra ChatGPT

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

谢谢

getSingleResult() 强制您在没有值的情况下使用异常处理,即使没有值是一种常见且自然的情况。最佳实践是异常应该只用于异常情况,而没有值则不是。由于这个原因,很多人不喜欢getSingleResult()。甚至 JPA 诞生的 Hibernate 的作者也批评 getSingleResult()。如果您也不喜欢它,请点赞:github.com/eclipse-ee4j/jpa-api/issues/298

S
Sk8erPeter

抛出异常是 getSingleResult() 表示找不到它的方式。我个人无法忍受这种 API。它强制进行虚假异常处理而没有真正的好处。您只需将代码包装在 try-catch 块中。

或者,您可以查询列表并查看其是否为空。这不会引发异常。实际上,由于您在技术上没有进行主键查找,因此可能会有多个结果(即使一个、两个或您的外键或约束的组合使这在实践中变得不可能)所以这可能是更合适的解决方案。


我不同意,getSingleResult() 用于以下情况:“我完全确定这条记录存在。如果它不存在,就杀了我”。我不想每次使用此方法时都测试 null,因为我确定它不会返回它。否则会导致大量的样板和防御性编程。如果记录确实不存在(与我们假设的相反),与几行后的 NullPointerException 相比,使用 NoResultException 要好得多。当然有两个版本的 getSingleResult() 会很棒,但如果我必须选择一个......
@cletus Null 确实是数据库的有效返回值。
@TomaszNurkiewicz 这是一个好点。但是,似乎应该有某种类型的“getSingleResultOrNull”。我想您可以为此创建一个包装器。
以下是从 getSingleResult() 开始抛出异常的好处方面的一些信息: 查询可用于检索几乎任何内容,包括单行中单列的值。如果 getSingleResult() 将返回 null,则您无法判断查询是否不匹配任何行,或者查询是否匹配行但所选列包含 null 作为其值。来自:stackoverflow.com/a/12155901/1242321
它应该返回 Optional。这是指示缺失值的好方法。
P
Pang

在 Java 8 中试试这个:

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

您可以通过添加 .orElse(null) 来摆脱 Optional
它可以更好:em.createQuery("...").getResultStream().findFirst().orElse(null);
E
Eugene Katz

我将逻辑封装在以下辅助方法中。

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();
    }
}

请注意,您可以通过调用 Query.setMaxResults(1) 更加优化。遗憾的是,由于 Query 是有状态的,因此您需要捕获 Query.getMaxResults() 的值并在 try-finally 块中修复对象,如果 Query.getFirstResult() 返回任何有趣的东西,可能会完全失败。
这就是我们在项目中实施它的方式。这个实现从来没有任何问题
n
nbrooks

这是一个很好的选择:

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+ 中最有效的调用方案。
D
Didier L

Spring为此提供了a utility method

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

请记住,当有多个元素时,使用 DataAccessUtils.singleResult 会引发异常。
L
LovaBill

我已经完成了(在 Java 8 中):

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

你的查询是什么意思?
你的意思是休眠查询?如果我想使用纯 JPA api 怎么办? javax.persistence.Query 中没有这样的方法
@EnricoGiurin,我已经编辑了片段。工作正常。没有 try-catch,也没有 list.size 检查。最好的一种衬里解决方案。
g
geisterfurz007

From JPA 2.2,而不是 .getResultList() 并检查列表是否为空或创建流,您可以返回流并获取第一个元素。

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

或者简单地用 .getResultStream().findFirst() 表示 Optional
S
Sorter

如果你想使用 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); 
}

E
Emmanuel Touzery

这是基于 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);
}

a
aces.

我会推荐一个替代方案:

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

这可以防止空指针异常,保证只返回 1 个结果。


C
Carl Smotricz

所以不要那样做!

你有两个选择:

运行选择以获取结果集的 COUNT,并且仅当此计数不为零时才拉入数据;或使用其他类型的查询(获取结果集)并检查它是否有 0 个或更多结果。它应该有 1,所以把它从你的结果集合中拉出来,你就完成了。

我同意第二个建议,与 Cletus 一致。它比(可能)2 个查询提供更好的性能。工作也少。


选项 3 尝试/捕获 NoResultException
P
Peter Walser

结合现有答案的有用位(限制结果数量,检查结果是否唯一)并使用已建立的方法名称(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);
}

a
at_sof

org.hibernate.query.Query 中未记录的方法 uniqueResultOptional 应该可以解决问题。您可以调用 query.uniqueResultOptional().orElse(null) 而不必捕获 NoResultException


T
TylerH

我通过使用 List<?> myList = query.getResultList(); 并检查 myList.size() 是否等于 0 解决了这个问题。


L
Leandro Ferreira

看这段代码:

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

调用 findFirst() 时可能会引发 NullPointerException。

最好的方法是:

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


t
tpdi

这与其他人建议的逻辑相同(获取结果列表,返回其唯一元素或 null),使用 Google Guava 和 TypedQuery。

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

请注意,如果结果集有多个结果,Guava 将返回不直观的 IllegalArgumentException。 (该异常对 getOnlyElement() 的客户端有意义,因为它将结果列表作为其参数,但对 getSingleResultOrNull() 的客户端来说不太容易理解。)


P
Pete Montgomery

这是另一个扩展,这次是在 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)
}

t
tg44

所以这个页面中所有的“尝试无例外地重写”的解决方案都有一个小问题。它要么不抛出 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。

随意发表评论。


感谢上帝,有人指出了一个明显的事实:如果 OP 正在调用 getSingleResult() 他期望结果是唯一的,而不是仅仅得到恰好是(可能是无序的)查询中的第一个!使用 Java8 会更干净:getResultList().stream().distinct().reduce((a, b) -> {throw new NonUniqueResultException();}).orElse(null);
U
Uchephilz

我通过获取结果列表然后检查它是否为空来实现这一点

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

getSingleResult() 抛出异常太烦人了

抛出:

NoResultException - 如果没有结果 NonUniqueResultException - 如果有多个结果和一些其他异常,您可以从他们的文档中获得更多信息


s
splashout

如果您可以使用新的 JPA 功能,我更喜欢@Serafins 的回答,但这是一种相当直接的方法,我很惊讶之前没有在这里提到过:

    try {
        return (Profile) query.getSingleResult();
    } catch (NoResultException ignore) {
        return null;
    }

M
Mohitkatta
            `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 希望这不是一个完美的回答,但它可能会有所帮助如果有人需要更精确地修改我的代码并纠正我总是受欢迎的。


p
peterzinho16

这对我有用:

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