如何在春季数据jpa查询中指定@lock超时?

2022-09-02 11:52:35

如何指定查询超时?我正在使用Oracle 11g,我希望我可以使用类似的东西。@Lock'select id from table where id = ?1 for update wait 5'

我定义了这样的方法:

@Lock(LockModeType.PESSIMISTIC_WRITE)
Stock findById(String id);

它似乎永远锁定。当我设置时,没有任何效果。javax.persistence.lock.timeout=0LocalContainerEntityManagerFactoryBean.jpaProperties


答案 1

要悲观地锁定实体,请将锁定模式设置为 、 或 。PESSIMISTIC_READPESSIMISTIC_WRITEPESSIMISTIC_FORCE_INCREMENT

如果无法获得悲观锁,但锁定失败不会导致事务回滚,则会引发 。LockTimeoutException

悲观锁定超时

可以使用 javax.persistence.lock.timeout 属性指定持久性提供程序在数据库表上获取锁定时应等待的时间长度(以毫秒为单位)。如果获取锁所需的时间超过此属性的值,则将引发 a,但当前事务不会标记为回滚。如果此属性设置为 0,则持久性提供程序在无法立即获取锁时应引发 LockTimeoutExceptionLockTimeoutException

如果在多个位置设置了 ,则该值将按以下顺序确定:javax.persistence.lock.timeout

  1. 或 之一的参数。EntityManagerQuery methods
  2. 批注中的设置。@NamedQuery
  3. 方法的参数。Persistence.createEntityManagerFactory
  4. 部署描述符中的值。persistence.xml

对于弹簧数据 1.6 或更高版本

@Lock从Spring Data JPA的1.6版本开始,CRUD方法就受支持了(事实上,已经有一个里程碑可用)。有关更多详细信息,请参阅此票证

使用该版本,您只需声明以下内容:

interface WidgetRepository extends Repository<Widget, Long> {

  @Lock(LockModeType.PESSIMISTIC_WRITE)
  Widget findOne(Long id);
}

这将导致后备存储库代理的 CRUD 实现部分将配置的 LockModeType 应用于 上的调用。find(…)EntityManager

另一方面

对于以前版本的 Spring Data 1.6

Spring Data 悲观注释仅适用于(如您所指出的)查询。据我所知,没有注释会影响整个事务。您可以创建一个使用悲观锁调用的方法,也可以更改为始终获取悲观锁。@LockfindByOnePessimisticfindByOnefindByOne

如果你想实现自己的解决方案,你可能可以。在引擎盖下处理注释,由其执行以下操作:@LockLockModePopulatingMethodIntercceptor

TransactionSynchronizationManager.bindResource(method, lockMode == null ? NULL : lockMode);

您可以创建一些具有成员变量的静态锁定管理器,然后将一个方面包裹在每个存储库中的每个方法上,这些方法调用bindResource,并在ThreadLocal中设置了锁定模式。这将允许您基于每个线程设置锁定模式。然后,您可以创建自己的注释,它将方法包装在一个方面中,该方面在运行方法之前设置特定于线程的锁定模式,并在运行方法后清除它。ThreadLocal<LockMode>@MethodLockMode

资源链接:

  1. 如何使用Spring Data JPA查找实体时启用LockModeType.PESSIMISTIC_WRITE?
  2. 如何将自定义方法添加到Spring Data JPA
  3. 春季数据悲观锁定超时与Postgres
  4. JPA Query API

悲观锁定超时的各种示例

设置悲观锁定

实体对象可以通过 lock 方法显式锁定:

em.lock(employee, LockModeType.PESSIMISTIC_WRITE);

第一个参数是实体对象。第二个参数是请求的锁定模式。

如果在调用 lock 时没有活动事务,则会引发 A,因为显式锁定需要活动事务。TransactionRequiredException

如果无法授予请求的悲观锁,则抛出 A:LockTimeoutException

  • 如果另一个用户(由另一个 EntityManager 实例表示)当前持有该数据库对象上的锁定,则锁定请求将失败。PESSIMISTIC_READPESSIMISTIC_WRITE
  • 如果另一个用户当前持有该数据库对象上的锁或锁,则锁定请求将失败。PESSIMISTIC_WRITEPESSIMISTIC_WRITEPESSIMISTIC_READ

设置查询提示(作用域)

可以在以下作用域(从全局到本地)中设置查询提示:

对于整个持久性单元 - 使用属性:persistence.xml

<properties>
   <property name="javax.persistence.query.timeout" value="3000"/>
</properties>

对于 EntityManagerFactory - 使用以下方法:createEntityManagerFacotory

Map<String,Object> properties = new HashMap();
properties.put("javax.persistence.query.timeout", 4000);
EntityManagerFactory emf =
  Persistence.createEntityManagerFactory("pu", properties);

对于实体管理器 - 使用以下方法:createEntityManager

Map<String,Object> properties = new HashMap();
properties.put("javax.persistence.query.timeout", 5000);
EntityManager em = emf.createEntityManager(properties);

或使用 setProperty 方法:

em.setProperty("javax.persistence.query.timeout", 6000);

对于定义 - 使用元素:named queryhints

@NamedQuery(name="Country.findAll", query="SELECT c FROM Country c",
    hints={@QueryHint(name="javax.persistence.query.timeout", value="7000")})

对于特定的查询执行 - 使用方法(在查询执行之前):setHint

query.setHint("javax.persistence.query.timeout", 8000);

资源链接:

  1. 锁定在 JPA 中
  2. 悲观锁定超时

答案 2

您可以在春季数据中使用@QueryHints

@Lock(LockModeType.PESSIMISTIC_WRITE)
@QueryHints({@QueryHint(name = "javax.persistence.lock.timeout", value ="5000")})
Stock findById(String id)

推荐