在lock()方法中使StaleObjectStateException异常

eko*_*eko 5 java grails multithreading hibernate

我在尝试将域对象锁定在事务服务内部时收到StaleObjectStateException(2.3.8版):

@Transactional
class AnalyticsService {
    boolean newStreamView(Long streamId) {

    Stream stream = Stream.lock(streamId) // The exception is launched here
Run Code Online (Sandbox Code Playgroud)

当然,只有在有许多并发调用此服务时才会发生这种情况。如我所见,hibernate尝试使用ID和版本参数进行锁定:

select id from stream where id =442 and version =305 for update
Run Code Online (Sandbox Code Playgroud)

这失败了。如果我在该域类中禁用了乐观锁定(版本:false),则一切正常(休眠状态仅使用ID来锁定行)。

正如Marc Palmer的博客中所发布的那样:

在保持乐观锁定为ON的同时避免StaleObjectException(s)的唯一简便方法是完成事务中的所有GORM工作,并始终使用Domain.lock(id)加载对象。使用动态查找器或条件时,您需要指定“锁定”选项以预先锁定结果

他说,我们应该保持乐观的态度。

有没有安全的方法可以避免在锁定和乐观锁定为ON的情况下避免StaleObjectStateException?

如果禁用乐观锁定(版本:false),还会发生什么其他问题。我对此很担心,因为此域类是从其他服务中更新的?

提前致谢。

小智 3

我们通过以下方式修复了 100% 的 StaleStateException/OptimisticLocking 问题:

  • 我们不会明确锁定任何内容。我们使用以下内容调整了代码流和对象,以最大限度地减少锁争用的可能性
  • 从所有控制器中删除所有 @Transactional 注释。将修改域对象的代码从控制器移至服务中。永远不要修改控制器中的域对象。仅委托服务(默认情况下是事务性的)来修改域对象。也许您在本例中这样做了,但请确保您 100% 都这样做。
  • 不要禁用乐观锁定,它的存在是有原因的。如果没有它,您将面临覆盖来自不同事务的更新的风险。一般来说,如果您要通过显式锁定或事务处理中的其他干预来解决此问题,您确实需要知道自己在做什么。
  • 请记住,如果您的域对象是belongsTo/hasMany关系的一部分,则当发生任何更新时,所有相关对象的版本号都会增加。因此,如果两个不同的进程正在更新对象图的不同部分,第一个提交的进程将使第二个进程无效并导致这种情况。看看伯特·贝克威斯在这里所说的是否相关:https://www.youtube.com/watch?v =-nofscHeEuU 。尽管他在这里谈论的是性能,但他提出的解决方案也最大限度地减少了版本号更新的级联。
  • 同样,您似乎没有在这里执行此操作,但当我们只需要执行特定更新时,我们会尽量减少传递可能脏的整个域对象。因此,当我们所做的只是设置状态时,我们可能会说 orderService.setStatus('foo',orderId) 并让该方法执行获取和更新,而不是 orderService.update(order) 。这会收紧 StaleState 发生的机会窗口,因为从获取到保存的时间很短。基本上,请确保您保留脏域对象的时间不会超过您绝对需要的时间。