Hibernate フラッシュ操作順序の初心者向けガイド
はじめに
この記事で説明したように、Hibernate は開発者の考え方を SQL からエンティティの状態遷移にシフトします。 JPA エンティティは、次のいずれかの状態になります:
- 新規/一過性 :データベースが何も知らない新しく作成されたオブジェクトであっても、エンティティは永続化コンテキストに関連付けられていません。
- しつこい :エンティティは永続コンテキスト (第 1 レベル キャッシュに存在) に関連付けられており、このエンティティを表すデータベース行があります。
- 離れている :エンティティは以前に永続化コンテキストに関連付けられていましたが、永続化コンテキストが閉じられたか、エンティティが手動で削除されました。
- 削除済み :エンティティは削除済みとしてマークされており、永続化コンテキストはフラッシュ時にデータベースからエンティティを削除します。
オブジェクトをある状態から別の状態に移動するには、次のような EntityManager メソッドを呼び出します:
persist
merge
remove
カスケードにより、特定のイベントを親から子に伝播でき、エンティティの関係管理も容易になります。
フラッシュ時間中、Hibernate は現在の永続コンテキストによって記録された変更を SQL クエリに変換します。
ドメイン モデル
ここで、次のエンティティがあると考えてみましょう:
@Entity(name = "Post") @Table( name = "post", uniqueConstraints = @UniqueConstraint( name = "slug_uq", columnNames = "slug" ) ) public class Post { @Id private Long id; private String title; @NaturalId private String slug; //Getters and setters omitted for brevity }
slug
に注意してください プロパティは @NaturalId
でマークされています これはビジネス キーを表すためです。
ここで、次の Post
を永続化したと考えてみましょう データベース内のエンティティ:
Post post = new Post(); post.setId(1L); post.setTitle("High-Performance Java Persistence"); post.setSlug("high-performance-java-persistence"); entityManager.persist(post);
テスト時間
既存の Post
を削除したいとしましょう エンティティを作成し、同じ slug
で新しいインスタンスを永続化します 属性:
Post post = entityManager.find(Post.class, 1L); entityManager.remove(post); Post newPost = new Post(); newPost.setId(2L); newPost.setTitle("High-Performance Java Persistence Book"); newPost.setSlug("high-performance-java-persistence"); entityManager.persist(newPost);
これを行おうとすると、Hibernate は次の例外をスローします:
Query:["insert into post (slug, title, id) values (?, ?, ?)"], Params:[(high-performance-java-persistence, High-Performance Java Persistence Book, 2)] -- SQL Error: -104, SQLState: 23505 -- integrity constraint violation: unique constraint or index violation; SLUG_UQ table: POST
Hibernate は DELETE
を実行しませんでした まず、テスト ケースで行ったように。 INSERT
を実行しました それが ConstraintviolationException
を取得する理由です。 .
remove
を呼び出しているのに、なぜこれが起こっているのか不思議に思うかもしれません。 2 番目の post
を追加する前に エンティティであり、答えはフラッシュ操作の順序です。
すべてのエンティティ状態遷移は、永続コンテキストによってエンキューされるアクションを生成します。 ActionQueue
ですべてのアクション キューを確認できます。 フラッシュ時に発生するすべての操作の順序も提供するクラス:
OrphanRemovalAction
AbstractEntityInsertAction
EntityUpdateAction
QueuedOperationCollectionAction
CollectionRemoveAction
CollectionUpdateAction
CollectionRecreateAction
EntityDeleteAction
つまり、DELETE
INSERT
の間、ステートメントはフラッシュの最後に実行されます。 ステートメントは先頭に向かって実行されます。
ハッキーな回避策
この問題を回避する 1 つの方法は、remove
の後に Persistence Context を手動でフラッシュすることです。 操作:
Post post = entityManager.find(Post.class, 1L); entityManager.remove(post); entityManager.flush(); Post newPost = new Post(); newPost.setId(2L); newPost.setTitle("High-Performance Java Persistence Book"); newPost.setSlug("high-performance-java-persistence"); entityManager.persist(newPost);
これにより、目的の動作が出力されます:
Query:["delete from post where id=?"], Params:[(1)] Query:["insert into post (slug, title, id) values (?, ?, ?)"], Params:[(high-performance-java-persistence, High-Performance Java Persistence Book, 2)]
適切な修正
ただし、flush
できるからといって、 Persistence Context を手動で変更しても、これが正しい方法であるとは限りません。
マニュアル flush
呼び出しはコードの匂いです。実際には、既存のエンティティを削除して同じビジネス キーで再挿入するよりも、既存のエンティティを更新する方が適切です:
Post post = entityManager.unwrap(Session.class) .bySimpleNaturalId(Post.class) .load("high-performance-java-persistence"); post.setTitle("High-Performance Java Persistence Book");
Te UPDATE
テーブル レコードとすべてのインデックス エントリ (プライマリ キー、slug
のセカンダリ インデックス) の両方が削除されるため、削除と挿入操作ではデータベース側で追加の作業が発生します ) は、再度追加する場合にのみ削除する必要があります。
結論
JPA と Hibernate を使用する場合、フラッシュ操作の順序を知ることは非常に重要です。 Hibernate は厳密な順序で SQL ステートメントを実行するため、JDBC バッチ処理を自動的に適用できます。
flush
する必要があると思われる場合 Persistence Context を手動で変更する場合は、よく考えてください。代わりに、単一のエンティティ更新でより適切に処理される、削除してから挿入するユース ケースがあるかもしれません。