web-dev-qa-db-ja.com

org.hibernate.Session.clear()は有害と見なされますか?

これは設計上の問題で、私の底を保護するために提出されていない具体的なコードです。

Hibernateを使用する場合の標準的なワークフローは次のとおりです。

  1. オープンセッション
  2. トランザクションを開始
  3. ビジネスを行う(データの読み取りと変更)
  4. トランザクションをコミット
  5. セッションを閉じる

2〜4までの反復が可能です。

Session.clear()の適切な使用例は何ですか?

A:私が持っている具体的な問題は、エンティティを読み込んで変更し、セッションをclear()する(大きな)コードであり、基本的に行われた変更を破棄します。 (達成されるビジネスタスクにはエンティティの変更が含まれていないため、コードは「機能します」)。

私には、(大きな)コードの一部が保存したくない変更を加えないようにするのが正しい設計でしょうか?

B:Session.clear()は便宜/柔軟性のために存在すると思いますが、それを使用するのは良い考えではありません。

Hibernateの哲学を誤解しましたか?

C:サブ質問:タスクが完了したときにフレームワークコードが無条件にセッションをclear()するのは悪い考えですか?私見、タスクが完了したときにセッションがダーティである場合、フレームワークは文句を言うべきです!タスクが完了するのを見て、セッションを閉じる必要があります...(その分のパフォーマンスは無視)

(ラベルA、B、Cで、回答している部分を示すことができます)。

広告。 Aclear()の機能を知っているようです。これを明示的に呼び出す理由は、L1キャッシュからすべての管理対象エンティティを削除して、1つのトランザクションで大きなデータセットを処理するときに無限に大きくならないようにするためです。

明示的に永続化されていない管理対象エンティティに対して行われたすべての変更を破棄します。つまり、エンティティを安全に変更し、明示的に更新して、セッションをクリアできます。これが正しいデザインです。変更が加えられない場合(長いが読み取り専用セッション)は、clear()が常に安全です。

ステートレスセッション を使用することもできます。

広告。 B:いいえ、上記の理由で存在します。L1(セッションキャッシュ)が大きくなりすぎないようにするためです。もちろん、手動で維持することはお粗末なアイデアであり、大規模なデータセットには別のツールを使用する必要があることを示していますが、必須の場合もあります。

JPA仕様にはclear()およびflush()メソッドもあることに注意してください。この場合、flush()を呼び出す前に、常に最初にclear()を呼び出して、変更をデータベースにプッシュする(明示的な更新)必要があります。

広告。 C:ユーザーがダーティーな変更でセッションをクリアするときに、ユーザーに警告することをお勧めします(例外をスローするのではなく、警告メッセージを発行することにより)。また、frameworkコードが無条件にclear()を呼び出す必要があるとは思わない。

29

ここで私が遭遇したもう1つの理由は、同じトランザクション内でストアドプロシージャを複数回呼び出すときに以前の結果をキャッシュすることです。次のように簡略化したコード。

//Begin transaction
SessionFactory sf = HibernateSessionFactory.getFactory();
Session dbSession = sf.getCurrentSession();
dbSession.beginTransaction();

//First call to stored procedure
Query query = dbSession.getNamedQuery("RR_CUST_OPP_DATA");
query.setString("custName", "A");
List<ShipSummaryRow> shipSummaryRows = query.list();

//Second call to stored procedure
Query query = dbSession.getNamedQuery("RR_CUST_OPP_DATA");
query.setString("custName", "B");
List<ShipSummaryRow> shipSummaryRows = query.list();

//Commit both    
dbSession.getTransaction().commit();

最初の呼び出しの後にclear()がない場合、最初の呼び出しの結果セットの行は2番目の呼び出しの結果セットに複製されます。 Oracle 11gR2を使用しています。

このバグを再現する鍵は、同じトランザクション内で両方の呼び出しを行うことです。ビューパターンでオープンセッションを使用しているため、同じトランザクション内で両方の呼び出しが自動的に行われます(元のコードがそれぞれの結果を格納するループ内でprocを呼び出すため)。したがって、私はそれをバグと呼びます。それ以外の場合は機能と見なすこともできますが、コードサンプルではclear()は呼び出されるべきであると記載されていません。 session.flush()は何もしませんでした。以下のマッピングファイル。その結果、すべてのプロシージャコールの最後にclear()を追加しました。カスタムSQL呼び出しでまだテストしていません。これは些細なことです。バグが存在することに驚いた。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
    <class name="com.jfx.rr.model.ShipSummaryRow">
        <id name="id" type="integer"/>
        <property name="shipQtrString" not-null="true" type="string"/>
        <property name="shipAmount" not-null="true" type="double"/>
    </class>
    <sql-query callable="true" name="RR_CUST_OPP_DATA">
        <return class="com.jfx.rr.model.ShipSummaryRow">
            <return-property column="SHIPPED_ID" name="id"/>
            <return-property column="SHIP_QTR" name="shipQtrString"/>
            <return-property column="SHIPPED_AMOUNT" name="shipAmount"/>
        </return>
        { call RR_DASHBOARD_REPORTS_PKG.RR_CUST_OPP_DATA(?, :custName) }
    </sql-query>
</hibernate-mapping>
3
Anand