JDBCを使用してページネーションを実装したい。私が知りたい実際のことは、「ページ1と2のそれぞれについてデータベースから最初の50レコードを取得し、次に次の50レコードを取得する方法」です。
クエリは_Select * from data
rs.absolute(row)
が最初のページのレコードをスキップする方法であることがわかりましたが、大きな結果セットでは時間がかかり、この時間をかけたくありません。また、クエリでrownum
とlimit
+ offset
を使用したくないのは、これらがクエリで使用するのに適していないからです。クエリで使用したい。
誰かがページネーションのためにResultSet
を制限する方法を手伝ってくれますか、JDBCが私たちに与えている方法はありますか?
JDBCを使用するだけでは、これを効率的に行う方法はありません。 n行に制限およびi番目の項目から開始句をSQLに直接作成して効率化する必要があります。データベースによっては、これは実際には非常に簡単かもしれません(MySQLのLIMITキーワードを参照)。Oracleなどの他のデータベースでは、少し注意が必要です(サブクエリとrownum疑似列を使用)。
このJDBCページネーションチュートリアルを参照してください: http://Java.avdiel.com/Tutorials/JDBCPaging.html
クエリonly実際に必要なデータは現在のページに表示する必要があります。データセット全体をJavaのメモリに持ち込んで、そこでフィルタリングしないでください。それは物事を不必要に遅くするだけです。
これを適切に実装したり、特定のデータベースのSQLクエリを計算したりするのに実際に苦労している場合は、 my answerhere をご覧ください。
Update:Oracleを使用しているため、前述の回答からOracleをターゲットにした抜粋を次に示します。
Oracleでは、
rownum
句を含むサブクエリが必要です。private static final String SQL_SUBLIST = "SELECT id, username, job, place FROM" + " (SELECT id, username, job, place FROM contact ORDER BY id)" + " WHERE ROWNUM BETWEEN %d AND %d"; public List<Contact> list(int firstrow, int rowcount) { String sql = String.format(SQL_SUBLIST, firstrow, firstrow + rowcount); // Implement JDBC. return contacts; }
免責事項: SQLページネーションとJDBCページネーションに関するこのブログ投稿は私によって投稿されています。
Hibernateページネーションを無視して、SQLページネーション/ JDBCページネーションを使用できます
SQLページネーション
2つの基本的なアプローチがあります。
その方法はSQL固有です
MySQL /他の多くのSQLの場合、制限とオフセットで実行できます
Postgresql: http://microjet.ath.cx/WebWiki/ResultPaginationWithPostgresql.html
Oracleでは、「トップNクエリ」を処理するのと同じ形式を使用します。最適化されている5人の最高賃金の従業員
select * from ( select a.*, rownum rnum
from ( YOUR_QUERY_GOES_HERE -- including the order by ) a
where rownum <= MAX_ROWS )
where rnum >= MIN_ROWS
JDBCページネーション
問題は、SQLを実行すると、結果がどのようにロードされるかということです。すぐにまたは要求に応じて?これと同じSOスレッド
最初にいくつかを理解する必要があります JDBCの基本、Oracleから
Javadocごと:statement.execute()
execute: Returns true if the first object that the query returns is a ResultSet object. Use this method if the query could return one or more ResultSet objects. Retrieve the ResultSet objects returned from the query by repeatedly calling Statement.getResutSet.
カーソルを使用して、結果セットのデータにアクセスします。このカーソルは、最初はデータの最初の行の前に配置されているポインターですが、DBのカーソルとは異なります。
データは要求に応じてフェッチされます。一方、execute()を実行すると、初めてフェッチします。
次に、ロードされるデータの数は?設定可能です。 ResultSetでJava API setFetchSize()メソッドを使用して、ドライバーがDBから一度に取得する行数、一度に取得するブロックの大きさを制御できます。
たとえば、合計結果が1000であると仮定します。フェッチサイズが100の場合、1行目のフェッチはDBから100行をロードし、2行目から100行目はローカルメモリからロードされます。
JavaDocから
Gives the JDBC driver a hint as to the number of rows that should be fetched from the database when more rows are needed for ResultSet objects genrated by this Statement. If the value specified is zero, then the hint is ignored. The default value is zero.
「ヒント」という言葉に注意してください-ドライバー固有の実装によって上書きされる可能性があります。
これは、SQL開発者のようなクライアントの「行を100に制限する」機能にも基づいています。
ソリューション全体を完成させて、結果をスクロールするには、APIのResultSetタイプとScrollableCursorを考慮する必要があります。
Oracleのこの投稿から実装例を見つけることができます
oracle Toplink開発者ガイドの例112 JDBCドライバのフェッチ・サイズの本から
ReadAllQuery query = new ReadAllQuery();
query.setReferenceClass(Employee.class);
query.setSelectionCriteria(new ExpressionBuilder.get("id").greaterThan(100));
// Set the JDBC fetch size
query.setFetchSize(50);
// Configure the query to return results as a ScrollableCursor
query.useScrollableCursor();
// Execute the query
ScrollableCursor cursor = (ScrollableCursor) session.executeQuery(query);
// Iterate over the results
while (cursor.hasNext()) {
System.out.println(cursor.next().toString());
}
cursor.close();
.....................
結局のところ、質問は沸騰します
ページネーションを行うより良い方法はどれですか?
SQLアプローチで意味をなすために、SQLはORDER byである必要があることに注意してください。
それ以外の場合、次のページでいくつかの行を再度表示することができます。
以下は、JDBCドライバーおよびその他のSO回答に関するPostgresqlのドキュメントからのポイントです。
まず、ページングソリューションを適切に機能させるために、元のクエリにORDER BY句が必要です。それ以外の場合、Oracleが最初のページ、2番目のページ、およびN番目のページに同じ500行を返すことは完全に有効です
主な違いはJDBCの方法です。フェッチ中に接続を保持する必要があります。これは、たとえば、ステートレスWebアプリケーションには適さない場合があります。
SQLウェイの場合
構文はSQL固有であり、保守が容易ではない場合があります。 JDBCウェイの場合
いくつかのさらなる読み物
MySQLまたはPostgreSQLを使用している場合limitおよびoffsetがキーワードです。 MSSqlServerとOracleには似たような機能がありますが、私はもう少し痛いようです。
MySQLおよびPostgreSQLの場合は次のとおりです。
http://www.petefreitag.com/item/451.cfm
Oracleについては、こちらをご覧ください。
私はこの質問が古いことを知っていますが、これは私がページングを実装する方法です、私はそれが誰かを助けることを願っています
int pageNo = ....;
String query = "SELECT * FROM data LIMIT ";
switch (pageNo) {
case 0: // no pagination, get all rows
query += Integer.MAX_VALUE; // a big value to get all rows
break;
case 1: // get 1st page i.e 50 rows
query += 50;
break;
default:
query += String.valueOf((pageNo-1)*50 + 1) ", " + String.valueOf(50);
break;
}
PreparedStatement ps = connection.prepareStatement(query);
....
値50をpageSizeという名前の定数にして、任意の数値に変更できるようにします
これは、結果をページ分割するための休止状態ソリューションへのリンクです。 HQL-ページネーションの行識別子
JDBC接続に1つの巨大な結果セットを持たせたくないことを暗黙的に理解します。これは非常に長い間開いたままで、必要に応じてナビゲートします。
通常のアプローチは、完全なリクエストのサブセットのみを取得するために必要なSQLを追加することです。これは、残念ながらデータベースごとに異なり、SQLステートメントをベンダー固有にします。正しく思い出せば、LIMITがMySQLで使用されます。リクエストごとに適切な範囲を尋ねてください。
また、HibernateにはHQLでこれを実行できる機能が含まれていると思いますが、私はそれをよく知りません。
HibernateやさらにJava Persistence APIまたは単なるSQLなど)のようなORMフレームワークを使用していますか?
私の答え:LIMITとOFFSETを使用してください http://www.petefreitag.com/item/451.cfm
または、ROWNUM演算子を使用してください。SQLの周りにラッパーが必要ですが、基本的には
select * from (select bla.*, ROWNUM rn from (
<your sql here>
) bla where rownum < 200) where rn >= 150'
_PreparedStatement pStmt = // ... however you make it
pStmt.setFetchSize( /* desired number of records to get into memory */ );
_
setFetchSize(int)
ヒントにすぎない -たとえば、MySQLで前回使用していたときに、サポートされていませんでした。 Oracleのドキュメントを簡単に見てみると、JDBCがサポートしているようです。私はそれについては引用しませんが、少なくとも試してみる価値はあります。そして、はい、この答えは脆弱ですが、堅牢なソリューションを実装するよりも頭痛が少ないかもしれません。
基本的に、すべてに対してリクエストを発行でき、一度にメモリにフェッチサイズを取得するだけです(以前の結果を保持していない場合)。したがって、フェッチサイズを50に設定し、接続/クエリを作成し、最初の50の結果を表示します(クエリの次のバイトで別のフェッチを発生させます)。
入力:
結果:
利点のみのクエリ:
不利益:
Oracle依存
select x。* from(select c.pk_field、c.numeric_a、c.char_b、c.char_c ROW_NUMBER()over(ORDER BY decode(?、 'A1'、to_char(c.numeric_a、 'FM00000000')、 'A2 '、c.char_b、' A3 '、c.char_c、' A ')asc、decode(?、' D1 '、to_char(c.numeric_a、' FM00000000 ')、' D2 '、c.char_b、' D3 ' 、c.char_c、 'A')desc、c.pk_field asc
)AS "idx"、COUNT(*)オーバー(1で並べる)myTable cの "cnt" c.haystack =? )xここで、greatest(nvl(?、1)、1)とnvl(?、1)-1+?の間のx。 "idx"
Oracleは8i以降の標準のROW_NUMBER()ウィンドウ関数をサポートしているため、使用できます。パラメータ化されたクエリとして実行できるため、開始行番号と終了行番号を設定するだけで済みます。例えば。
SELECT *
FROM ( SELECT *, ROW_NUMBER() ORDER BY (sort key) AS rowNumber FROM <your table name> ) AS data
WHERE
rowNumber>=:start AND
rowNumber<:end
(名前付きパラメーターを使用していない場合は、:start /:endを位置パラメーターのプレースホルダー「?」に置き換えます)
ウィキペディアの SELECT SQL、ウィンドウ関数 を参照してください。この記事では、ROW_NUMBER()標準ウィンドウ関数をサポートする他のDBもリストしています。
これには以下の方法を使用できます。
public CachedRowSet getScrollableList(PreparedStatement prestmt、int rowNo) { getPageSize(); ResultSet rs = null; CachedRowSet crs = null; try { rs = prestmt.executeQuery(); crs = new CachedRowSetImpl(); crs.setPageSize(pageSize); crs.populate(rs、rowNo); } catch(SQLException ex) { logger.error( "getScrollableList()メソッドのDatabaseInterfaceの例外:" + ex.getMessage()、ex); } finally { // close rs // psを閉じるtmt } return crs; }