grails 2.5:failOnError:false在出错时总是仍然失败。如何在出错时失败?

时间:2016-11-12 21:25:40

标签: grails transactions

Grails 2.5.5交易问题。

我们有一个get Balance调用API调用,它是一个调用一些服务的简单控制器。它做两件事,用他们的余额返回客户账户并更新客户会话(比如保持活着)。

问题是当客户端同时调用getBalance两次时,而不是总是抛出一个异常,即使在save()中我们有failOnError:false(并且把try catch放在没有帮助,见下文)。如果其他人当前正在刷新会话,我们需要refreshSession静默失败,并返回帐户余额,就好像什么都没有出错一样。我们无法弄清楚如何在grails中执行此操作,因为failOnError:false和try catch无效,并且discard和refresh都没有任何效果。

SessionService:

boolean refreshSession( Session aSession ) {
    if ( aSession != null ) {
        aSession.lastUpdatedAt = new Date()
        aSession.save(flush:true, failOnError: false)  // line 569.  This always fails on error even with failOnError set to false.
        return true
    }
    return false
}

这是我们得到的错误:

org.springframework.orm.hibernate4.HibernateOptimisticLockingFailureException: Object of class [com.xxx.Session] with identifier [23]: optimistic locking failed; nested exception is org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect) : [com.xxx.Session#23]
at com.xxx.services.SessionService$$EQ2LbJCm.refreshSession(SessionService.groovy:569)
at com.xxx.getBalance(AccountController.groovy:56)

如果failOnError = false,它怎么会失败?

会话服务由控制器调用,该控制器没有定义任何事务性能:

控制器:

def getBalance() {
 try {
        :
        Session session = sessionService.getSession(payload.token)

        sessionService.refreshSession(session) // this updates the session object

        def accountResult = accountService.getBalance(session.player)  // line 59: this returns a structure of account domain objects 
                                                                       // which have a balance and currency etc. It is ready only!

        render(status: 200, contentType: 'application/json') {
            [
                   'result'  : 0,
                   'accounts': accountResult.accounts
            }
        return

     } catch (Exception e) {
        renderError(ERROR, e.toString())
        log.error("getBalance API Error:" + e.toString(), e)
    }

我们尝试了什么:

  1. 制作refreshSession Transactional和NonTransactional(相同结果)
  2. 删除" flush:true"来自refreshSession。 (同样的结果)
  3. 在刷新会话的主体周围添加try catch(Exception e)。这没有发现异常。 (同样的结果)
  4. 在刷新会话的主体周围添加try catch(Exception e)并生成refreshSession NotTransactional。
  5. 有趣的是,最后一行更改了异常发生在下一行的行(读取帐户的行,不写入,只读取)

    org.springframework.orm.hibernate4.HibernateOptimisticLockingFailureException: Object of class [com.xxx.Session] with identifier [23]: optimistic locking failed; nested exception is org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect) : [com.xxx.Session#23]
    at com.xxx.getBalance(AccountController.groovy:59)
    
    1. 尝试丢弃()。没有帮助。

       @NotTransactional
      
       boolean refreshSession( Session aSession ) {
          if (aSession != null) {
              try {
                  aSession.lastUpdatedAt = new Date()
                  aSession.save(failOnError: false)
              } catch (Exception e) {
                  aSession.discard()
                  aSession.refresh()
              } finally {
                  return true
              }
          }
      
       return false
       }
      
    2. 尝试使用flush:true尝试上述操作。同样的结果。

    3. 尝试让refreshSession在自己的事务中获得自己的会话副本。

      @Transactional
      boolean refreshSession( long id ) {
          Session session = Session.get(id)
          if (session != null) {
              try {
                  session.lastUpdatedAt = new Date()
                  session.save(flush: true, failOnError: false)
             } catch (Exception e) {
                 println("XXX got exception:" + e.message)
                 session.discard()
             } finally {
                 return true
             }
          }
          return false
      }
      
    4. 此失败,原始异常。似乎无法忽略在grails中写入失败。 Bazarely,即使异常被捕获,并打印出" XXX得到异常",异常再次被抛出,没有明显的原因。

      我对制作所有NonTransaction的理解就像使用autoCommit:true - 每次db更新都会立即发生,如果有一件事失败,它就不会回滚其他东西了?

      任何想法?

2 个答案:

答案 0 :(得分:3)

  

如果failOnError = false,它怎么会失败?

您提到了HibernateOptimisticLockingFailureExceptionfailOnError与此无关。 failOnError控制在.save()期间发生验证错误时是否抛出异常。这不会影响HibernateOptimisticLockingFailureException

答案 1 :(得分:1)

我认为你需要.withNewTransaction

boolean refreshSession( long id ) {
Session.withNewTransaction {
Session session = Session.get(id)
    if (session != null) {
        try {
            session.lastUpdatedAt = new Date()
            session.save(flush: true, failOnError: false)
       } catch (Exception e) {
           println("XXX got exception:" + e.message)
           session.discard()
       } finally {
           return true
       }
    }
    }
    return false
}

在提供的链接中,它返回了最新状态,因为该元素在进程中可能已经更新,但是它已经记录并且正在尝试检查它。您还可以查看isDirty以及getPersistentValue。个人认为用NewTransaction包装将解决问题。你说你想丢弃第二次点击但实际上不是那么有效且最新的lastUpdatedAt?