読者です 読者をやめる 読者になる 読者になる

修行@ホーチミン

ホーチミン長期出張の日記です

Webアプリ DB更新時の排他制御 修正

前書き

以前の下記記事で

Webアプリ DB更新時の排他制御 - 修行@ホーチミン

楽観的ロックの方法に誤りがあったので、訂正します。

 

訂正

Serviceクラスに、EntityManagerを利用して楽観的ロックを行っておりましたが、

JpaRepositoryを用いる場合の方法が異なっておりました。

 

Serviceクラス(前回)

@Service
@Transactional
public class HotelUpdateService {

    @Autowired
    private HotelRepository hotelRepository;
    
    @PersistenceContext
    private EntityManager entityManager;

    /**
     * 
     * @return
     */
    public HotelUpdateResponseBody updateHotel(HotelInfo hotelInfo) {
               
        Hotel target = hotelRepository.findOne(hotelInfo.getId());
        
    //ここで「このアプリでの更新の流れ」①で表示したときのversionと、現在のversionを比較
        if(target.getVersion() != hotelInfo.getVersion()){
            throw new ObjectOptimisticLockingFailureException(Hotel.class, hotelInfo.getId());
        }

        
        //省略してますが、更新内容をsetする処理       
        target.setUpdateDatetime(new Timestamp(System.currentTimeMillis()));
              

        //楽観的ロック
        entityManager.lock(target, LockModeType.OPTIMISTIC); 


     //更新処理
        Hotel updatedHotel = hotelRepository.save(target);

    }
}

JpaRepositoryを用いる場合は、Entityクラスに@Versionフィールドを設置し

Repositoryクラスで、下記のようにアノテーションを用いて楽観的ロックを実装します。

 

Entityクラス

    @Column(name = "version")
    @Version
    private int version;

 

Repositoryクラス

@Repository
public interface HotelRepository extends JpaRepository<Hotel, Long> {
    
    @Lock(LockModeType.OPTIMISTIC)
    Hotel findOne(Long id);
}

よってServiceクラスではEntityManagerは不要です。

 

Serviceクラス(訂正後)

@Service
@Transactional
public class HotelUpdateService {

    @Autowired
    private HotelRepository hotelRepository;

    /**
     * 
     * @return
     */
    public HotelUpdateResponseBody updateHotel(HotelInfo hotelInfo) {
               
        Hotel target = hotelRepository.findOne(hotelInfo.getId());
        
    //ここで「このアプリでの更新の流れ」①で表示したときのversionと、現在のversionを比較
        if(target.getVersion() != hotelInfo.getVersion()){
            throw new ObjectOptimisticLockingFailureException(Hotel.class, hotelInfo.getId());
        }

        
        //省略してますが、更新内容をsetする処理       
        target.setUpdateDatetime(new Timestamp(System.currentTimeMillis()));


     //更新処理
        Hotel updatedHotel = hotelRepository.save(target);

    }
}

 

Unitテストをした際に、@MonckBeanを定義していたServiceクラスで

NoSuchBeanDefinitionException: No bean named 'entityManagerFactory' is defined

のようなエラーが発生して、EntityManagerの利用に問題が発覚し、今回の訂正にいたりました。

どうやら、@PersistenceContextを複数定義していたことに問題がありそうでした。

 

これでUpdateのリクエストを投げると、下記のようにversionで条件指定して、インクリメントするようなクエリが発行されます。

Hibernate: update hotel set ..., version=? where id=? and version=?

 

それぞれの仕組みについての理解があまく、だいぶはまってしまいましたが

こんな感じのことを繰り返して、どんどん詳しくなれたらなと思います。