在事务中使用悲观锁不能阻止记录被更新?

Posted

技术标签:

【中文标题】在事务中使用悲观锁不能阻止记录被更新?【英文标题】:Using pessimistic lock in transaction can not prevent record from being updated? 【发布时间】:2019-02-23 10:17:18 【问题描述】:

我正在使用带有 TransactionTemplate 和悲观锁的 Spring Boot JPA,我试图防止记录在事务期间被其他线程更新。代码如下:

    transactionService.executeTransaction(() -> 
        Map<String, Object> properties = new HashMap<>();
        properties.put("javax.persistence.lock.scope", PessimisticLockScope.EXTENDED);
        System.out.println("--------------------- start find with lock, job id: " + entityId);
        Entity newEntity = (Entity) em.find(entityClass, entityId, LockModeType.PESSIMISTIC_WRITE, properties);
        System.out.println("+++++++++++++++++++++ end find with lock, job id: " + entityId);
        try
            Thread.sleep(2000);
        catch (InterruptedException e)

        
        newEntity = setEntity.setEntity(newEntity);
        System.out.println(Thread.currentThread().getName() + ": use transaction to save: " + newEntity.getId());
    );

还有一个带有 while 循环的线程,并试图更新记录。如下所示:

while(true)
    Entity entity = entity.findOne(id);
    repository.save(entity);
    System.out.println("save success: " + id)

但是我发现悲观锁根本不起作用,在“start find with lock”和“end find with lock”之间的打印过程中,以及2秒的睡眠,我发现实体总是可以被保存另一个线程。我对悲观锁有什么误解吗?

编辑 1: 我已经调试并看到了 sql。

em.find(entityClass, entityId, LockModeType.PESSIMISTIC_WRITE, properties);

已经生成了两个sql,一个没有连接到任何其他表,但是有“for update”

SELECT * FROM ... WHERE ID=? FOR UPDATE

另一个是,加入但没有“更新”

SLECT * FROM ... left outer join ... left outer join ... where id=? 

希望这会有所帮助。

【问题讨论】:

悲观锁应该根据 JPA 规范创建一个类似“SELECT ... FOR UPDATE”的 SQL 语句。也许您应该通过查看您的 JPA 提供程序日志以了解他们发送的 SQL 来调试您的情况。 @BillyFrost 添加 sql,有一个带有“for update”的 sql 【参考方案1】:

是的,如果您使用 Hibernate 作为您的 JPA 实现,那么它还不受支持。所以锁不会扩展到连接

按照 JPA 标准的规定,尚不支持 javax.persistence.lock.scope。

来自Hibernate Docs

Related Ticket

【讨论】:

以上是关于在事务中使用悲观锁不能阻止记录被更新?的主要内容,如果未能解决你的问题,请参考以下文章

乐观锁与悲观锁

乐观锁和悲观锁

乐观锁和悲观锁配合事务的应用

day18 12.丢失更新介绍与悲观锁

乐观锁与悲观锁

悲观锁,乐观锁