JPA getSingleResult()またはnull


136

存在しない場合に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

ありがとう

回答:


266

例外をスローすると、getSingleResult()それが見つからないことを示します。個人的には、この種のAPIには我慢できません。実際の利益のために偽の例外処理を強制します。コードをtry-catchブロックでラップするだけです。

または、リストをクエリして、リストが空かどうかを確認することもできます。それは例外をスローしません。実際には、主キーのルックアップを技術的に行っていないため、複数の結果が生じる可能性があります(1つ、両方、または外部キーまたは制約の組み合わせによってこれが実際に不可能になる場合でも)、これはおそらくより適切なソリューションです。


115
同意しませんgetSingleResult()。「このレコードが存在することは完全に確信しています。存在しない場合は、私を撮影してください。」nullこのメソッドを使用するたびにテストする必要はありません。返されないことが確実だからです。それ以外の場合は、多くの定型文や防御的なプログラミングが発生します。そして、レコードが実際に存在しない場合(私たちが想定したものとは逆に)、後の数行NoResultExceptionと比較したほうがはるかに優れていNullPointerExceptionます。もちろん、の2つのバージョンを持つgetSingleResult()ことは素晴らしいことですが、1つを
取得

8
@cletus Nullは、実際にはデータベースの有効な戻り値です。
Bill Rosmus

12
@TomaszNurkiewiczいいですね。ただし、「getSingleResultOrNull」のタイプが必要なようです。そのようなラッパーを作成できると思います。
cbmeeks 2013年

2
getSingleResult()からスローされた例外開始の利点に関する情報を次に示します。クエリを使用して、単一行の単一列の値を含むほとんどすべてのものを取得できます。getSingleResult()がnullを返す場合、クエリがどの行とも一致しなかったか、またはクエリが行に一致したが、選択した列の値にnullが含まれているかどうかを判断できませんでした。from:stackoverflow.com/a/12155901/1242321
user1242321

5
Optional <T>を返す必要があります。これは、欠損値を示すのに適した方法です。
Vivek Kothari

33

ロジックを次のヘルパーメソッドにカプセル化しました。

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

2
Query.setMaxResults(1)を呼び出すと、もう少し最適化できることに注意してください。悲しいことに、Queryはステートフルであるため、Query.getMaxResults()の値をキャプチャして、try-finallyブロックでオブジェクトを修正し、Query.getFirstResult()が興味深いものを返した場合は完全に失敗する可能性があります。
Patrick Linskey、2011

それが私たちのプロジェクトに実装されている方法です。この実装で問題が発生したことはありません
walv '13

25

これをJava 8で試してください:

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

3
追加することでオプションを取り除くことができます.orElse(null)
Justin Rowe

24

これを行うための適切なオプションを次に示します。

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

2
きちんと!私は受け入れTypedQuery<T>ますが、その場合、getResultList()はすでにとして正しく入力されていList<T>ます。
2013

fetch()エンティティと組み合わせると、完全に入力されない場合があります。stackoverflow.com/a/39235828/661414を
Leukipp

1
これは非常に優れたアプローチです。setMaxResults()あなたが書くことができるように流暢なインターフェースを持っていることに注意してくださいquery.setMaxResults(1).getResultList().stream().findFirst().orElse(null)。これは、Java 8以降で最も効率的な呼び出し方式です。
ダークヒルブレヒト


15

私は(Java 8で)完了しました:

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

クエリとはどういう意味ですか?
Enrico Giurin 2017年

あなたはHibernateQueryを意味しますか?純粋なJPA APIを使用したい場合はどうなりますか?javax.persistence.Queryにはそのようなメソッドはありません
Enrico Giurin 2017年

2
@EnricoGiurin、スニペットを編集しました。うまくいきます。try-catch、list.sizeチェックはありません。最も素晴らしいワンライナーソリューション。
LovaBill

10

JPA 2.2以降では.getResultList()、リストが空かどうかを確認したり、ストリームを作成したりする代わりに、ストリームを返して最初の要素を取得できます。

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

6

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

5

私がお勧めする代替案があります:

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

これにより、Nullポインタ例外から保護され、結果が1つだけ返されることが保証されます。


4

だから、それをしないでください!

次の2つのオプションがあります。

  1. 選択を実行して結果セットのCOUNTを取得し、このカウントがゼロ以外の場合にのみデータをプルします。または

  2. 他の種類のクエリ(結果セットを取得する)を使用して、0以上の結果があるかどうかを確認します。これは1になるはずなので、結果コレクションからそれを引き出しれば完了です。

私はクレタスに同意して、2番目の提案を採用します。(潜在的に)2つのクエリよりもパフォーマンスが向上します。作業量も減ります。


1
オプション3のtry /キャッチNoResultException
CED

3

既存の回答の有用なビットを組み合わせ(結果の数を制限し、結果が一意であることを確認)、確立されたメソッド名(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);
}

3

uniqueResultOptionalorg.hibernate.query.Query のドキュメントに記載されていないメソッドは、トリックを行う必要があります。をキャッチする代わりに、NoResultException単に呼び出すことができますquery.uniqueResultOptional().orElse(null)


2

これを使用List<?> myList = query.getResultList();myList.size()てゼロに等しいかどうかを確認することで解決しました。


1

これは、Google GuavaとTypedQueryを使用した、他の提案と同じロジック(resultListを取得し、その唯一の要素またはnullを返す)です。

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

結果セットに複数の結果がある場合、Guavaは直感的でないIllegalArgumentExceptionを返すことに注意してください。(この例外は、結果リストを引数として取るため、getOnlyElement()のクライアントには意味がありますが、getSingleResultOrNull()のクライアントにはわかりません。)


1

これがもう1つの拡張です。今回は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)
}

1

このコードを見てください:

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

ときにfindFirst()呼ばれるかもしれないNullPointerExceptionがthrowedすることができます。

最良のアプローチは:

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


0

したがって、このページの「例外なしで書き直そう」という解決策にはすべて、小さな問題があります。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を返し、リストに異なる要素がある場合は非一意を返しますが、選択の1つが適切に設計されておらず、同じオブジェクトを1回以上返す場合は非一意を返しません。

コメントしてください。


0

結果リストを取得し、それが空かどうかを確認することでこれを達成しました

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

getSingleResult()例外をスローするのはとても煩わしい

スロー:

  1. NoResultException-結果がない場合
  2. NonUniqueResultException-複数の結果と、ドキュメントから詳細情報を取得できるその他の例外の場合

-3

それは私にはたらきます:

Optional<Object> opt = Optional.ofNullable(nativeQuery.getSingleResult());
return opt.isPresent() ? opt.get() : null;
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.