web-dev-qa-db-ja.com

結果セットを使用してJPA名前付きクエリのカウントサイズを取得する方法はありますか?

これから行う静的クエリのJPAでの名前付きクエリのアイデアは気に入っていますが、クエリのカウント結果と、クエリのサブセットからの結果リストを取得したいことがよくあります。ほぼ同じNamedQuerieを2つ作成したくありません。理想的には、私が欲しいのは次のようなものです。

@NamedQuery(name = "getAccounts", query = "SELECT a FROM Account")
.
.
  Query q = em.createNamedQuery("getAccounts");
  List r = q.setFirstResult(s).setMaxResults(m).getResultList();
  int count = q.getCount();

したがって、mが10で、sが0で、Accountに400行あるとします。 rには10個のアイテムのリストがあると思いますが、合計400行あることを知りたいと思います。 2番目の@NamedQueryを書くことができます:

@NamedQuery(name = "getAccountCount", query = "SELECT COUNT(a) FROM Account")

しかし、常にカウントが必要な場合は、DRY違反のようです。この単純なケースでは、2つを同期させるのは簡単ですが、クエリが変更された場合は、値を一列に保つために両方の@NamedQueriesを更新する必要があるのは理想的ではないようです。

ここでの一般的な使用例は、アイテムのサブセットをフェッチすることですが、合計数を示す何らかの方法が必要です(「400の1-10を表示する」)。

17
Tim

したがって、私が使用することになった解決策は、2つの@NamedQueryを作成することでした。1つは結果セット用、もう1つはカウント用ですが、静的文字列で基本クエリをキャプチャしてDRYを維持し、両方を確実にするクエリは一貫性を保ちます。したがって、上記の場合、次のようになります。

@NamedQuery(name = "getAccounts", query = "SELECT a" + accountQuery)
@NamedQuery(name = "getAccounts.count", query = "SELECT COUNT(a)" + accountQuery)
.
static final String accountQuery = " FROM Account";
.
  Query q = em.createNamedQuery("getAccounts");
  List r = q.setFirstResult(s).setMaxResults(m).getResultList();
  int count = ((Long)em.createNamedQuery("getAccounts.count").getSingleResult()).intValue();

明らかに、この例では、クエリ本体は些細なものであり、これはやり過ぎです。しかし、はるかに複雑なクエリでは、クエリ本体の定義が1つになり、2つのクエリを確実に同期させることができます。また、クエリがプリコンパイルされており、少なくともEclipselinkを使用すると、クエリを呼び出すときではなく、起動時に検証を取得できるという利点もあります。

2つのクエリ間で一貫した名前を付けることにより、クエリのベース名を基にするだけで、コードの本体をラップして両方のセットを実行できます。

14
Tim

setFirstResult / setMaxResults donotを使用して結果セットのサブセットであり、これらのメソッドを呼び出したときにクエリは実行されていません。これらは、 getResultList を呼び出したときに実行される生成されたSELECTクエリに影響します。合計レコード数を取得する場合は、別のクエリでエンティティをSELECT COUNTする必要があります(通常はページ付けする前に)。

完全な例については、 JSF、カタログファサードステートレスセッション、およびJava Persistence API を使用したサンプルアプリケーションのデータセットのページング。

5
Pascal Thivent

イントロスペクションを使用して、次のような名前付きクエリアノテーションを取得できます。

String getNamedQueryCode(Class<? extends Object> clazz, String namedQueryKey) {
    NamedQueries namedQueriesAnnotation = clazz.getAnnotation(NamedQueries.class);
    NamedQuery[] namedQueryAnnotations = namedQueriesAnnotation.value();

    String code = null;
    for (NamedQuery namedQuery : namedQueryAnnotations) {
        if (namedQuery.name().equals(namedQueryKey)) {
            code = namedQuery.query();
            break;
        }
    }

    if (code == null) {
        if (clazz.getSuperclass().getAnnotation(MappedSuperclass.class) != null) {
            code = getNamedQueryCode(clazz.getSuperclass(), namedQueryKey);
        }
    }

    //if not found
    return code;
}
1
alessandro