[英]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_READ
、PESSIMISTIC_WRITE
或PESSIMISTIC_FORCE_INCREMENT
。如果无法获得悲观锁,但锁定失败不会导致事务回滚,则抛出
LockTimeoutException
。可以使用 javax.persistence.lock.timeout 属性指定持久性提供者为获取数据库表上的锁定而应等待的时间长度(以毫秒为单位)。 如果获取锁所花费的时间超过该属性的值,则会抛出
LockTimeoutException
异常,但不会标记当前事务进行回滚。 如果此属性设置为 0,并且持久性提供程序无法立即获得锁,则它应该抛出LockTimeoutException
。如果在多个地方设置了
javax.persistence.lock.timeout
以下顺序确定该值:
EntityManager
或Query methods
之一的参数。@NamedQuery
注释中的设置。Persistence.createEntityManagerFactory
方法的参数。persistence.xml
部署描述符中的值。
从 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 悲观的@Lock
注释仅适用于(如您所指出的)查询。 我知道没有注释会影响整个事务。 您可以创建一个findByOnePessimistic
方法,该方法使用悲观锁调用findByOne
,或者您可以更改findByOne
以始终获取悲观锁。
如果您想实现自己的解决方案,您可能可以。 在@Lock
注释由LockModePopulatingMethodIntercceptor
处理,它执行以下操作:
TransactionSynchronizationManager.bindResource(method, lockMode == null ? NULL : lockMode);
你可以创建一些静态锁管理器,它有一个ThreadLocal<LockMode>
成员变量,然后在每个存储库中的每个方法周围都有一个方面,这些方法调用 bindResource 并在 ThreadLocal 中设置锁定模式。 这将允许您在每个线程的基础上设置锁定模式。 然后,您可以创建自己的@MethodLockMode
注释,该注释会将方法包装在一个方面,该方面在运行该方法之前设置线程特定的锁定模式,并在运行该方法后清除它。
实体对象可以通过 lock 方法显式锁定:
em.lock(employee, LockModeType.PESSIMISTIC_WRITE);
第一个参数是一个实体对象。 第二个参数是请求的锁定模式。
如果在调用 lock 时没有活动事务,则抛出TransactionRequiredException
因为显式锁定需要活动事务。
如果无法授予所请求的悲观锁,则会抛出LockTimeoutException
:
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);
您可以在 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.