当前位置: 首页 > 知识库问答 >
问题:

为什么我在Oracle的JPA中悲观的锁定不起作用

冷善
2023-03-14

我试图为运行在不同JBoss节点上的cron作业实现某种信号量。我尝试使用数据库(Oracle 11g)作为锁定机制,使用一个表来同步不同节点中的cron作业。表格很简单:

CREATE TABLE SYNCHRONIZED_CRON_JOB_TASK
(
   ID            NUMBER(10)           NOT NULL,
   CRONJOBTYPE   VARCHAR2(255 Byte),
   CREATIONDATE  TIMESTAMP(6)         NOT NULL,
   RUNNING       NUMBER(1)
);

ALTER TABLE SYNCHRONIZED_CRON_JOB_TASK
   ADD CONSTRAINT PK_SYNCHRONIZED_CRON_JOB_TASK
   PRIMARY KEY (ID); 

因此,当一个作业启动时,它会在表中搜索它的cronjobtype条目,并检查它是否已经在运行。如果不是,它将条目设置运行标志更新为真。第一个选择是使用Hibernate和悲观锁通过JPA CriteriaApi做出的。

query.setLockMode(javax.persistence.LockModeType.PESSIMISTIC_WRITE);

所有这些操作都是在一次交易中完成的。

当一个进程运行时,它进行的查询如下:

[Server:server-two] 10:38:00,049 INFO  [stdout] (scheduler-2) 2015-04-30 10:38:00,048 WARN  (Loader.java:264) - HHH000444: Encountered request for locking however dialect reports that database prefers locking be done in a separate select (follow-on locking); results will be locked after initial query executes
[Server:server-two] 10:38:00,049 INFO  [stdout] (scheduler-2) Hibernate: select * from ( select distinct synchroniz0_.id as id1_127_, synchroniz0_.creationDate as creation2_127_, synchroniz0_.running as running3_127_, synchroniz0_.CRONJOBTYPE as CRONJOBT4_127_ from SYNCHRONIZED_CRON_JOB_TASK synchroniz0_ where synchroniz0_.CRONJOBTYPE=? ) where rownum <= ?
[Server:server-two] 10:38:00,053 INFO  [stdout] (scheduler-2) Hibernate: select id from SYNCHRONIZED_CRON_JOB_TASK where id =? for update
[Server:server-two] 10:38:00,056 INFO  [stdout] (scheduler-2) Hibernate: update SYNCHRONIZED_CRON_JOB_TASK set creationDate=?, running=?, CRONJOBTYPE=? where id=?

该警告没有问题,您可以看到第一个select和第二个select for update,因此Oracle应该阻止该行上的其他select操作。但关键是,查询没有被阻塞,所以两个作业可以毫无问题地进入并进行选择和更新。锁不起作用,如果我们同时运行两个cron作业,我们可以看到它:

[Server:server-one] 10:38:00,008 INFO  [stdout] (scheduler-3) 2015-04-30 10:38:00,008 WARN  (Loader.java:264) - HHH000444: Encountered request for locking however dialect reports that database prefers locking be done in a separate select (follow-on locking); results will be locked after initial query executes
[Server:server-two] 10:38:00,008 INFO  [stdout] (scheduler-2) 2015-04-30 10:38:00,008 WARN  (Loader.java:264) - HHH000444: Encountered request for locking however dialect reports that database prefers locking be done in a separate select (follow-on locking); results will be locked after initial query executes
[Server:server-two] 10:38:00,009 INFO  [stdout] (scheduler-2) Hibernate: select * from ( select distinct synchroniz0_.id as id1_127_, synchroniz0_.creationDate as creation2_127_, synchroniz0_.running as running3_127_, synchroniz0_.CRONJOBTYPE as CRONJOBT4_127_ from SYNCHRONIZED_CRON_JOB_TASK synchroniz0_ where synchroniz0_.CRONJOBTYPE=? ) where rownum <= ?
[Server:server-one] 10:38:00,009 INFO  [stdout] (scheduler-3) Hibernate: select * from ( select distinct synchroniz0_.id as id1_127_, synchroniz0_.creationDate as creation2_127_, synchroniz0_.running as running3_127_, synchroniz0_.CRONJOBTYPE as CRONJOBT4_127_ from SYNCHRONIZED_CRON_JOB_TASK synchroniz0_ where synchroniz0_.CRONJOBTYPE=? ) where rownum <= ?
[Server:server-two] 10:38:00,013 INFO  [stdout] (scheduler-2) Hibernate: select id from SYNCHRONIZED_CRON_JOB_TASK where id =? for update
[Server:server-one] 10:38:00,014 INFO  [stdout] (scheduler-3) Hibernate: select id from SYNCHRONIZED_CRON_JOB_TASK where id =? for update
[Server:server-two] 10:38:00,016 INFO  [stdout] (scheduler-2) 2015-04-30 10:38:00,015 DEBUG (SynchronizedCronJobService.java:65) - Task read SynchronizedCronJobTask [id=185, type=AlertMailTaskExecutor, creationDate=2015-04-25 07:11:33.0, running=false]
[Server:server-two] 10:38:00,018 INFO  [stdout] (scheduler-2) Hibernate: update SYNCHRONIZED_CRON_JOB_TASK set creationDate=?, running=?, CRONJOBTYPE=? where id=?
[Server:server-one] 10:38:00,022 INFO  [stdout] (scheduler-3) 2015-04-30 10:38:00,022 DEBUG (SynchronizedCronJobService.java:65) - Task read SynchronizedCronJobTask [id=185, type=AlertMailTaskExecutor, creationDate=2015-04-25 07:11:33.0, running=false]
[Server:server-one] 10:38:00,024 INFO  [stdout] (scheduler-3) Hibernate: update SYNCHRONIZED_CRON_JOB_TASK set creationDate=?, running=?, CRONJOBTYPE=? where id=?

