Java 锁定相互事务执行

Java 锁定相互事务执行,java,hibernate,spring,jpa,transactions,Java,Hibernate,Spring,Jpa,Transactions,我想用下面的例子来描述这个问题: 假设我们正在开发一个应用程序(基于Hibernate JPA和Spring),该应用程序允许其用户执行两种事务性操作,这两种操作必须以可序列化模式为每个用户执行。只要应用程序必须同时具有健壮性和良好的性能,我们就可以在ReentrantLock周围使用某种包装器进行同步: void operation1(user) { userLockWrapper.lock(user.id); //gets ReentrantLock for specified id

我想用下面的例子来描述这个问题:

假设我们正在开发一个应用程序(基于Hibernate JPA和Spring),该应用程序允许其用户执行两种事务性操作,这两种操作必须以可序列化模式为每个用户执行。只要应用程序必须同时具有健壮性和良好的性能,我们就可以在ReentrantLock周围使用某种包装器进行同步:

void operation1(user) {
    userLockWrapper.lock(user.id); //gets ReentrantLock for specified id from internal map and locks it
    try {   
        //start transaction 
        //perform operation
        //commit transaction    
    } finally {
        userLockWrapper.unlock(user.id).
    }   
}
void operation2(user) {
    userLockWrapper.lock(user.id); //same wrapper instance as in operation1
    //same logic as in operation1   
}
现在,让我们假设为了提高可伸缩性,我们将系统划分为两个独立模块,它们运行在不同的虚拟机上,使用相同的数据库,其中第一个模块负责操作1,第二个模块负责操作2。首先想到的决定是使用悲观锁,例如:

@Transactional
void operation1(user) { //same goes for operation2
    //firing extra select for update sql statement here
    entityManager.find(User.class, user.id, LockModeType.PESSIMISTIC_WRITE) 
    //performing operation      
}

我想知道是否有更可取的解决办法。希望从社区得到一些建议。

是的。使用乐观锁定。您只需将@Version注释的持久字段添加到实体中,并了解其工作原理。阅读

每次Hibernate将实体写入数据库时,它都会检查存储的版本是否仍然与内存中的版本相同,并递增此版本。如果版本不匹配,它将抛出一个使事务回滚的命令


这将允许最佳吞吐量,也是唯一可以跨两个事务使用的系统:第一个事务加载对象,用户修改对象(可能需要几分钟),第二个事务合并对象。

实际上,我考虑过使用乐观锁定,但在这种情况下似乎不合适,因为应用程序不仅必须对非陈旧数据进行操作,而且还必须提供可以实现的最佳健壮性,所以回滚第二个事务并取消操作不是一个选项。但是现在我认为,当锁定失败时,通过自动重试操作可以解决这个问题。谢谢,我会深入研究的。