JPQLまたはHQLで制限クエリを実行するにはどうすればよいですか?


239

Hibernate 3では、HQLで次のMySQLの制限と同等のことを行う方法はありますか?

select * from a_table order by a_table_column desc limit 0, 20;

できればsetMaxResultsを使いたくありません。これは古いバージョンのHibernate / HQLで確かに可能でしたが、消えてしまったようです。


2
使用していHibernate-5.0.12ます。これはまだ利用できませんか?setMaxResults@skaffmanの回答で@Rachelが気づいたように、100万件程度のレコードを取得してからフィルターを適用するのは非常に重いでしょう。
Rajeev Ranjan 2017年

回答:


313

これは、Hibernate 2では機能したがHibernate 3では機能しなかった理由について数年前にHibernateフォーラムに投稿されました。

Limitは、HQLでサポートされる句にはなりませんでした。setMaxResults()を使用するためのものです。

したがって、Hibernate 2で機能した場合は、仕様ではなく偶然によるものと思われます。私が考えるあなたには、いくつかのネイティブSQLでこっそりできるようにHibernate 2 HQLパーサーは、それはHQLとして認識することをクエリのビットを交換し、それがあったように残りの部分を残しているので、これはでした。ただし、Hibernate 3には適切なAST HQLパーサーがあり、許容度がかなり低くなっています。

私はQuery.setMaxResults()本当にあなたの唯一の選択肢だと思います。


3
Hibernate 3のアプローチの方が正しいと私は主張します。Hibernateの使用はデータベースに依存しないことを意図しているため、このようなことを抽象的な方法で行う必要があります。
マットb

6
私は同意しますが、そのように機能が削除されると、移行はお尻の王室の痛みになります。
skaffman 2009

52
しかし、setMaxResultsを使用すると、最初のクエリが実行され、次に、結果セットで呼び出し、結果セットsetMaxResultsから限られた数の結果行を取得してユーザーに表示します。 50レコードですが、それはしたくないのですが、クエリ自体は50レコードをクエリしたいのですが、それを行う方法はありますか?
レイチェル

2
私が知っている古い記事。私はレイチェルに完全に同意します。NHibernate(Hibernateの.Netポート)を使用して、最近2から3にアップグレードしました。同じことですが、トップXがパーサーエラーをスローしています。ただし、クエリにsetMaxResultsを追加すると、結果のSQLにTOP Mが生成されました(MsSql2008Dialectを使用)。これはいい。
Thierry_S 2013年

17
@Rachel With setMaxResultshibernateは、limitパーツをクエリに追加します。すべての結果が得られるわけではありません。あなたは、有効にすることによって、それが生成するクエリを確認することができます<property name="show_sql">true</property>
Andrejs

148
 // SQL: SELECT * FROM table LIMIT start, maxRows;

Query q = session.createQuery("FROM table");
q.setFirstResult(start);
q.setMaxResults(maxRows);

6
最高のこのようなIのでsetFirstResult、彼らはちょうど言うここで、他の場所のに対し、実際にこの回答に記載されているsetMaxResultsこのおよびsetMaxResultsオフセットを設定する方法を言及することなく、そのに。
demongolem 2014

19

オブジェクトで使用setMaxResults()したくないQuery場合は、常に通常のSQLの使用に戻すことができます。


37
それは本当にそれほどエキサイティングなことではありません。
スティーブドブラウン、2009

8
HQLもエキサイティングだとは思いません。なぜ制限を適用し、あなたのDBサーバー上のビューを作成していないし、そのビューを見にHQLを得る:P
PJP

1
これはそれらの1つにすぎませんが、SQLは各クエリのHQLよりもはるかに簡単ですが、ビューの作成やネイティブSQLの記述は、リファクタリングにはそれほど優れていない傾向があります。できる限り避けようとしています。その実際の実際の問題は、MySQLクエリを間違って書いて、setMaxResultsがおかしいと思ったことです。そうではなかった。
スティーブドブラウン、2009

また、異なるDBMSベンダーを切り替えようとすると、苦痛が待っています。
Olgun Kaya

5

