java - lock() 方法中的 Grails StaleObjectStateException

标签 java multithreading hibernate grails

我在尝试锁定事务服务 (grails 2.3.8) 中的域对象时收到 StaleObjectStateException:

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

    Stream stream = Stream.lock(streamId) // The exception is launched here

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

select id from stream where id =442 and version =305 for update

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

发布于 Marc Palmer's Blog似乎是:

The only foolproof way to avoid StaleObjectException(s) while keeping optimistic locking ON, is to do all GORM work in transactions, and always load objects with Domain.lock(id). When using dynamic finders or criteria you will need to specify the “lock” option to have results pre-locked

他说我们应该保持乐观锁定。

是否有任何安全的方法来避免带有锁和乐观锁定的 StaleObjectStateException?

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

提前致谢。

最佳答案

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

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

关于java - lock() 方法中的 Grails StaleObjectStateException,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25301785/

相关文章:

java - 如何设计一个对请求进行排队并批量处理的系统?

java - java中如何从多个文档中搜索一个单词?

java - 线程在 Tomcat 中启动和停止失败。发生了什么?

java - Dropwizard for Hibernate 中的 SQLite 方言

hibernate - 让Oracle触发ID序列而不是Hibernate吗?

java - Apache CXF FaultListener 未在 cxf 总线中注册

java - 为什么插入排序在不同的实现中在时间上运行如此不同

java - Apache Flink 如何并行读取 CSV 文件

java - 线程可运行与 AsyncTask 生命周期

java - 为什么我的 native 生成策略不只在 SQL Server 上创建 id?