この長い答えで怖がらないでください。このトピックは単純ではありません。あなたが任意のロック(LockModeType.NONE
を使用するのと同じ動作)を指定しない場合、デフォルトJPAことで
読むを課すは分離レベルを犯しました。
読むにはが汚れの非存在が現象を読んで必要と約束しました。単にT1は、T2がコミットした後にT2によって行われた変更を見ることができます。
JPAでのオプティミスティック・ロックの使用は、隔離レベルを に変更します。反復可能な読み取りは、です。
トランザクションの開始時と終了時にT1が一部のデータを読み取る場合、の反復可能な読み取りは、T2がデータを変更してT1の途中でコミットした場合でも同じデータを表示します。
ここではトリッキーな部分があります。 JPAはを実現します。を可能な限り単純に読み取ることができます。防止Non-Repetable read現象。 JPAはあなたの読書のスナップショットを保持するのに十分な洗練されたものではありません。これは単に、例外を発生させることによって(データが最初の読み込みから変更された場合)、2回目の読み込みを防止します。
次の2つの楽観的ロックのオプションから選択できます
:違いは何
ふたつの間に?
このPerson
エンティティの例を説明しましょう。
@Entity
public class Person {
@Id int id;
@Version int version;
String name;
String label;
@OneToMany(mappedBy = "person", fetch = FetchType.EAGER)
List<Car> cars;
// getters & setters
}
今、私たちは、データベースに格納されジョンという名前の1人を持っていると仮定しましょう。私たちはこの人をT1で読むが、彼の名前はマイクに2番目のトランザクションT2で変更する。変更が他のエンティティ(おそらく非所有関係)に言及する場合、任意のロック
Person person1 = em1.find(Person.class, id, LockModeType.NONE); //T1 reads Person("John")
Person person2 = em2.find(Person.class, id); //T2 reads Person("John")
person2.setName("Mike"); //Changing name to "Mike" within T2
em2.getTransaction().commit(); // T2 commits
System.out.println(em1.find(Person.class, id).getName()); // prints "John" - entity is already in Persistence cache
System.out.println(
em1.createQuery("SELECT count(p) From Person p where p.name='John'")
.getSingleResult()); // prints 0 - ups! don't know about any John (Non-repetable read)
楽観的読み取りロック
Person person1 = em1.find(Person.class, id, LockModeType.OPTIMISTIC); //T1 reads Person("John")
Person person2 = em2.find(Person.class, id); //T2 reads Person("John")
person2.setName("Mike"); //Changing name to "Mike" within T2
em2.getTransaction().commit(); // T2 commits
System.out.println(
em1.createQuery("SELECT count(p) From Person p where p.name='John'")
.getSingleResult()); // OptimisticLockException - The object [[email protected]] cannot be updated because it has changed or been deleted since it was last read.
LockModeType.OPTIMISTIC_FORCE_INCREMENT
なし
が使用され、我々は完全性を維持したい。 ジョンが新しい車を買収したことを説明しましょう。
楽観読み取りロック
Person john1 = em1.find(Person.class, id); //T1 reads Person("John")
Person john2 = em2.find(Person.class, id, LockModeType.OPTIMISTIC); //T2 reads Person("John")
//John gets a mercedes
Car mercedes = new Car();
mercedes.setPerson(john2);
em2.persist(mercedes);
john2.getCars().add(mercedes);
em2.getTransaction().commit(); // T2 commits
//T1 doesn't know about John's new car. john1 in stale state. We'll end up with wrong info about John.
if (john1.getCars().size() > 0) {
john1.setLabel("John has a car");
} else {
john1.setLabel("John doesn't have a car");
}
em1.flush();
楽観書き込みロック
Person john1 = em1.find(Person.class, id); //T1 reads Person("John")
Person john2 = em2.find(Person.class, id, LockModeType.OPTIMISTIC_FORCE_INCREMENT); //T2 reads Person("John")
//John gets a mercedes
Car mercedes = new Car();
mercedes.setPerson(john2);
em2.persist(mercedes);
john2.getCars().add(mercedes);
em2.getTransaction().commit(); // T2 commits
//T1 doesn't know about John's new car. john1 in stale state. That's ok though because proper locking won't let us save wrong information about John.
if (john1.getCars().size() > 0) {
john1.setLabel("John has a car");
} else {
john1.setLabel("John doesn't have a car");
}
em1.flush(); // OptimisticLockException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect)
JPAの仕様では、以下の発言があるが、HibernateとEclipseLinkのはうまく動作し、それを使用しないでください。実装はLockModeType.OPTIMISTICはなく、その逆、 を要求されたLockMode- Type.OPTIMISTIC_FORCE_INCREMENTを使用するバージョンのオブジェクトについて
は、それが許容されます。
フィールド(@oneToOne、@oneToMany)として別のタイプのエンティティを含むエンティティを更新する場合は、OPTIMISTIC_FORCE_INCREMENTを使用します。したがって、リストを更新する場合は、OPTIMISTIC_FORCE_INCREMENT(たとえば、すべてのListItem)でロックします。したがって、コミットするときに、更新したいエンティティにリンクされているすべてのエンティティが変更されていないことを確認します。ではない ? –