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()
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;
}
}