3

我最近在尝试将子对象添加到父对象的列表时遇到了一些并发修改错误。

根据我的阅读,添加子对象会影响父对象的版本号,因此如果另一个线程在我的 create 方法中途,它会抱怨。

很公平。

我回去尝试锁定域对象,这样它就会阻塞,直到之前的添加完成。令人惊讶的是,当我尝试锁定对象时,它现在似乎抛出了一个 StaleObjectStateException !

这是我的测试代码:

  def create(User user, Company company, ReferenceCodeCommand cmd) {                                                                                                                                                                                                                                                                                   
      log.debug("superbooking type: " + cmd.superBooking.getClass().getName())                                                                                                                          
      cmd.superBooking.refresh();      
      log.debug("#" + random + " Locking superbooking: " + superBooking.id)                                                                                                                                                                  
      def superBooking = SuperBooking.lock(cmd.superBooking.id)  
      log.debug("#" + random + " Superbooking: " + superBooking.id + " locked")                                                                                                                                         
      def referenceCode = new ReferenceCode(cmd.properties)                                                                                                                                             

      def random = Math.random()                                                                                                                                                                        

      log.debug("#" + random + " ReferenceCode test point 1. SuperBooking version " + referenceCode.superBooking.version)                                                                                                                                                                                                                                           
      superBooking.addToRefs(referenceCode)                                                                                                                                                                                                                                                                                                                 
      log.debug("#" + random + " ReferenceCode test point 2. SuperBooking version " + superBooking.version)                                                                                                                                                                                                                              

      if (!referenceCode.hasErrors()                                                                                                                                                                    
              && referenceCode.save(flush: true)) {                                                                                                                                                     
          log.debug("ReferenceCode saved successfully")                                                                                                                                                                                                                            
      } else {                                                                                                                                                                                                                                                                                                                                                                              
          log.warn("#" + random + " ReferenceCode not saved successfully ${referenceCode}.")                                                                                                            
      }                                                                                                                                                                                                 
      log.debug("#" + random + " ReferenceCode test point 3. SuperBooking version " + superBooking.version)                                                                                             
      superBooking.save(flush: true, failOnError: true)                                                                                                                                                 
      log.debug("#" + random + " ReferenceCode test point 4. SuperBooking version " + superBooking.version)                                                                                             

      return referenceCode                                                                                                                                                                              
  } 

这是一些示例输出:

2013-09-06 13:31:21,645 [http-bio-8080-exec-19] DEBUG dao.ReferenceCodeDAOService  - superbooking type: com.ngs.id.model.SuperBooking
2013-09-06 13:31:21,667 [http-bio-8080-exec-19] DEBUG dao.ReferenceCodeDAOService  - #0.06947409938388194 Locking superbooking: 208535
2013-09-06 13:31:21,667 [http-bio-8080-exec-19] DEBUG dao.ReferenceCodeDAOService  - #0.06947409938388194 Superbooking: 208535 locked
2013-09-06 13:31:21,667 [http-bio-8080-exec-19] DEBUG dao.ReferenceCodeDAOService  - #0.06947409938388194 ReferenceCode test point 1. SuperBooking version 6
2013-09-06 13:31:21,667 [http-bio-8080-exec-19] DEBUG dao.ReferenceCodeDAOService  - #0.06947409938388194 ReferenceCode test point 2. SuperBooking version 6
2013-09-06 13:31:21,701 [http-bio-8080-exec-19] DEBUG dao.ReferenceCodeDAOService  - #0.06947409938388194 ReferenceCode test point 3. SuperBooking version 7
2013-09-06 13:31:21,708 [http-bio-8080-exec-19] DEBUG dao.ReferenceCodeDAOService  - #0.06947409938388194 ReferenceCode test point 4. SuperBooking version 7
| Error 2013-09-06 13:31:21,715 [http-bio-8080-exec-20] ERROR errors.GrailsExceptionResolver  - StaleObjectStateException occurred when processing request: [POST] /interpreter-direct/api/superBooking/208535/referenceCode/
Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [com.ngs.id.model.SuperBooking#208535]. Stacktrace follows:
Message: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [com.ngs.id.model.SuperBooking#208535]

错误发生在我锁定 SuperBooking 的行上。这真的让我感到困惑:从我读过的内容来看,lock() 将从数据库中获取行并锁定它,直到事务完成。我会认为 StaleObjectStateException 在这里甚至没有意义。

有没有人对我有任何提示,或者可能是另一种方法?这对我们的应用程序产生了严重影响。

谢谢!

4

1 回答 1

0

我想您使用数据库锁定来支持可以使用相同数据库的多服务器。看起来你的代码很好。可能导致此异常的原因是在调试时运行并在没有正确完成执行的情况下停止,因此未提交 db 事务并且锁仍然存在。尝试在没有为同一个 cmd.superBooking.id 打开事务时执行此操作。或者在测试时尝试使用不同的 id。通常,如果您的应用程序更有可能不会对同一行执行多次更新,请改用乐观锁定。即,不要使用锁定方法,而是使用 try 和 catch 处理 StaleObjectException。

于 2013-09-08T19:19:10.433 回答