setMaxResultsを使用したくない場合は、listの代わりにQuery.scrollを使用して、必要な行をフェッチすることもできます。たとえば、ページングに役立ちます。


おかげで、受け入れられた回答は私にとって問題を解決しませんでした。setMaxResults()最初にメモリ内のすべてのレコードをロードしてから、サブリストを作成します。これにより、メモリが不足しているため、レコードが数十万以上ある場合にサーバーがクラッシュします。ただし、JPAタイプのクエリからHibernateクエリに進むことができQueryImpl hibernateQuery = query.unwrap(QueryImpl.class)、その後、scroll()提案された方法を使用できます。
toongeorges

少なくともOracle方言ではこれは当てはまりません(HibernateはROWNUM仮想列を使用します)。多分それはドライバーに依存します。他のDBにはTOP関数があります。
Lluis Martinez 2017

私のクエリは結合フェッチを使用しています。これにより、Hibernateの警告「コレクションフェッチで指定されたfirstResult / maxResults;メモリに適用」が発生します。したがって、結合フェッチを使用すると、Hibernateはコレクション全体をメモリにロードします。パフォーマンス上の理由により、結合を削除することはできません。ScrollableResultsを使用すると、メモリにロードするレコードをより詳細に制御できます。単一のScrollableResultsですべてのレコードをロードすることはできません。これもメモリ不足になるためです。異なるScrollableResultsで複​​数のページをロードする実験をしています。これが機能しない場合は、SQLを使用します。
toongeorges 2017年

それは奇妙です、私はそれに遭遇したことがありません。はい、ときどきストレートJDBCを使用することが、特に大規模/バッチプロセスの場合に有効です。
Lluis Martinez 2017

@OneToMany関係が問題を引き起こしています。どういうわけか、HibernateでOracle集計関数LISTAGGを実行して複数の値を1つの値に連結できる場合、結合を削除してサブクエリで置き換えることができます。
toongeorges

2

これにはページネーションを簡単に使用できます。

    @QueryHints({ @QueryHint(name = "org.hibernate.cacheable", value = "true") })
    @Query("select * from a_table order by a_table_column desc")
    List<String> getStringValue(Pageable pageable);

new PageRequest(0, 1)レコードをフェッチするにはリストを渡し、リストから最初のレコードをフェッチする必要があります


2

これは非常に一般的な質問であるため、 この回答に基づいてこの記事を作成しました。

方法setFirstResultsetMaxResults Query

JPAおよびHibernateのQuery場合、setFirstResultメソッドはと同等でOFFSETあり、setMaxResultsメソッドはLIMITと同等です。

List<Post> posts = entityManager
.createQuery(
    "select p " +
    "from Post p " +
    "order by p.createdOn ")
.setFirstResult(10)
.setMaxResults(10)
.getResultList();

LimitHandlerアブストラクション

Hibernate LimitHandlerはデータベース固有のページネーションロジックを定義し、次の図に示すように、Hibernateは多くのデータベース固有のページネーションオプションをサポートしています。

<code> LimitHandler </ code>の実装

これで、使用している基礎となるリレーショナルデータベースシステムに応じて、上記のJPQLクエリは適切なページネーション構文を使用します。

MySQL

SELECT p.id AS id1_0_,
       p.created_on AS created_2_0_,
       p.title AS title3_0_
FROM post p
ORDER BY p.created_on
LIMIT ?, ?

PostgreSQL

SELECT p.id AS id1_0_,
       p.created_on AS created_2_0_,
       p.title AS title3_0_
FROM post p
ORDER BY p.created_on
LIMIT ?
OFFSET ?

SQLサーバー

SELECT p.id AS id1_0_,
       p.created_on AS created_on2_0_,
       p.title AS title3_0_
FROM post p
ORDER BY p.created_on
OFFSET ? ROWS 
FETCH NEXT ? ROWS ONLY

オラクル

SELECT *
FROM (
    SELECT 
        row_.*, rownum rownum_
    FROM (
        SELECT 
            p.id AS id1_0_,
            p.created_on AS created_on2_0_,
            p.title AS title3_0_
        FROM post p
        ORDER BY p.created_on
    ) row_
    WHERE rownum <= ?
)
WHERE rownum_ > ?

