注:最適化ロックに関する説明は不要です。この質問は、楽観的ロックを使用する場合の特定のSpring Dataの動作と思われるものについてです。
Jpa specs から、エンティティに_@Version
_注釈付きフィールドがある場合は常に、エンティティで楽観的ロックが自動的に有効になります。
リポジトリを使用した春のデータテストプロジェクトでこれを行うと、ロックがアクティブにならないようです。 Infact no OptimisticLockException
は、Non Repetable Readテストの実行中にスローされます(JPA仕様の93ページのP2を参照)。
ただし、春から docs 単一のメソッドに@Lock(LockModeType.OPTIMISTIC)
で注釈を付けると、基盤となるシステムがOptimisticLockException
を正しくスローすることがわかります(これにより、春とわずかに異なる形式でスタックを伝播しました)。
これは正常ですか、それとも何かを見逃しましたか? Springデータでオプティミスティックな動作を有効にするために、すべてのメソッドに注釈を付ける(またはロックを取得するベースリポジトリ実装を作成する)義務がありますか?
Spring Boot Projectバージョン1.4.5のコンテキストでSpringデータを使用しています。
テスト:
_public class OptimisticLockExceptionTest {
static class ReadWithSleepRunnable extends Thread {
private OptimisticLockExceptionService service;
private int id;
UserRepository userRepository;
public ReadWithSleepRunnable(OptimisticLockExceptionService service, int id, UserRepository userRepository) {
this.service = service;
this.id = id;
this.userRepository = userRepository;
}
@Override
public void run() {
this.service.readWithSleep(this.userRepository, this.id);
}
}
static class ModifyRunnable extends Thread {
private OptimisticLockExceptionService service;
private int id;
UserRepository userRepository;
public ModifyRunnable(OptimisticLockExceptionService service, int id, UserRepository userRepository) {
this.service = service;
this.id = id;
this.userRepository = userRepository;
}
@Override
public void run() {
this.service.modifyUser(this.userRepository, this.id);
}
}
@Inject
private OptimisticLockExceptionService service;
@Inject
private UserRepository userRepository;
private User u;
@Test(expected = ObjectOptimisticLockingFailureException.class)
public void thatOptimisticLockExceptionIsThrown() throws Exception {
this.u = new User("email", "p");
this.u = this.userRepository.save(this.u);
try {
Thread t1 = new ReadWithSleepRunnable(this.service, this.u.getId(), this.userRepository);
t1.start();
Thread.sleep(50);// To be sure the submitted thread starts
assertTrue(t1.isAlive());
Thread t2 = new ModifyRunnable(this.service, this.u.getId(), this.userRepository);
t2.start();
t2.join();
assertTrue(t1.isAlive());
t1.join();
} catch (Exception e) {
e.printStackTrace();
}
}
}
_
テストサービス:
_@Component
public class OptimisticLockExceptionService {
@Transactional
public User readWithSleep(UserRepository userRepo, int id) {
System.err.println("started read");
User op = userRepo.findOne(id);
Thread.currentThread();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.err.println("read end");
return op;
}
@Transactional
public User modifyUser(UserRepository userRepo, int id) {
System.err.println("started modify");
User op = userRepo.findOne(id);
op.setPassword("p2");
System.err.println("modify end");
return userRepo.save(op);
}
}
_
リポジトリ:
_@Repository
public interface UserRepository extends CrudRepository<User, Integer> {
}
_
Spring Data JPAを使用した楽観的ロックは、使用されるJPA実装によって実装されます。
JPA仕様の93ページのP2を参照しています。セクションは次で始まります:
トランザクションT1がバージョン管理されたオブジェクトで
lock(entity, LockModeType.OPTIMISTIC)
を呼び出す場合、エンティティーマネージャーは次の現象が発生しないようにする必要があります。
しかし、テストではそのようなシナリオは作成されません。メソッドlock
が呼び出されることはありません。したがって、関連するロックは発生しません。特に、エンティティをロードするだけではlock
は呼び出されません。
オブジェクトを変更すると状況が変わります(仕様の93ページ目ですが最後の段落)。
バージョン付きオブジェクトが別の方法で更新または削除された場合、
LockModeType.OPTIMISTIC_FORCE_INCREMENT
への明示的な呼び出しが行われていなくても、実装はEntityManager.lock
の要件が満たされていることを確認する必要があります。
注:同じリポジトリを使用して2つのスレッドをスポーンしているため、同じEntityManager
が使用されます。これがEntityManager
でサポートされているかどうかは疑問ですが、この方法で実際に2つのトランザクションを取得しているかどうかはわかりませんが、それは別の日の質問です