web-dev-qa-db-ja.com

Spring data-楽観的ロックを有効にする

:最適化ロックに関する説明は不要です。この質問は、楽観的ロックを使用する場合の特定の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> {
}
_
6
vratojr

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つのトランザクションを取得しているかどうかはわかりませんが、それは別の日の質問です

7
Jens Schauder