【问题标题】:Handling LockModeType in Optimistic locking with spring + JPA使用spring + JPA处理乐观锁定中的LockModeType
【发布时间】:2012-09-21 17:39:50
【问题描述】:

我已经在 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

提前致谢!

【问题讨论】:

  • 你到底想达到什么目的?
  • 我正在尝试通过定义 lockModeType 来实现乐观锁定。如果我不介绍这行 entityManager.lock(userProfile, LockModeType.OPTIMISTIC);在我的第一种方法中,一切都执行得很好并且版本增加了。但是它不提供覆盖spring默认的LockModeType的功能。希望有帮助。谢谢!
  • 实体管理器工厂附加的持久单元中是否缺少实体?它应该在META-INT/persistence.xml
  • 没有 Abhinav,持久性单元中没有缺少实体,我正在使用 entityManagerFactory 的 packagesToScan 属性为实体提供基础包。

标签: java spring jpa transactions


【解决方案1】:

锁定 JPA 实体的条件:

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

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

你可以早点合并()。

要点:

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

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

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

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

=:-)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-08-11
    • 2017-10-02
    • 2014-03-07
    • 2016-09-23
    • 1970-01-01
    • 2012-11-14
    • 2021-09-27
    • 1970-01-01
    相关资源
    最近更新 更多