grails - grails 2.5:failOnError:false始终始终在出错时失败。如何不因错误而失败?

标签 grails transactions

Grails 2.5.5交易问题。

我们有一个get Balance调用API调用,它是一个简单的 Controller ,可以调用一些服务。它做两件事,用其余额返回客户帐户并更新客户 session (例如保持 Activity )。

问题是,当客户端同时调用两次getBalance时,总是抛出一个异常,即使在save()中,我们有failOnError:false(并且尝试四处寻找也无济于事,请参见下文)。如果其他任何人当前正在刷新 session ,我们需要refreshSession静默失败,并返回帐户余额,就好像什么都没出错。我们无法弄清楚如何做到这一点,因为failOnError:false并尝试catch不起作用,并且丢弃或刷新均无效。

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,它将如何失败?

session 服务由未定义事务性质的 Controller 调用。

Controller :
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)
    }

我们尝试的是:
  • 使refreshSession具有事务性和非事务性(相同结果)
  • 从refreshSession中删除了“flush:true”。 (结果相同)
  • 在刷新 session 的主体周围添加了try catch(异常e)。这没有捕获到异常。 (结果相同)
  • 在刷新 session 的主体周围添加了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)
    
  • 尝试了throw()。没有帮助。
     @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
     }
    
  • 使用flush:true尝试了上述方法。结果相同。
  • 尝试使refreshSession在自己的事务中获取自己的 session 副本。
    @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一样-每个数据库更新都将立即发生,并且如果某件事失败,它将不会回滚其他事情吗?

    有任何想法吗?

    最佳答案

    我认为您需要 .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吗?

    关于grails - grails 2.5:failOnError:false始终始终在出错时失败。如何不因错误而失败?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40567819/

    相关文章:

    unit-testing - 在域类中测试服务

    Grails - 多个数据源

    java - 带 hibernate 的 DAO 和服务层

    java - 通用连接池 java JDBC Oracle 12c 的应用程序连续性

    java - Spring 同步 Hibernate 和 JMS 事务

    database - Cloud Firestore 文档锁定

    java - eachWithIndex 循环不稳定

    grails withTransaction - 为什么它在域对象上?

    grails install-plugin 对我不起作用

    Java MySQL事务和executeBatch