JPA LockModeType.PESSIMISTIC_WRITE没有按预期工作。没有密钥的应用程序,在锁定

时间:2016-05-09 15:39:27

标签: java oracle hibernate jpa locking

我有两个运行相同代码的应用程序,在hibernate的帮助下查询同一个Oracle数据库 有一个表可以保存要发送的电子邮件 这两个应用程序都运行一个调度程序,用于查询此表中的列以查找" QUEUED" string(标记要发送的电子邮件),创建和发送电子邮件,最后更新," QUEUED"到" SENT"所以这些电子邮件不会再被发送 理论上,我希望一个应用程序读取一些行,锁定它们的读写,更新它们并解锁以供另一个应用程序使用。
因此我使用以下查询:

String jpql = "SELECT m FROM Email m WHERE m.status = :status";
return em.createQuery(jpql, Email.class)
        .setParameter("status", "QUEUED")
        .setLockMode(LockModeType.PESSIMISTIC_WRITE)
        .getResultList();  

由应用程序和文档使用,LockModeType.PESSIMISTIC_WRITE相当于" SELECT FOR UPDATE"。
相反,以下行为发生:

13:06:02,160 | MailQueueMonitor_1| Found 0 email(s) to be sent. // No rows returned from app1  

13:06:03,813 | MailQueueMonitor_2| Found 0 email(s) to be sent. // No rows returned from app2  

13:06:12,180 | MailQueueMonitor_1| Found 1 email(s) to be sent. // 1 mail returned from app1  

13:06:12,190 | MailQueueMonitor_1| Mailer will sleep for 30s    // App1 will sleep for 30s   

// At this point, app2 tries to execute query but freezes as app1 has the keys to the rows  

13:06:42,191 | MailQueueMonitor_1| Mailer woke up and will try to send mails    // App1 wakes up  

13:06:46,796 | MailQueueMonitor_1| Mailer sent mail     // App1 sent mail  

13:06:46,798 | MailQueueMonitor_1| Mailer changed mail status to SENT // App1 update status from QUEUED to SENT  

// At this point, app1 releases the locks and app2 unfreezes and executes query looking for QUEUED rows which should not exist at this point since they where updated to SENT.  

13:06:46,809 | MailQueueMonitor_2| Found 1 email(s) to be sent. // App2 queries and finds 1 row! It is like it queried a snapshot of the database before app2 updated all rows.  

13:06:46,836 | MailQueueMonitor_2| Mailer will sleep for 30s    // App2 will sleep for 30s  

13:07:16,836 | MailQueueMonitor_2| Mailer woke up and will try to send mails    // App2 wakes up  

13:07:21,457 | MailQueueMonitor_2| Mailer sent mails    // App2 sent mail. This is re-senting above email occuring to duplicate emails.  

13:07:21,458 | MailQueueMonitor_2| Mailer changed mail status SENT  // App2 update status from QUEUED to SENT, again!

问题是,为什么app2不会读取更新的行,即使在锁定释放后执行查询。 为什么app2尝试查询锁定的行时不会抛出异常? 我应该如何锁定行被读取或更新,并且在锁定释放后,将查询数据库的下一个应用程序将看到更新的数据?

一些注意事项:
1.如果我在两个连续的行上运行查询两次,当锁被释放时,先前锁定的应用程序将执行第一个查询(这是被锁定的一个)将返回未更新的数据,但第二个将返回应用程序的更新数据以前有过锁 2.如果我通过两个ORACLE SQL DEVELOPER实例手动运行上述过程,则行为符合预期,即:

SQL_DEV_1: SELECT * FROM T_MAIL WHERE STATUS = 'QUEUED' FOR UPDATE; // Returns 1 row, locks the row
SQL_DEV_2: SELECT * FROM T_MAIL WHERE STATUS = 'QUEUED' FOR UPDATE; // Doesn't return anything but keeps waiting for locks to be released
SQL_DEV_1: UPDATE T_MAIL SET STATUS = 'SENT'; // Returns 1 row, locks the row
SQL_DEV_1: COMMIT; // Commit update, locks are released
SQL_DEV_2: // waiting query is executed, returns no rows since one and only row was update to SENT

1 个答案:

答案 0 :(得分:1)

我认为我找到了解决方案(或解决方法)。当您使用PESSIMISTIC_WRITE时,第二个服务器不知道第一个服务器正在更改数据。在使用LockModeType.PESSIMISTIC_READ时,他可以了解这一点,但在LockModeType.PESSIMISTIC_READ中使用LockModeType.PESSIMISTIC_WRITE实现了version。因此,针对您的解决方法是将LockModeType.PESSIMISTIC_FORCE_INCREMENT字段和设置模式添加到library(ggplot2) dat = data.frame(x=c(0,100), ymin=0, y=c(100,0), ymax=100) ggplot(dat) + geom_ribbon(aes(x, ymin=y, ymax=ymax), fill="blue") + geom_ribbon(aes(x, ymin=ymin, ymax=y), fill="red") + geom_abline(aes(intercept=100, slope=-1)) + #geom_line(aes(x,y), lwd=1) + # If you just want a line with the same extent as the data theme_bw()