java 如何在spring data jpa查询中指定@lock timeout?

idfiyjo8  于 2023-04-04  发布在  Java
关注(0)|答案(5)|浏览(252)

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

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

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

00jrzges

00jrzges1#

要悲观地锁定实体,请将锁定模式设置为PESSIMISTIC_READPESSIMISTIC_WRITEPESSIMISTIC_FORCE_INCREMENT
如果不能获得悲观锁,但锁定失败没有导致事务回滚,则抛出LockTimeoutException
Pessimistic Locking Timeouts
持久性提供者应该等待以获得数据库表上的锁的时间长度(以毫秒为单位)可以使用javax.persistence.lock.timeout属性来指定。如果获得锁所花费的时间超过该属性的值,则将抛出LockTimeoutException,但当前事务将不会被标记为回滚。如果该属性设置为0,如果持久性提供程序不能立即获得锁,它应该抛出LockTimeoutException
如果javax.persistence.lock.timeout在多个位置设置,则值将按以下顺序确定:

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

Spring Data 1.6以上版本

从Spring Data JPA版本1.6开始,CRUD方法支持@Lock(事实上,已经有一个milestone可用)。有关更多详细信息,请参阅ticket
使用该版本,只需声明以下内容:

interface WidgetRepository extends Repository<Widget, Long> {

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

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

Spring Data 1.6之前版本

Spring Data pessimistic @Lock annotations仅适用于查询(正如您指出的那样)。我不知道有任何注解可以影响整个事务。您可以创建一个findByOnePessimistic方法,该方法使用pessimistic lock调用findByOne,或者您可以更改findByOne以始终获得pessimistic lock。
如果你想实现自己的解决方案,你可能可以。在后台,@Lock注解由LockModePopulatingMethodIntercceptor处理,它执行以下操作:

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

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

资源链接:

  1. How to enable LockModeType.PESSIMISTIC_WRITE when looking up entities with Spring Data JPA?
  2. How to add custom method to Spring Data JPA
  3. Spring Data Pessimistic Lock timeout with Postgres
  4. JPA Query API

各种悲观锁超时示例

设置悲观锁

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

em.lock(employee, LockModeType.PESSIMISTIC_WRITE);

第一个参数是实体对象,第二个参数是请求的锁模式。
如果调用lock时没有活动事务,则抛出TransactionRequiredException,因为显式锁定需要活动事务。
如果请求的悲观锁不能被授予,则抛出LockTimeoutException

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

设置查询提示(作用域)

查询提示可以在以下范围内设置(从全局到局部):

对于整个持久性单元-使用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);

对于EntityManager -使用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 query定义-使用hints元素:

@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. Locking in JPA
    1.悲观锁定超时
q8l4jmvw

q8l4jmvw2#

您可以在Spring Data中使用@QueryHints

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

57hvy0tb3#

对于Spring Data 1.6或更高版本,我们可以使用spring data jpa提供的@Lock注解。
此外,也可以使用@QueryHints设置锁定超时。最初,在默认的CRUD方法中不支持查询提示注解,但在修复1.6M1后可用。https://jira.spring.io/browse/DATAJPA-173
下面是具有PESSIMISTIC_WRITE模式类型的悲观锁的示例,该模式类型是排他锁。

@Lock(LockModeType.PESSIMISTIC_WRITE) 
@QueryHints({@QueryHint(name = "javax.persistence.lock.timeout", value   ="5000")}) 
Customer findByCustomerId(Long customerId);
wgeznvg7

wgeznvg74#

javax.persistence.lock.timeout似乎对我也不起作用,当提供如下:

@QueryHints({@QueryHint(name = "javax.persistence.lock.timeout",value = "15000")})

但后来我尝试了其他有效的方法,而不是使用@Repository和CrudRepository,现在我正在使用实体管理器配置我的hbernate。使用createQuery沿着lock和设置锁定超时。这个配置按预期工作。我有两个事务在parellel中运行,并试图锁定数据库中的同一行。第一个事务能够获取WRITE锁并在释放锁之前保持锁大约10秒。同时,第二个事务尝试获取同一行上的锁,但由于javax.persistence.lock.timeout设置为15秒,因此,它等待锁被释放,然后获取自己的锁。因此使流序列化。

@Component
public class Repository {

    @PersistenceContext
    private EntityManager em;

    public Optional<Cache> getById(int id){
        List<Cache> list = em.createQuery("select c from Cache c where c.id = ?1")
                            .setParameter(1, id)
                            .setHint("javax.persistence.lock.timeout", 15000)
                            .setLockMode(LockModeType.PESSIMISTIC_WRITE)
                            .getResultList();

    
        return Optional.ofNullable(list.get(0));
    }
    
    public void save(Cache cache) {
        cache = em.find(Cache.class, cache.getId());
        em.merge(cache);
    }
}
atmip9wb

atmip9wb5#

不幸的是,javax.persistence.lock.timeout属性仅由spring data jpa中的oracle提供程序支持。要设置适当的锁定超时,您必须为每个DBMS提供程序执行一些自定义的本地SQL语句。
下面是一些特定于DBMS的语句,用于将锁定超时设置为3秒:

MySQL

set session innodb_lock_wait_timeout = 3

PostgreSQL

set local lock_timeout = 3000

SQLServer

set lock_timeout 3000
有关该主题的更多信息,请参阅以下帖子:https://blog.mimacom.com/handling-pessimistic-locking-jpa-oracle-mysql-postgresql-derbi-h2/

相关问题