2

私はstackoverflowの投稿を検索しましたが、これが重複していないことを願っています。

初めてオプティミスティックロックを試してみました。スプリングマネージドLockModeTypeを使用して実行できますが、自分でLockModeを定義することはできません。

コード例は次のとおりです。

私は以下を使用して永続コンテキストを注入しています:

@PersistenceContext
private EntityManager entityManager;

最初のアプローチ:注釈トランザクションを使用する

@Transactional
    public void updateUserProfile(UserProfile userProfile) {
        entityManager.lock(userProfile, LockModeType.OPTIMISTIC); // 1*
        entityManager.merge(userProfile);
    }

1での例外:java.lang.IllegalArgumentException: entity not in the persistence context

2番目のアプローチ:トランザクションの管理

public void updateUserProfile(UserProfile userProfile) {
        entityManager.getTransaction().begin(); // 2*
        entityManager.lock(userProfile, LockModeType.OPTIMISTIC); 
        entityManager.merge(userProfile);
        entityManager.getTransaction().commit();
    }

2での例外:Not allowed to create transaction on shared EntityManager - use Spring transactions or EJB CMT instead

3番目のアプローチ:共有entityManagerで例外が発生したため、entityManagerFactoryからEntityManagerを作成しようとしました。

@Transactional
public void updateUserProfile(UserProfile userProfile) {
        EntityManager em = entityManager.getEntityManagerFactory().createEntityManager();
        em.getTransaction().begin();
        em.lock(userProfile, LockModeType.OPTIMISTIC);  // 3*
        em.merge(userProfile);
        em.getTransaction().commit();
    }

3時の例外:entity not in the persistence context

私のアプリケーションコンテキストではorg.springframework.orm.jpa.JpaTransactionManager、定義transactionManagerorg.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean定義に使用していますentityManagerFactory

前もって感謝します!

4

1 に答える 1

7

JPA エンティティーをロックするための条件:

  1. あなたはトランザクションコンテキスト内にいる必要があります
  2. エンティティは管理された状態 (つまり、永続化コンテキスト内でアクティブ) でなければなりません。

(2) に違反しているようです - 切り離されたエンティティをロックしようとしています。

以前に merge() できました。

重要なポイント:

  • エンティティを変更してトランザクションを実行し、エンティティに @Version でマークされたバージョン属性がある場合、楽観的ロックはエンティティの粒度で自動的に実行されます。バージョン属性が更新され、DB でまだ変更されていない場合、書き込みは成功します。これは、破損を避けるために個々のエンティティへのすべての書き込みがシリアル化される、一般的な単純なロックのケースです。各エンティティ/レコードを個別に処理できる場合、これがデフォルトの動作であるため、LockMode を設定する必要はありません。このオプションをよく見てください-これはあなたの単純な要件に合っていると思います.

  • より複雑な処理があり、一貫性のある一貫したアトミック操作として、論理的に関連する多数のエンティティ インスタンス間で読み取りまたは書き込みを実行する必要がある場合、他のすべての書き込みはその間ブロック/分離されます。独自のロック モードを設定する必要があります。 、自動化された個々のエンティティごとのロックは機能しないためです。一貫した方法で読み書きされる一連のエンティティを慎重に設計し、独自の手動ロック ソリューションを手動で設計および実装する必要があります。おそらく、関係階層の最上位エンティティを選択して、すべての「グローバル」ロックを記録します。関連する「子」エンティティ (その @Version 属性を利用)。

  • 手動ロック ソリューションでは、ロックを受け入れるためにすべての DB 書き込みロジックが必要です。これは、同じエンティティで動作する他のトランザクションが、ロック設計を理解する必要があり、実際に書き込み前にロックを解除しようとする必要があることを意味します。さまざまな書き込みがブロックおよびシリアライズされないロックは、実際にはまったくロックではありません。

  • 楽観的ロックの場合、ロックを解除する正確なタイミングは柔軟です。コミットおよびフラッシュ操作が発生するまで、ロックはチェックされず、DB に書き込まれません。したがって、tx の開始からコミットまで、いつでもロックを解除できます。悲観的ロックの場合は逆です。コードの重要な領域は、自分の命がかかっているかのように保護する必要があります。通常、悲観的な lockMode は、トランザクションを開始する em.find() または em.query の一部として設定する必要があります。または、管理対象オブジェクトが既にメモリ内にあるためにこれが不可能な場合は、em.flush() を実行し、 em.refresh(PESSIMISTIC_WRITE)

=:-)

于 2012-10-15T07:51:45.393 に答える