Oracle JPA悲观锁不工作
我正在使用SpringBoot、JPA、Oracle12C和下面的键入查询来选择要处理的“新”项目。一旦我选择了一个“新”项目,我会更新它的状态,这样它就不再有资格被选择了,但是我看到了一个相同项目被选中的并发问题 我读到我需要在查询上设置一个“LockModeType.悲观_WRITE”,以防止其他线程选择同一行,但它似乎不起作用 我是否遗漏了下面的内容,或者我是否需要另一种配置来防止并发线程从表中检索相同的行?问题是与锁定级别有关还是与实体管理器未得到更新/刷新有关 我的Oracle JPA悲观锁不工作,oracle,hibernate,jpa,transactions,locking,Oracle,Hibernate,Jpa,Transactions,Locking,我正在使用SpringBoot、JPA、Oracle12C和下面的键入查询来选择要处理的“新”项目。一旦我选择了一个“新”项目,我会更新它的状态,这样它就不再有资格被选择了,但是我看到了一个相同项目被选中的并发问题 我读到我需要在查询上设置一个“LockModeType.悲观_WRITE”,以防止其他线程选择同一行,但它似乎不起作用 我是否遗漏了下面的内容,或者我是否需要另一种配置来防止并发线程从表中检索相同的行?问题是与锁定级别有关还是与实体管理器未得到更新/刷新有关 我的@事务性服务: @O
@事务性服务:
@Override
@Transactional(isolation = Isolation.READ_COMMITTED, rollbackFor=RuntimeException.class)
public MyObject retrieveItemByStatus(StatusEnum status) {
return myRepository.retrieveItemByStatus(status);
}
“我的存储库”层中的查询:
@Override
public MyObject retrieveItemByStatus(StatusEnum status) {
String sql = "SELECT t FROM myTable t WHERE status = :status ORDER BY id ASC";
try {
TypedQuery<MyObject> query = em.createQuery(sql, MyObject.class).setParameter("status", status);
query.setLockMode(LockModeType.PESSIMISTIC_WRITE);
query.setFirstResult(0);
query.setMaxResults(1);
MyObject myObject = (MyObject) query.getSingleResult();
if (myObject != null) {
myObject.setStatus(StatusEnum.IN_PROGRESS);
MyObject myUpdatedObject = em.merge(myObject);
return myUpdatedObject;
}
} catch (IllegalArgumentException iae) {
//some logging
} catch(NoResultException nrf) {
//some logging
} catch(Exception ex) {
//some logging
}
return null;
}
@覆盖
公共MyObject retrieveItemByStatus(状态枚举状态){
String sql=“从myTable t中选择t,其中状态=:按id ASC排序的状态”;
试一试{
TypedQuery query=em.createQuery(sql,MyObject.class).setParameter(“状态”,status);
setLockMode(LockModeType.悲观_WRITE);
query.setFirstResult(0);
query.setMaxResults(1);
MyObject MyObject=(MyObject)query.getSingleResult();
if(myObject!=null){
myObject.setStatus(状态枚举进行中);
MyObject MyUpdateObject=em.merge(MyObject);
返回MyUpdateObject;
}
}捕获(IllegalArgumentException iae){
//一些伐木
}捕获(NoResultException nrf){
//一些伐木
}捕获(例外情况除外){
//一些伐木
}
返回null;
}
如果您无法阻止oracle中的“读取”。。。悲观锁对应于不阻止其他select
语句的select for update
。。。它只强制它读取数据的旧版本(在选择更新之前)
运行)。。。它将只阻止其他select for update
语句(因此其他查询具有悲观锁)我可以确认这一观察结果。我用H2数据库测试了几种锁模式,所有这些都按预期工作。与Oracle数据库结合使用时,两种悲观锁模式都不能正常工作。因此,问题是:这个代码有什么问题
对于Oracle,其中两个并发代码执行会产生相同的数据,尽管第一个会阻止第二个:
// Every Thread gets its own Hibernate session:
final Session session = HibernateSessionHolder.get();
session.getTransaction().begin();
final List<EventDeliveryDataDB> eddList =
session.createCriteria(EventDeliveryDataDB.class)
.setLockMode(LockMode.PESSIMISTIC_WRITE) // with READ the same
.add(eq("progress", NEW))
.list();
eddList.stream().forEach(eddElem -> eddElem.setProgress(IN_PROGRESS));
session.getTransaction().commit();
感谢您的回复,更新我的查询要使用什么解决方案select for update
。。。您现在谈论的是另一个能够读取数据的查询,但您不希望它这样做。。。对吗?如果是这样的话。。我认为这取决于您的业务和实施。。。它是。需要锁定它吗?多久打一次电话?这将如何影响性能?有没有死锁的机会?是否允许业务智能阻止此查询?我指的是另一个线程试图使用上述查询同时更新同一行。我在云环境中运行应用程序,当我将状态从NEW
更新为in_progress
时,需要锁定行,这样其他线程就不能同时读/写同一行。附加说明:我希望第二次调用在尝试执行“选择更新和更新”并提交它们。这是H2数据库的行为示例。有关于此问题的更新吗?您是否能够解决此问题或解决此问题?
Hibernate: select this_.DD_ID as DD_ID1_2_0_, this_.CHANNEL_NAME as CHANNEL_NAME2_2_0_, this_.created as created3_2_0_, this_.DELIVERY_TIME as DELIVERY_TIME4_2_0_, this_.ERROR_CODE as ERROR_CODE5_2_0_, this_.ERROR_MESSAGE as ERROR_MESSAGE6_2_0_, this_.EVENT_ID as EVENT_ID7_2_0_, this_.MAX_RETRIES as MAX_RETRIES8_2_0_, this_.PROGRESS as PROGRESS9_2_0_, this_.PROGRESS_ID as PROGRESS_ID10_2_0_, this_.RECIPIENT_CRID as RECIPIENT_CRID11_2_0_, this_.RETRY_COUNTER as RETRY_COUNTER12_2_0_, this_.RETRY_TIME as RETRY_TIME13_2_0_, this_.updated as updated14_2_0_ from HR.NOS_DELIVERY_DATA this_ where this_.PROGRESS=?
Hibernate: select this_.DD_ID as DD_ID1_2_0_, this_.CHANNEL_NAME as CHANNEL_NAME2_2_0_, this_.created as created3_2_0_, this_.DELIVERY_TIME as DELIVERY_TIME4_2_0_, this_.ERROR_CODE as ERROR_CODE5_2_0_, this_.ERROR_MESSAGE as ERROR_MESSAGE6_2_0_, this_.EVENT_ID as EVENT_ID7_2_0_, this_.MAX_RETRIES as MAX_RETRIES8_2_0_, this_.PROGRESS as PROGRESS9_2_0_, this_.PROGRESS_ID as PROGRESS_ID10_2_0_, this_.RECIPIENT_CRID as RECIPIENT_CRID11_2_0_, this_.RETRY_COUNTER as RETRY_COUNTER12_2_0_, this_.RETRY_TIME as RETRY_TIME13_2_0_, this_.updated as updated14_2_0_ from HR.NOS_DELIVERY_DATA this_ where this_.PROGRESS=?
Hibernate: select DD_ID from HR.NOS_DELIVERY_DATA where DD_ID =? for update
Hibernate: select DD_ID from HR.NOS_DELIVERY_DATA where DD_ID =? for update
Hibernate: update HR.NOS_DELIVERY_DATA set CHANNEL_NAME=?, created=?, DELIVERY_TIME=?, ERROR_CODE=?, ERROR_MESSAGE=?, EVENT_ID=?, MAX_RETRIES=?, PROGRESS=?, PROGRESS_ID=?, RECIPIENT_CRID=?, RETRY_COUNTER=?, RETRY_TIME=?, updated=? where DD_ID=?
Hibernate: update HR.NOS_DELIVERY_DATA set CHANNEL_NAME=?, created=?, DELIVERY_TIME=?, ERROR_CODE=?, ERROR_MESSAGE=?, EVENT_ID=?, MAX_RETRIES=?, PROGRESS=?, PROGRESS_ID=?, RECIPIENT_CRID=?, RETRY_COUNTER=?, RETRY_TIME=?, updated=? where DD_ID=?