【发布时间】: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)
}
我们尝试过的:
- 使 refreshSession 事务和非事务(相同的结果)
- 从 refreshSession 中删除了“flush:true”。 (结果相同)
- 在刷新会话主体周围添加 try catch(异常 e)。这没有捕获异常。 (结果相同)
- 在刷新会话主体周围添加 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)
-
尝试丢弃()。没有帮助。
@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 中的失败写入。奇怪的是,即使异常被捕获,并且打印“XXX got exception”,异常还是会无缘无故地再次抛出。
我对制作所有东西 NonTransaction 的理解是它的工作方式就像拥有 autoCommit:true - 每个数据库更新都会立即发生,如果一件事失败,它不会回滚其他事情?
有什么想法吗?
【问题讨论】:
标签: grails transactions