我尝试在一个有两个连接的SQL工具上进行更新选择,并且这个工具在这个工具中工作得很好。但是如果我在SQL工具上选择更新并启动cron作业,它们就不会出现问题。

我认为问题来自JPA、HiberNate或Oracle驱动程序,但我不确定。知道问题出在哪里吗?我应该使用anotehr策略吗?提前感谢。

共有3个答案

谷梁卓
2023-03-14

将锁定模式设置为PESSIMSTIC_READ,因为您需要第二台服务器在更改数据之前知道第一台服务器的更改。

钱选
2023-03-14

我可以证实里卡多的观察。我用H2数据库测试了几种锁定模式,都如预期的那样工作。这两种悲观锁模式都不能与Oracle数据库结合使用。我没有尝试乐观锁定,但令人惊讶的是,有一个lockmode根本不适合老大。

薛兴德
2023-03-14

最后,我设法使它工作,但做了一些修改。想法是使用LockModeType。PESSIMSTIC_FORCE_INCREMENT而不是PESSIMITIC_WRITE。使用此锁定模式,Cron作业的行为如下:

  1. 当第一个作业选择更新时,一切都按预期进行,但对象上的版本会更改
  2. 如果另一个作业在第一个作业仍在其事务中时尝试进行相同的选择,JPA将启动OptimisticLockException,因此如果您捕获到该异常,您可以确保它是为读取锁而引发的

此解决方案有多种对应方案:

  1. SyncdCronJobTask 必须具有版本字段,并且受版本控制,@Version
  2. 您需要处理 OptimisticLockException,并且应该在事务服务方法之外捕获它,以便在发生解除锁定时进行回滚。
  3. 恕我直言,这是一个不优雅的解决方案,比简单地锁定 Cron 作业等待以前的作业完成要糟糕得多。
 类似资料:
  • 我正在使用Spring Boot、JPA、Oracle 12C和下面的类型化查询来选择要处理的“新”项目。一旦我选择了“新”项目,我就会更新其状态,使其不再符合选择条件,但我看到一个并发问题,相同的项目被选中。 我在这里读到,我需要设置一个'LockModeType.PESSIMISTIC_WRITE'的查询,以防止其他线程选择相同的行,但它似乎不起作用。 我是否遗漏了下面的内容,或者我是否需要另

  • 问题内容: 我正在尝试为在不同JBoss节点中运行的cron作业实现某种信号量。我正在尝试使用数据库(Oracle 11g)作为一种锁定机制,该锁定机制使用一张表同步不同节点中的cron作业。该表非常简单: 因此,当作业启动时,它将在表中搜索其cronjobtype的条目,并检查其是否已在运行。如果不是,它将条目设置运行标志更新为true。第一次选择是使用Hibernate和Pessimistic

  • Hibernate是我的JPA实现。

  • 我上面代码的日志是: 数据库也会更新。为什么lock()不工作?不是在lock()之后其他实例无法更新吗?还是别的什么?还是我错过了什么?

  • 问题内容: 我在java下有spring项目,使用hibernate查询,我喜欢使用悲观锁定。 在Spring + Hibernate中如何进行悲观锁定? 编辑: 问题: 我想在一个方法中使用悲观锁定,并且我将此方法称为从不同的方法。当我从第一个方法调用它时,悲观的工作效果很好,但是当我从第二个方法调用它时,它给出了(无法提交事务) 例外: 问题答案: http://www.amicabile.c

  • 我使用的是Spring Boot 2和Spring数据JPA。 我有一个带有@Transactional annotation的服务,它从存储库中读取记录,如果记录不存在,则添加记录并保存所有记录。我创建了一个测试方法,并行执行服务方法5次。由于我使用的是@Lock(LockModeType.悲观写入),我希望其中一个线程在读取可用性时会得到锁,而其他4个线程必须等待事务(createReserv