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

在服务中处理StaleObjectException

席安康
2023-03-14

我有一个场景,当一个对象被两个不同的线程更新。下面是grails服务类中的代码。我能够捕捉StaleObject异常,但当我试图从数据库中再次获取它并重试保存值时,它不起作用。

public long updateTimer(Long timeLeft, TestAttempted testAttempted){
    // Let's say testAttempted.version() is now 5
    // It is concurrently updated by other thread, version is now 6
    ........
    ............
    testAttempted.setTimer(someCalculatedValue)
    try{
        testAttempted.save(failOnError: true,flush:true) // StaleObject exception occurs
    }catch(OptimisticLockingFailureException e){
        testAttempted.refresh()
        testAttempted.setTimer(someCalculatedValue)
        testAttempted.save(failOnError:true)
    }
}

为什么上面的代码不更新/保存catch块中的值?我也试过了。get(id)方法从数据库中获取最新的,但它不起作用。

但当我尝试此操作时,它会更新最新的计时器值:

在控制器中:-

    try{
         timeLeft = assessmentService.updateTimer(timeLeft,testAttempted)
    }catch(OptimisticLockingFailureException e){
        testAttempted = TestAttempted.get(session['testAttemptedId'])
        ........
        testAttempted.setTimer(someCalculatedValue)
        testAttempted.save(failOnError: true)
    }

在职:

 public long updateTimer(Long timeLeft, TestAttempted testAttempted){
    ........
    .................
    testAttempted.setTimer(someValue)
    testAttempted.save(failOnError: true)
    return timeLeft
}

如果在控制器/服务中抛出和处理它,则它不起作用。它在投入服务并在控制器中处理时工作。怎么可能呢?

共有3个答案

苏华藏
2023-03-14

重试方法的问题是,重试多少次就足够了?试试这个:

class AssessmentService {

    /*
     * Services are transactional by default.
     * I'm just making it explicit here. 
    */
    static transactional = true

    public long updateTimer(Long timeLeft, Long testAttemptedId) {
        def testAttempted = TestAttempted.lock(testAttemptedId)

        testAttempted.setTimer(someCalculatedValue)
        testAttempted.save()
    }
} 

传递TestAttempted ID而不是实例,以便服务可以使用自己的事务自行检索实例。

如果您想传入TestAttemted实例,我认为在对实例进行更改之前,您必须在服务方法中调用testAttempted.merge()。

这里有类似的问题。

胡博艺
2023-03-14

很可能,当您在catch块中执行refresh()然后执行save()时,testAttempted的实例在刷新和保存之间发生了更改,因此它会失败,并出现相同的异常,现在您无法捕获该异常,因为它已经在catch块中。

域的get()方法afaik被缓存在会话中,因此被测试。get(id)将从会话而不是数据库返回实例。

Merge()在这种情况下是不必要的,因为您在刷新后和保存前手动设置值。

使用Domain.lock()可能是一个解决方案,但它会影响您如何处理代码的其他部分中的TesttAttemture,因为现在您可能会在您试图检索一个实例,它被这部分代码锁定。

问题是——什么是冲突解决战略?如果是“last writer wins”,则只需为域设置version=false。或者,您可以使用TestAttemted。executeUpdate('set timer=..,其中id=..)在不增加版本的情况下进行更新。

如果我想了解更复杂的场景,请查阅马克·帕尔默对该问题的深入报道。http://www.anyware.co.uk/2005/2012/11/12/the-false-optimism-of-gorm-and-hibernate/

殷宾白
2023-03-14

关键是您应该始终重试整个事务。让事务回滚并在新事务中重复所有操作,因为旧事务是脏的(Hibernate会话无效,可能有一些未提交的更改已刷新到数据库)。

 类似资料:
  • 问题内容: 我最近公布的我面对这个问题的详细说明,这里的SO。由于我无法发送实际的请求,因此我使用了超时来模拟异步行为。在@Gloopy的帮助下,从模型到视图的数据绑定工作正常 现在,当我使用而不是(在本地测试)时,我可以看到异步请求成功,并且在我的服务中充满了json响应。但是,我的看法没有更新。 在这里更新了Plunkr 问题答案: 这是一个可以满足您需求的Plunk:http ://plnk

  • 问题内容: 我最近公布的我面对这个问题的详细说明,这里的SO。由于我无法发送实际的请求,因此我使用了超时来模拟异步行为。在@Gloopy的帮助下,从模型到视图的数据绑定工作正常 现在,当我使用而不是(在本地测试)时,我可以看到异步请求成功,并且在我的服务中充满了json响应。但是,我的看法没有更新。 问题答案: 这个想法是您直接使用promise及其promise,然后使用它们的“ then”函数

  • 问题内容: 我的公司拥有数千个现有的xml Web服务,并开始为新项目采用AngularJs。 http://angularjs.org/上的教程专门使用json服务。看起来他们在控制器中进行了服务调用,解析了生成的JSON,并将生成的对象直接传递给视图。 我该如何处理XML?我看到四个选项: 解析它,然后将DOM对象直接传递到UI(view)。 在服务器端围绕我的XML服务放置一个JSON包装器

  • 问题内容: 如果我的Dao层抛出了Dao特定的异常,那么在我的服务层中对它们的处理是否会引起关注的泄漏?如果是,那么我应该使异常通用且独立于任何层来解决它,还是有其他方法吗? 相同的问题适用于服务层引发的UI层处理异常。 问题答案: 当我们创建一个分层的应用程序时,总是有一个用户层和另一个使用过的层。对于这种情况,UI层->使用服务层->使用DAO层。 现在,它非常主观并且易于解释。但目标应该是

  • 我对Spring和Activiti完全陌生,并为自己做了一个运行良好的小项目。该服务中有4个服务任务、一个REST控制器、1个进程、1个服务和4个方法。 当我调用服务器endpoint时,我启动了我的流程,它只是一步一步地完成我的服务任务并调用服务。方法,如表达式${service.myMethod()}中定义的。 但是,我真正需要的是一个工作流,它在servicecall之后停止,并等待发送另一

  • 我是web服务新手,正在阅读Martin Kalin的《Java Webservices》一书。我已经了解了它最初的基本概念,有一个问题: 假设将HTTP请求(包含SOAP消息信封)发送到JavaWeb服务()。该请求是否由Servlet内部处理,Servlet提取SOAP消息并将其转换为相应Java域对象,然后调用服务实现bean? 无论Metro和Axis等现成框架如何,这个问题都是通用的。只