我一直试图找出为什么我无法解决这个问题无济于事。
基本上,我有一个域类(PrizeSchedule),它在我的应用程序中用于安排奖品时。当一个请求进入并且是时候给予奖励(预定时间少于现在)时,它将分配该奖品并将其标记为已处理。
这意味着,如果参赛作品之间有足够的时间,可能会有2个或更多奖品准备送出。当我直接在10个(或更多个)请求之后测试我的应用程序时,在这种情况下,我总是在一些请求上遇到以下问题:
optimistic locking failed; nested exception is org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect)
我尝试过使用prizeScheduleInstance.merge和prizeScheduleInstance.lock,但会弹出相同的错误。经过很长时间,我发现PrizeSchedule.find()调用没有产生正确的结果。尽管如此,即使先前的请求已执行以下操作,也会出现:
prizeSchedule.setProcessed(true);
prizeSchedule.setReferenceNumber(prizeSchedule.generateToken());
prizeSchedule.save(flush:true);
下一个请求仍然可以找到已处理的PrizeSchedule实例,您可以在下面的日志中看到
请求1:
2015-07-13 22:05:28,915 [http-bio-8080-exec-7] DEBUG PrizeService - Session ID: 603 won prize ID: 103314
请求2:
2015-07-13 22:05:30,136 [http-bio-8080-exec-12] DEBUG PrizeService - Session ID: 604 won prize ID: 103314
正如您所看到的,即使已处理的标志已标记为true,第二个请求仍以某种方式找到了PrizeSchedule实例。
我尝试过这个查询的变体来找到PrizeSchedule实例,它们都产生了同样的问题,下面是我试过的那些
PrizeSchedule ps = PrizeSchedule.findByCampaignAndDateScheduledLessThanAndProcessed(campaign, new Date(), false, [cache: false])
和
PrizeSchedule ps = ps = PrizeSchedule.find("FROM PrizeSchedule ps WHERE ps.campaign = :campaign AND ps.processed = :processed AND ps.dateScheduled < :now ORDER BY ps.dateScheduled ASC",
[campaign: campaign, processed: false, now: new Date()], [cache: false]
)
如果它很重要 - 控制器我调用了一个服务来处理请求,而这些服务又调用其他groovy / src代码,这些代码通过
来使用prizeServicedef prizeService = Holders.grailsApplication.mainContext.getBean 'prizeService'
这是因为这些领域中使用的通用接口和抽象类。
我希望有人可以提供帮助,并提前感谢您花时间阅读本文!
答案 0 :(得分:1)
好的我发布这个作为我的问题的解决方案,以防万一其他人在将来遇到这个问题。这可能是显而易见的,但作为Grails的相对新人,我发现这很难解决/找到有关的信息。
基本上我的控制器是用
注释的@Transactional(readOnly = true)
进行实际处理的方法用
注释@Transactional
最后,服务本身也注明了
@Transactional
我的假设是,这些注释会以某种方式混合在Hibernate会话中访问和存储数据时使用的事务。从控制器中删除注释(并调整代码)并将我的服务作为@Transactional组件后 - 一切都按照我的预期开始工作。
我希望这可以帮助像我一样受苦的人!
答案 1 :(得分:0)
我希望您理解,当您使用乐观锁进行并发更新时,可能会抛出StateObjectStateException。随着模型中并发性的增加,它将变得更加普遍。
由于您提到这是预定作业的一部分,您可以使用代码在新会话中重试奖励,例如,安排另一个作业立即再次运行。