使用Spring + JPA进行乐观锁处理LockModeType

3

我在stackoverflow上搜索过,希望这不是重复的问题。

我第一次尝试乐观锁定,使用spring管理的LockModeType可以实现,但是我无法自己定义LockMode。

以下是代码示例:

我正在使用注入的持久化上下文:

@PersistenceContext
private EntityManager entityManager;

第一种方法:使用注释式事务

@Transactional
    public void updateUserProfile(UserProfile userProfile) {
        entityManager.lock(userProfile, LockModeType.OPTIMISTIC); // 1*
        entityManager.merge(userProfile);
    }

第一个异常: java.lang.IllegalArgumentException: 实体不在持久化上下文中

第二个方法:管理事务

public void updateUserProfile(UserProfile userProfile) {
        entityManager.getTransaction().begin(); // 2*
        entityManager.lock(userProfile, LockModeType.OPTIMISTIC); 
        entityManager.merge(userProfile);
        entityManager.getTransaction().commit();
    }

第二个异常: 不允许在共享EntityManager上创建事务 - 请改用Spring事务或EJB CMT

第三种方法:由于我遇到了使用共享EntityManager的异常,因此我尝试从entityManagerFactory创建EntityManager。

@Transactional
public void updateUserProfile(UserProfile userProfile) {
        EntityManager em = entityManager.getEntityManagerFactory().createEntityManager();
        em.getTransaction().begin();
        em.lock(userProfile, LockModeType.OPTIMISTIC);  // 3*
        em.merge(userProfile);
        em.getTransaction().commit();
    }

第3个异常:实体不在持久化上下文中

在我的应用程序上下文中,我使用org.springframework.orm.jpa.JpaTransactionManager来定义transactionManager,并使用org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean来定义entityManagerFactory

提前感谢!


你究竟想要实现什么? - axtavt
我正在尝试通过定义lockModeType来实现乐观锁定。如果我在第一次尝试中不引入entityManager.lock(userProfile, LockModeType.OPTIMISTIC)这一行,那么一切都会正常执行并且版本号也会增加。但是它不能提供覆盖spring默认LockModeType的功能。希望这有所帮助。谢谢! - Anshu
实体是否缺失于与实体管理器工厂附加的持久单元中?它应该在 META-INT/persistence.xml 中。 - Abhinav Sarkar
不,Abhinav,实体并没有从持久化单元中丢失,我正在使用entityManagerFactory的packagesToScan属性来提供实体的基本包。 - Anshu
1个回答

7

JPA实体锁定的条件:

  1. 必须处于事务上下文中
  2. 实体必须处于托管状态(即在持久性上下文中处于活动状态)。

您似乎违反了(2) - 尝试锁定已分离的实体。

你可以提前执行merge()操作。

重要提示:

  • 如果您修改一个实体并运行事务,并且在实体中有一个带有@Version注释的版本属性,则会自动以实体的粒度进行乐观锁定。版本属性将被更新,如果它尚未在DB中更改,则写入成功。这是常见的简单锁定情况,其中所有对单个实体的写入都是串行化的,以避免损坏。如果每个实体/记录可以独立处理,则无需设置任何LockMode,因为这是默认行为。仔细查看此选项-我怀疑这符合您的简单要求。

  • 如果您有更复杂的处理,并且需要跨多个逻辑相关实体实例进行读取或写入作为一致的连贯原子操作,并阻止/隔离所有其他写入以获取持续时间-则需要设置自己的锁定模式,因为自动化的单独每个实体锁定将无法正常工作。您需要仔细设计一组以协调方式读取或编写的实体,并手动设计和实现自己的手动锁定解决方案-可能选择一个关系层次结构中的最顶层实体,以记录所有相关“子”实体的“全局”锁(利用其@Version属性)。

  • 任何手动锁定解决方案都需要所有DB写入逻辑遵守锁定。这意味着操作相同实体的其他事务必须了解您的锁定设计,并且实际上必须在编写之前尝试获取您的锁。不会导致各种写入阻塞和串行化的锁定实际上根本不是锁定。

  • 对于乐观锁定,获取锁的确切时间是灵活的。在提交和刷新操作发生之前,不会检查锁定并将其写入数据库。因此,您可以在tx开始时随时获取锁。对于悲观锁定,则相反-您必须像生命取决于它一样保护关键区域的代码。通常,应将悲观lockMode设置为em.find()或em.query的一部分,该查询启动事务-或者如果这不可能因为管理的对象已经在内存中,则应执行em.flush()和em.refresh(PESSIMISTIC_WRITE)

=:-)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接