setFirstResultおよびを使用する利点setMaxResultsは、Hibernateがサポートされているリレーショナルデータベースのデータベース固有のページネーション構文を生成できることです。

また、JPQLクエリのみに限定されません。ネイティブSQLクエリにはsetFirstResultsetMaxResultsメソッド7を使用できます。

ネイティブSQLクエリ

ネイティブSQLクエリを使用する場合、データベース固有のページネーションをハードコードする必要はありません。Hibernateはそれをクエリに追加できます。

したがって、PostgreSQLでこのSQLクエリを実行している場合:

List<Tuple> posts = entityManager
.createNativeQuery(
    "SELECT " +
    "   p.id AS id, " +
    "   p.title AS title " +
    "from post p " +
    "ORDER BY p.created_on", Tuple.class)
.setFirstResult(10)
.setMaxResults(10)
.getResultList();

Hibernateは次のように変換します。

SELECT p.id AS id,
       p.title AS title
FROM post p
ORDER BY p.created_on
LIMIT ?
OFFSET ?

かっこいいですよね?

SQLベースのページネーションを超えて

フィルター処理と並べ替えの条件にインデックスを付けることができる場合、ページ分割は適切です。ページネーションの要件が動的フィルタリングを意味する場合、ElasticSearchなどの逆インデックスソリューションを使用する方がはるかに優れたアプローチです。

チェックアウトこの記事を詳細については。


1

String hql = "select userName from AccountInfo order by points desc 5";

これは使用せずに私のために働きました setmaxResults();

キーワードを使用せずに、最後の最大値(この場合は5)を指定するだけlimitです。:P


7
うーん...これについてはあまりよくわかりません。[引用が必要]これは偶然の可能性があり、Hibernateの新しいバージョンで突然動作しなくなる可能性があります。
Konrad「ktoso」Malawski、2011

4
公式資料をご参照ください。
Do Nhu Vy

1
これは、Hibernateバージョン5.2.12決勝で私のために働いていない
jDub9

1
動作していません。Hibernate4.xもサポートしていません。
Faiz Akram

0

私の見解では、HQL(hibernate 3.x)に制限がある場合でも、解析エラーが発生するか、無視されるだけです。(制限の前に+ desc / ascで注文した場合、それは無視されます。制限の前にdesc / ascがない場合、解析エラーが発生します)


0

このモードで制限を管理できる場合

public List<ExampleModel> listExampleModel() {
    return listExampleModel(null, null);
}

public List<ExampleModel> listExampleModel(Integer first, Integer count) {
    Query tmp = getSession().createQuery("from ExampleModel");

    if (first != null)
        tmp.setFirstResult(first);
    if (count != null)
        tmp.setMaxResults(count);

    return (List<ExampleModel>)tmp.list();
}

これは、制限またはリストを処理するための本当に単純なコードです。


0
Criteria criteria=curdSession.createCriteria(DTOCLASS.class).addOrder(Order.desc("feild_name"));
                criteria.setMaxResults(3);
                List<DTOCLASS> users = (List<DTOCLASS>) criteria.list();
for (DTOCLASS user : users) {
                System.out.println(user.getStart());
            }

0

ネイティブクエリを作成する必要があります。これを参照してください。

@Query(value =
    "SELECT * FROM user_metric UM WHERE UM.user_id = :userId AND UM.metric_id = :metricId LIMIT :limit", nativeQuery = true)
List<UserMetricValue> findTopNByUserIdAndMetricId(
    @Param("userId") String userId, @Param("metricId") Long metricId,
    @Param("limit") int limit);

0

以下のクエリを使用できます

NativeQuery<Object[]> query = session.createNativeQuery(select * from employee limit ?)
query.setparameter(1,1);

0

以下のスニペットは、HQLを使用して制限クエリを実行するために使用されます。

Query query = session.createQuery("....");
query.setFirstResult(startPosition);
query.setMaxResults(maxRows);

このリンクからデモアプリケーションを入手できます。


弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.