Java >> Java チュートリアル >  >> Tag >> hibernate

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 を手動で変更する場合は、よく考えてください。代わりに、単一のエンティティ更新でより適切に処理される、削除してから挿入するユース ケースがあるかもしれません。


Java タグ