org.hibernate.StaleObjectStateException行已被另一个事务更新或删除(或未保存的值映射不正确)

时间:2014-08-31 15:03:02

标签: grails

这个错误一直困扰着我6个月,并且不知道如何解决它。

第一个问题是堆栈跟踪没有给出关于哪个行,查询或控制器导致问题的任何提示。该错误发生在PageFragmentCachingFilter.java

我做了几个并发的事情,每次调用特定函数时我都会在DB中更新一个计数器。如果两个人在同一时间执行此操作,则可能存在锁定问题。

我的控制器做了三件事:

  1. 从名为Query的域对象的grails DB中读取一行(使用Query.get(id))
  2. 调用服务从外部数据库中提取大量数据。这需要几分钟
  3. 增加从db中读取的行中的值。
  4. 使用(failOnError:false)
  5. 保存行

    failOnError = false是试图通过忽略更新错误来解决问题,但它没有工作 - 仍然发生异常。

    问题是:

    1. 我不知道这是不是问题。或者如何找到问题实际发生的位置。
    2. 我不知道为什么设置failOnError:false没有修复它。
    3. 我不知道如何将悲观锁定变成真正的锁定让你等待(见下面的尝试)
    4. 我不知道锁定在什么时候发生,所以它只锁定了最短的时间。
    5. 我可以通过同时从两个不同的浏览器窗口调用控制器来重现该问题。

      === update 1 === 我尝试将服务中的步骤3和4放在服务中,理解服务自动启动和结束事务。这并没有解决问题,但是在调用这个新服务来更新计数器的行上抛出相同的exeption,所以这至少指出了问题。

      假设有一个域对象报告,如下所示:

      class Query {
        String sql
        String name
        int counter
      }
      

      现在的步骤是:

      1. 从grails DB中读取所需的Query实例,例如Query.get(1)
      2. 调用需要很长时间的服务,传入sql值。
      3. 调用服务,传递查询实例对象。此服务递增计数器,然后调用保存。 (见下面的代码)
      4. 两个不同的人在同一个查询对象上同时执行上述步骤。

        @Transactional
        class aService {
        
            def count(Query query) {
                query.counter++
                query.save();
            }
        

        以上,即使是事务性的,也会失败并出现同样的错误。

        我认为我需要做一些像重新读取实例对象的事情。

        ====更新2 =====

        尝试更改服务以执行此操作:

        def count(Query query) {
            Query q2 = Query.get(query.id)
            q2.counter++
            q2.save();
        }
        
        当两个人同时尝试这样做时,

        得到同样的错误。不起作用。

        === update 3 ====

        寻找一种方法来“选择更新”,这样它就会锁定行,更新它,释放。这必须是多用户安全的。

        试过这个:

        def count(Query query) {
            Query q2 = Query.get(query.id)
            q2.lock()
            q2.counter++
            q2.save();
        }
        
        猜猜是什么?同样的错误,但这次把它扔在锁线上。如何进行全新的select更新会导致staleObjectStateException?

        === update 4 ====

        试过这个:

        def count(Query query) {
            Query q2 = Query.lock(query.id)
            q2.counter++
            q2.save();
        }
        
        猜猜是什么?同样的错误。似乎无法更新2人或更多人使用的计数器。此外,我可以100%重现,这是非常奇怪的,因为这个错误应该只发生很少,因为这个简单的更新的速度。有些事情是非常错误的。

        === update 5 ========

        变得更加绝望。尝试以下方法:

        import org.springframework.transaction.annotation.*
        class aService {
        @Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
        def count(Query query) {
            Query q2 = Query.lock(query.id)
            q2.counter++
            q2.save(flush:true);
        }
        

        锁定行上的相同错误。似乎在grails中不可能做相同的

        update query set counter = counter + 1 where id = y
        

        甚至

        select * from query where id = 1 for update
        update query set counter = counter + 1 where id = 1
        commit
        

        === update 6 ====

        有趣的是,在步骤1中发生的读取使用get。我尝试将其更改为读取(id),希望这可能会有所帮助。有趣的是,如果我使用read读取实例,那么应用程序每次都会在此行上失败,即使有1个用户:

            Query q2 = Query.lock(query.id)
        

        错误:

         attempted to lock a deleted instance
        

        WTH?首先,我没有删除任何实例,其次,我试图锁定一个不错的新读取,第三,我没有编辑或更改原始读取对象。

0 个答案:

没有答案