7

Web リクエスト間で Hibernate を使用してデータベース カーソルを維持する方法はありますか?

基本的に、ページネーションを実装しようとしていますが、ページングされているデータは常に変化しています (つまり、新しいレコードがデータベースに追加されます)。最初の検索 (最大 5000 件の結果を返す) を実行し、結果をページ表示すると、それらの同じレコードが常に同じページに表示されるように設定しようとしています (つまり、クエリを継続的に実行していません)。次のページ ボタンと前のページ ボタンがクリックされるたびに)。現在これを実装している方法は、ページングしているテーブルから 5000 (最大) の主キーを選択し、それらのキーをメモリに格納し、一度に 20 個の主キーを使用してデータベースから詳細を取得することです。 . でも、

Hibernate の ScrollableResults でこれを実行しようとしましたが、別の Web リクエスト / Hibernate セッション内で next() や previous() などのメソッドを呼び出すことができず、例外が発生することがわかりました (驚くことではありません)。

ScrollableResults オブジェクトをセッションに再アタッチする方法はありますか?それは、デタッチされたデータベース オブジェクトを再アタッチして永続化するのとほぼ同じ方法ですか?

4

2 に答える 2

7

オフセットはオフセットの前のすべてのデータも読み取るため、オフセットは使用しないでください。これは非常に非効率的です。

インデックス付きの一意のプロパティで並べ替え、API 呼び出しで最後のアイテム プロパティの値を返し、WHERE句を使用して、終了した場所から開始する必要があります。この最後の項目のプロパティ値がカーソル位置になります。たとえば、主キーidをカーソルとして使用する単純なページ分割されたクエリは次のようになります。

List<MyEntity> entities = entityManager
    .createQuery("""
        FROM
            MyEntity e
        WHERE
            e.id > :cursorPosition
        ORDER BY
            e.id ASC
    """, MyEntity.class)
    .setParameter("cursorPosition", cursorPosition)
    .setMaxResults(pageSize)
    .getResultList()

API への最初の呼び出しで、cursorPosition値は 0 にすることができます。2 番目の呼び出しでは、クライアントが最初の呼び出しで受け取ったカーソルをクライアントから受け取ります。Google マップのページ分割された場所のクエリがnextPageToken属性でどのように機能するかをご覧ください。

カーソルは、クエリのすべてのパラメーターを識別する文字列である必要があります。したがって、追加のパラメーターがある場合は、カーソルで取得できる必要があります。

これは複数の方法で実行できると思います。1 つの方法は、すべてのパラメーターを連結cursorPositionし、文字列で Base64 などの URL フレンドリーな文字列にエンコードし、受信時にデコードして元のパラメーターに分割することです。

 String nextPageToken = Base64.getUrlEncoder()
     .encodeToString("indexProperty=id&cursorPos=123&ageBiggerThan=65".getBytes())

API 呼び出しは、次のような json を返します。

{
    "items": [ ... ],
    "nextPageToken": "aW5kZXhQcm9wZXJ0eT1pZCZjdXJzb3JQb3M9MTIzJmFnZUJpZ2dlclRoYW49NjU="
}

そして、クライアントの次の呼び出し:

GET https://www.example.com/api/myservice/v1/myentity?pageToken=aW5kZXhQcm9wZXJ0eT1pZCZjdXJzb3JQb3M9MTIzJmFnZUJpZ2dlclRoYW49NjU=

カーソル文字列を連結して分割する部分は面倒かもしれません。トークンを作成して解析するこの作業を処理するライブラリがあるかどうかは本当にわかりません。探していたので、実際にこの質問にいます。しかし、私の推測では、GSON または Jackson を使用すると、これに関するコード行を節約できると思います。

于 2019-03-26T16:55:18.100 に答える
2

基本的に、これについてはあなた自身です。やりたいことは、OpenSessionInView フィルターを見て、独自のフィルターを作成して、要求ごとに新しい HibernateSession を作成する代わりに、ユーザーの Web セッションに関連付けられているキャッシュから 1 つを引き出すことです。

Spring WebFlow のような何らかの会話構造を提供するフレームワークがない場合は、それも構築する必要があります。おそらく、「Web セッションの有効期限が切れたとき」を超えて、その Hibernate セッションのライフサイクルを管理する何らかの方法が必要になるためです。また、同じ Web セッションからの 2 つのユーザー スレッドではなく、休止状態のセッションを共有する異なるブラウザー タブが必要になることもよくあります。(陽気さが続く可能性があります。)

于 2010-05-04T16:14:30.307 に答える