【问题标题】:grails 2.5: failOnError:false always still fails on error. how to not fail on error?grails 2.5:failOnError:false 总是在出错时失败。如何不因错误而失败?
【发布时间】:2016-11-13 22:34:44
【问题描述】:

Grails 2.5.5 事务问题。

我们有一个 get Balance call API 调用,它是一个简单的控制器,它调用一些服务。它做了两件事,返回客户帐户及其余额并更新客户会话(如保持活动状态)。

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

会话服务:

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 事务和非事务(相同的结果)
  2. 从 refreshSession 中删除了“flush:true”。 (结果相同)
  3. 在刷新会话主体周围添加 try catch(异常 e)。这没有捕获异常。 (结果相同)
  4. 在刷新会话主体周围添加 try catch(异常 e)并使 refreshSession NotTransactional。

有趣的是,最后一行将异常发生的行更改为下一行(读取帐户的行,不写入,仅读取)

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
    }
    

这会因原始异常而失败。似乎不可能忽略 grails 中的失败写入。奇怪的是,即使异常被捕获,并且打印“XXX got exception”,异常还是会无缘无故地再次抛出。

我对制作所有东西 NonTransaction 的理解是它的工作方式就像拥有 autoCommit:true - 每个数据库更新都会立即发生,如果一件事失败,它不会回滚其他事情?

有什么想法吗?

【问题讨论】:

    标签: grails transactions


    【解决方案1】:

    如果 failOnError = false 怎么会失败?

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

    【讨论】:

      【解决方案2】:

      我觉得你需要.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
      }
      

      在提供的链接中,它返回了最新状态,因为元素可能在此过程中已更新,同时它获取记录并尝试检查它。您还可以查看isDirtygetPersistentValue。个人认为包装 withNewTransaction 将解决问题。你说你想放弃第二次点击,但实际上不是有效和最新的 lastUpdatedAt 吗?

      【讨论】:

      • Session.withNewTransaction 解决了它。我以前没见过那个结构。我还假设,如果控制器不是跨国的,并且服务是事务,那么服务无论如何都会在该服务方法的持续时间内创建一个新事务。我(和我所有的 grails 同事)错了多少。有没有人找到详细介绍 Grails 中事务如何工作的博客或文档?
      猜你喜欢
      • 1970-01-01
      • 2011-08-12
      • 2022-08-11
      • 1970-01-01
      • 1970-01-01
      • 2014-08-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多