当前位置: 首页 > 知识库问答 >
问题:

无法在存储库方法上获取@Lock注释以使用OPTIMIZE\u FORCE\u INCREMENT

邹锦
2023-03-14

我试图在Spring Jpa存储库上使用@Lock注释,将锁定模式设置为OPTIMIZE\u FORCE\u INCREMENT,但版本没有增加。

使用EntityManager.find()并指定锁定模式的相同测试用例工作正常。使用PESSIMISTIC_FORCE_INCREMENT的相同测试用例也工作正常。

我想知道我是否遗漏了什么,或者我遇到了一些未记录的限制,或者只是一个bug。

我的环境:Windows 10 Spring Boot 2.1.3上的Java 11。根据Spring Boot BOM,使用相关Spring组件(如Spring Data Jpa)发布。Hibernate5.3.7。最终H2 1.4.199[但我与MariaDB进行了测试,得到了相同的结果]

实体:

@Entity
@Access(AccessType.FIELD)
public class Parent {
@Id private Long id;
@Version private Long version;
private String desc;
... getters, setters, hashcode and equals
}

存储库...

public interface ParentRepository extends JpaRepository<Parent, Long> {

  @Lock(LockModeType.OPTIMISTIC_FORCE_INCREMENT)
  Optional<Parent> findById(Long id);

  @Lock(LockModeType.OPTIMISTIC_FORCE_INCREMENT)
  @Query("select p from Parent p where p.id = ?1")
  Optional<Parent> optimisticFindById(Long id);

  @Lock(LockModeType.PESSIMISTIC_FORCE_INCREMENT)
  @Query("select p from Parent p where p.id = ?1")
  Optional<Parent> pessimisticFindById(Long id);

}

使用EntityManager.find()的测试用例,版本在检索实体时递增

public void forceIncrementWithEmTest() {

    // set the id
    Long id = 275L;

    // build the entity
    Parent p0 = new Parent(id, "Hello world.");

    assertThat(p0.getVersion(), is(nullValue()));

    // persist it 
    em.persist(p0);
    em.flush();

    // Version is 0L as expected
    assertThat(p0.getVersion(), is(0L));

    // clear the persistence context
    em.clear();

    // get the object again from DB, requiring the lock
    Parent p1 = em.find(
           Parent.class, 
           id, 
           LockModeType.PESSIMISTIC_FORCE_INCREMENT);
    assertThat(p1, notNullValue());

    // version has been incremented as expected
    assertThat(p1.getVersion(), is(1L));

    // flush and clear the context
    em.flush();
    em.clear();

    // get from DB without locks
    Parent p2 = em.find(Parent.class, id);
    assertThat(p2, notNullValue());

    // version has not been incremented this time, as expected
    assertThat(p2.getVersion(), is(1L));

  }

使用重新声明的findById存储库方法的测试用例版本没有像我预期的那样增加

  @Test
  @Transactional
  public void forceIncrementWithRepoQueryOptimisticTest() {

    // set the id
    Long id = 275L;

    // build the entity
    Parent p0 = new Parent(id, "Hello world.");

    assertThat(p0.getVersion(), is(nullValue()));

    // persist it 
    em.persist(p0);
    em.flush();

    // Version is 0 as expected
    assertThat(p0.getVersion(), is(0L));

    // clear the persistence context
    em.clear();

    // get the object again from DB, requiring the lock
    // this time using the repository with the @Lock annotation
    // and a custom query

    Optional<Parent> popt = parentRepo.optimisticFindById(id);
    assertThat(popt.isPresent(), is(true));
    Parent p1 = popt.get();

    // I expected version to have been incremented, but instead it is still 0L
    // so the @Lock annotation has had no effect
    assertThat(p1.getVersion(), is(0L));

    Parent p2 = parentRepo.saveAndFlush(p1);

    // also the saved entity still has version not incremented
    // as if the @Lock annotation was not considered.
    assertThat(p2.getVersion(), is(0L));


  }

我使用带有@Query注释的自定义方法获得相同的行为。

用PESSIMISTIC_FORCE_INCREMENT代替OPTIMISTIC_FORCE_INCREMENT,我得到了所有情况下的预期行为。

完整的设置和测试用例可在https://github.com/gpaglia/spring-lock-problem.git

共有1个答案

冯泓
2023-03-14

我回答了自己的问题,感谢Jens指出了我的疏忽,并澄清了预期的行为;也许这对其他人有用,因为文档在这一点上不是很清楚。

我现在明白PESSIMISTIC_FORCE_INCREMENT将在获取实体时强制递增,因此,在事务边界内,实体的版本已经递增。

另一方面,OPTIMIZE\u FORCE\u INCREMENT将仅在事务提交时增加版本,因此,在事务边界内,实体的版本尚未增加。

如果这对其他人有用,我更新了github上的测试用例。

 类似资料:
  • 问题内容: 有人可以给MWE 直接在方法上使用注释的方法吗? 我已经看到了无数的关于在类定义中使用它的示例-但还没有方法的示例。 引用文档: 这种添加到一个类定义或 一个方法 @Target(value = {TYPE, METHOD }) 因此,我认为还有一种可能性和预期的用途-但不幸的是,我无法弄清楚。 问题答案: 这里的DataSource类具有属性url,用户名,密码,driverClas

  • 我希望获得、和。以下是我现在所拥有的:

  • 使用laravel 7/livewire应用程序,我使用Repository制作crud,并获得了数据列表,在装载事件中,我分配了受保护的var$FacilityRepository,它在render方法中正常工作, 但在编辑方法中为空,我得到错误: 当用户单击“编辑链接”时 在模板中,编辑链接定义为: 为什么会出现错误以及如何修复? 修改#2: > 类设施扩展组件{...公共$FacilityR

  • 我正在我的应用程序中嵌入用于推送通知的GCM。我面临一个非常奇怪的问题,第一次运行时我无法获得GCM注册令牌,但当你第二次运行我的应用程序时,你将在控制台上打印注册ID。我不知道我在干什么。以下是我迄今为止所做的工作。 这是我的onCreate()方法,我想在其中打印GCM注册表: 执行以下代码: 通过以下方法将GCM_regId发送到服务器,如本教程所示: 我不这么认为,这里需要gcminent

  • 这个问题来自于,如果我有一个methodA和methodB在classA中,如下所示 但是,如果我在中确实有很多工作要做,那么spring会在整个执行时间内锁定表吗?

  • Eclipse for Testers版本:Indigo Release Build ID:20110615-0604 “无法读取http://beust.com/eclipse上得存储库.http://beust.com/eclipse不是有效得存储库位置.”