2

我已经搜索了stackoverflow中的帖子,我希望这不是重复的。

我第一次尝试乐观锁定,我可以使用 spring 管理的 LockModeType 来做到这一点,但无法自己定义 LockMode

以下是代码示例:

我正在使用以下方法注入持久性上下文:

@PersistenceContext
private EntityManager entityManager;

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

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

1 处的例外情况:java.lang.IllegalArgumentException: entity not in the persistence context

第二种方法:管理事务

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

2 处的例外情况:Not allowed to create transaction on shared EntityManager - use Spring transactions or EJB CMT instead

第三种方法:由于共享 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 处的例外情况:entity not in the persistence context

在我的应用程序上下文中,我org.springframework.orm.jpa.JpaTransactionManager用于定义transactionManagerorg.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean定义entityManagerFactory

提前致谢!

4

1 回答 1

7

锁定 JPA 实体的条件:

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

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

您可以更早地合并()。

要点:

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

  • 如果您有更复杂的处理并且需要跨多个逻辑相关的实体实例执行读取或写入,作为一致的连贯原子操作,并且所有其他写入在持续时间内被阻止/隔离 - 那么您需要设置自己的锁定模式,因为自动的单个实体锁不起作用。您需要仔细设计以连贯方式读取或写入的实体集,并手动设计和实施您自己的手动锁定解决方案 - 可能会选择关系层次结构中的最顶层实体来记录所有“全局”锁定相关的“子”实体(利用其@Version 属性)。

  • 任何手动锁定解决方案都需要所有数据库写入逻辑来兑现锁定。这意味着在相同实体上运行的其他事务必须了解您的锁定设计,并且实际上必须在写入之前尝试取出您的锁定。一个不会导致各种写入阻塞和序列化的锁实际上根本就不是锁。

  • 对于乐观锁定,取出锁的确切时间是灵活的。在提交和刷新操作发生之前,不会检查锁并将其写入数据库。因此,您可以从 tx 开始到提交的任何时间取出锁。对于悲观锁定,情况正好相反——你必须保护代码的关键区域,就好像你的生命依赖于它一样。通常悲观的 lockMode 应该设置为事务开始的 em.find() 或 em.query 的一部分 - 或者如果因为托管对象已经在内存中而无法做到这一点,那么你应该执行 em.flush() 和em.refresh(PESSIMISTIC_WRITE)

=:-)

于 2012-10-15T07:51:45.393 回答