【问题标题】:Why according to JPA are Update/Delete operations are not allowed to set lock?为什么根据 JPA 不允许更新/删除操作设置锁?
【发布时间】:2018-04-02 04:47:24
【问题描述】:

我正在使用带有 Spring Data JPA 的 Hibernate 5.2.15。

假设我需要实现投票系统 - 所以用户只需点击“投票”按钮,数据库中的“投票”列现在是 +1。

我已经实现了下一个方法:

public interface CrudRestRepository extends Repository<Restaurant,Integer> {

@Modifying
@Transactional
@Query("UPDATE Restaurant r SET r.votes=r.votes+1 where r.id=:restId ")
@Lock(LockModeType.PESSIMISTIC_WRITE)
void addVoteToRestaurant(@Param("restId")int restId);
}

但是当我尝试使用这种方法时,我得到了下一个异常:

IllegalStateException:非法尝试在非 SELECT 查询上设置锁定模式

所以,我用谷歌搜索了它,似乎不允许对非选择查询设置锁定 - https://docs.oracle.com/javaee/6/api/javax/persistence/Query.html#setLockMode(javax.persistence.LockModeType)

我的问题是:

1) 为什么会这样?我是数据库的新手,但我认为尽管查询类型我可以使用锁定。

2) 如果我需要为一列的大量更新提供并发性,比如百万用户,我该怎么办?从使用Hibernate的角度来看。

非常感谢!

【问题讨论】:

    标签: java database hibernate jpa spring-data-jpa


    【解决方案1】:

    我认为你必须采取不同的方法。首先锁定要更新的记录列表。一旦它们被锁定,其他事务就无法更新您锁定的那些记录。以下是取自 AxonFramework 源代码的示例。它首先锁定令牌(记录),然后在锁定的记录上持续存在。

    TokenEntry token = entityManager.find(TokenEntry.class, new TokenEntry.PK(processorName, segment), LockModeType.PESSIMISTIC_WRITE);
    if (token == null) {
        token = new TokenEntry(processorName, segment, null, serializer);
        token.claim(nodeId, claimTimeout);
        entityManager.persist(token);
        // hibernate complains about updates in different transactions if this isn't flushed
        entityManager.flush();
    

    另一种具有显式事务调用的方法。示例取自 LogicBig.com

      EntityManager em = entityManagerFactory.createEntityManager();
      em.getTransaction().begin();
      log("write thread before acquiring lock");
      Article article = em.find(Article.class, 1L, LockModeType.PESSIMISTIC_WRITE);
      log("write thread after acquiring lock");
      article.setContent("updated content .. ");
      log("committing in write thread.");
      em.getTransaction().commit();
      em.close();
      log("Article updated", article);
    

    为了您的 +1 操作。

    Restaurant res = entityManager.find(Restaurant.class, restId,LockModeType.PESSIMISTIC_WRITE);
    
       if(res.getVote()>-1){
         res.setVote(res.getVote()+1); // assumed vote is int
       }
       entityManager.persist(res);
       entityManager.flush();
    

    【讨论】:

    • 感谢您的反馈,Zaw Htut!但是,如果我需要对一个阻塞查询执行 +1 操作怎么办?
    猜你喜欢
    • 2011-07-04
    • 2018-10-29
    • 2018-01-24
    • 1970-01-01
    • 2012-08-12
    • 2016-07-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多