繁体   English   中英

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

[英]How to specify @lock timeout in spring data jpa query?

如何为查询指定@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时,没有效果。

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

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

悲观锁定超时

可以使用 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 (事实上​​,已经有一个里程碑可用)。 有关更多详细信息,请参阅此票证

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

interface WidgetRepository extends Repository<Widget, Long> {

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

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

另一方面,

对于之前版本的 Spring Data 1.6

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

如果您想实现自己的解决方案,您可能可以。 @Lock注释由LockModePopulatingMethodIntercceptor处理,它执行以下操作:

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

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

资源链接:

  1. 使用 Spring Data JPA 查找实体时如何启用 LockModeType.PESSIMISTIC_WRITE?
  2. 如何将自定义方法添加到 Spring Data JPA
  3. 使用 Postgres 的 Spring Data 悲观锁超时
  4. JPA 查询 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. 锁定 JPA
  2. 悲观锁超时

您可以在 Spring Data 中使用@QueryHints

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

对于 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);

当如下提供时,javax.persistence.lock.timeout 似乎也不适用于我:

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

但后来我尝试了其他有效的方法。 现在我正在使用实体管理器配置我的 hbernate,而不是使用 @Repository 和 CrudRepository。 使用 createQuery 以及锁定和设置锁定超时。 并且此配置按预期工作。 我有两个并行运行的事务,并试图在数据库中锁定完全相同的行。 第一个事务能够获取 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);
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM