java - 使用 spring + JPA 在乐观锁定中处理 LockModeType

标签 java spring jpa transactions

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

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

代码示例如下:

我正在使用以下方式注入(inject)持久性上下文:

@PersistenceContext
private EntityManager entityManager;

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

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

1 处的异常:java.lang.IllegalArgumentException:实体不在持久性上下文中

第二种方法:管理事务

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

2 处的异常:不允许在共享 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 来定义 transactionManagerorg.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean 用于定义 entityManagerFactory

提前致谢!

最佳答案

锁定 JPA 实体的条件:

  1. 您必须处于交易环境中
  2. 实体必须处于托管状态(即在持久性上下文中处于 Activity 状态。

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

您可以更早地进行 merge()。

要点:

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

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

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

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

=:-)

关于java - 使用 spring + JPA 在乐观锁定中处理 LockModeType,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12535463/

相关文章:

java - 如何在android中读取txt文件?

java - 带有 JMS 2.0 的 Spring 4 CachingConnectionFactory 不能正确缓存生产者

java - EntityManager.createStoredProcedureQuery() 未解析

java - 在 CriteriaBuilder 查询的 groupBy 子句中包含实体

java - 如何以编程方式将 "@Cascade"添加到 POJO 字段(Spring mvc + Hibernate)

java - writeObject() 如何访问 serialPersistantFields

java - 如何让 Java 显示日期中的日期?

java - 如何用Java写一个自更新程序?

java - spring_security_last_用户名

java - 如何使用 Spring RestTemplate 发送 XML POST 请求?