How to enable LockModeType.PESSIMISTIC_WRITE when looking up entities with Spring Data JPA?

后端 未结 3 1840
难免孤独
难免孤独 2020-12-02 18:27

How can I achieve the equivalent of this code:

tx.begin();
Widget w = em.find(Widget.class, 1L, LockModeType.PESSIMISTIC_WRITE);
w.decrementBy(4);
em.flush()         


        
3条回答
  •  时光取名叫无心
    2020-12-02 18:39

    If you don't want to override standard findOne() method, you can acquire a lock in your custom method by using select ... for update query just like this:

    /**
     * Repository for Wallet.
     */
    public interface WalletRepository extends CrudRepository, JpaSpecificationExecutor {
    
        @Lock(LockModeType.PESSIMISTIC_WRITE)
        @Query("select w from Wallet w where w.id = :id")
        Wallet findOneForUpdate(@Param("id") Long id);
    }
    

    However, if you are using PostgreSQL, things can get a little complicated when you want to set lock timeout to avoid deadlocks. PostgreSQL ignores standard property javax.persistence.lock.timeout set in JPA properties or in @QueryHint annotation.

    The only way I could get it working was to create a custom repository and set timeout manually before locking an entity. It's not nice but at least it's working:

    public class WalletRepositoryImpl implements WalletRepositoryCustom {
    
    @PersistenceContext
    private EntityManager em;
    
    
    @Override
    public Wallet findOneForUpdate(Long id) {
        // explicitly set lock timeout (necessary in PostgreSQL)
        em.createNativeQuery("set local lock_timeout to '2s';").executeUpdate();
    
        Wallet wallet = em.find(Wallet.class, id);
    
        if (wallet != null) {
            em.lock(wallet, LockModeType.PESSIMISTIC_WRITE);
        }
    
        return wallet;
    }
    

    }

提交回复
热议问题