121

読み取り専用操作にHibernateのトランザクションが必要なのはなぜですか?

次のトランザクションはDBをロックしますか?

DBからフェッチするサンプルコード:

Transaction tx = HibernateUtil.getCurrentSession().beginTransaction(); // why begin transaction?
//readonly operation here

tx.commit() // why tx.commit? I don't want to write anything

session.close() 代わりに使用できますtx.commit()か?

4

4 に答える 4

149

読むためのトランザクションは確かに奇妙に見えるかもしれません、そしてしばしば人々はこの場合トランザクションのためのメソッドをマークしません。ただし、JDBCはとにかくトランザクションを作成しますautocommit=true。別のオプションが明示的に設定されていない場合は、JDBCが機能するだけです。ただし、トランザクションを読み取り専用としてマークする実際的な理由は次のとおりです。

データベースへの影響

  1. 読み取り専用フラグにより​​、DBMSはそのようなトランザクションまたは並行して実行されるトランザクションを最適化できます。
  2. 複数のSELECTステートメントにまたがるトランザクションがあると、Repeatable ReadまたはSnapshotで始まるレベルの適切な分離が保証されます(たとえば、PostgreSQLのRepeatable Readを参照)。そうしないと、別のトランザクションが並行してコミットした場合に、2つのSELECTステートメントで一貫性のない状況が発生する可能性があります。これは、ReadCommittedを使用する場合は関係ありません。

ORMへの影響

  1. トランザクションを明示的に開始/終了しない場合、ORMは予測できない結果を引き起こす可能性があります。たとえば、Hibernateは最初のステートメントの前にトランザクションを開始しますが、終了しません。したがって、接続は未完了のトランザクションで接続プールに返されます。それではどうなりますか?JDBCは沈黙を維持するため、これは実装固有です。MySQL、PostgreSQLドライバーはそのようなトランザクションをロールバックし、Oracleはそれをコミットします。これは接続プールレベルでも構成できることに注意してください。たとえば、C3P0にはそのようなオプションがあり、デフォルトではロールバックされます。
  2. Springは、読み取り専用トランザクションの場合にFlushMode = MANUALを設定します。これにより、ダーティチェックが不要になるなどの他の最適化が行われます。これにより、ロードしたオブジェクトの数によっては、パフォーマンスが大幅に向上する可能性があります。

アーキテクチャとクリーンなコードへの影響

メソッドがデータベースに書き込まないという保証はありません。メソッドをとしてマークすると、このトランザクションのスコープ内でDBに実際@Transactional(readonly=true)に書き込むことができるかどうかを指定できます。アーキテクチャが煩雑で、一部のチームメンバーが変更クエリを予期しない場所に配置することを選択する可能性がある場合、このフラグは問題のある場所を示します。

于 2012-11-24T10:50:42.157 に答える
63

すべてのデータベース ステートメントは、トランザクション境界 (BEGIN、COMMIT、ROLLBACK など) を明示的に宣言しない場合でも、物理トランザクションのコンテキスト内で実行されます。

トランザクション境界を明示的に宣言しない場合、各ステートメントは個別のトランザクション (autocommitモード) で実行する必要があります。これにより、環境がスレッドごとの接続バインディングを処理できない場合、ステートメントごとに 1 つの接続を開いたり閉じたりすることさえあります。

サービスを as として宣言する@Transactionalと、トランザクション期間全体で 1 つの接続が提供され、すべてのステートメントでその単一の分離接続が使用されます。これは、最初から明示的なトランザクションを使用しないよりもはるかに優れています。

大規模なアプリケーションでは、多数の同時リクエストが発生する可能性があり、データベース接続の取得リクエスト レートを下げると、アプリケーション全体のパフォーマンスが確実に向上します。

JPA は、読み取り操作でトランザクションを強制しません。TransactionRequiredExceptionトランザクションコンテキストを開始するのを忘れた場合に備えて、書き込みのみがスローされます。それにもかかわらず、読み取り専用トランザクションであってもトランザクション境界を宣言することを常にお勧めします (Spring@Transactionalでは、読み取り専用トランザクションをマークできるため、パフォーマンスが大幅に向上します)。

于 2016-10-26T18:00:51.060 に答える
18

実際、トランザクションはデータベースにロックをかけます — 優れたデータベース エンジンは、同時ロックを賢明な方法で処理します — 読み取り専用で使用すると、ビューの一貫性を損なうデータを他のトランザクションが追加しないようにするのに役立ちます。常にトランザクションが必要です (分離レベルを調整することが妥当な場合もありますが、最初からそうしないのが最善です)。トランザクション中に DB に書き込むことがない場合、トランザクションのコミットとロールバックの両方が同じになります (そして非常に安価です)。

運が良く、DB に対するクエリが、ORM がそれらを常に単一の SQL クエリにマップするようなものである場合、DB の組み込みの自動コミット動作に依存して、明示的なトランザクションなしで逃げることができますが、ORMは比較的複雑なシステムです。そのため、実装が実際に何を行っているかを確認するためにさらに多くの作業を行わない限り、そのような動作に依存することはまったく安全ではありません。明示的なトランザクション境界を記述することは、正しく行うのがはるかに簡単です (特に、AOP または同様の ORM 主導の手法でそれを行うことができる場合。Java 7 以降では、try-with-resources も使用できると思います)。

于 2012-11-24T11:39:14.347 に答える