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)
}
我们尝试了什么:
有趣的是,最后一行更改了异常发生在下一行的行(读取帐户的行,不写入,只读取)
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)
尝试丢弃()。没有帮助。
@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在自己的事务中获得自己的会话副本。
@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中写入失败。 Bazarely,即使异常被捕获,并打印出" XXX得到异常",异常再次被抛出,没有明显的原因。
我对制作所有NonTransaction的理解就像使用autoCommit:true - 每次db更新都会立即发生,如果有一件事失败,它就不会回滚其他东西了?
任何想法?
答案 0 :(得分:3)
如果failOnError = false,它怎么会失败?
您提到了HibernateOptimisticLockingFailureException
。 failOnError
与此无关。 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?