首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >高频更新上的StaleObjectStateException

高频更新上的StaleObjectStateException
EN

Stack Overflow用户
提问于 2011-04-29 05:18:51
回答 2查看 7.5K关注 0票数 3

我们使用Hibernate 3.6.3Final和MySQL 5.5.8作为web应用程序。后端在JBoss 6.0.0最终服务器上运行。大多数情况下,事情运行得很好,但偶尔我们会得到一个StaleObjectStateException。经过一段时间的实验,我们发现可以通过向后端发送高频率的请求(即,点击一个尽可能快地触发请求的按钮)。

据我所知,这个异常意味着从数据库中获取了一个域对象,当Hibernate再次尝试持久化它时,它注意到另一个事务在此期间更改了它。

然而,就我对数据库的理解而言,冲突的事务应该被隔离到一定程度,以防止这种行为。我显式地将隔离级别更改为SERIALIZABLE,这保证了可重复读取,并禁用了Hibernate缓存。这应该可以防止一个事务看到同一个域对象的不同版本的情况。

完整的堆栈跟踪是:

代码语言:javascript
复制
    2011-04-28 20:46:17,865 WARN  [com.arjuna.ats.arjuna] (WorkerThread#2[127.0.0.1:57772]) ARJUNA-12125 TwoPhaseCoordinator.beforeCompletion - failed for SynchronizationImple< 0:ffff7f000001:126a:4db9c7b0:74d, org.hibernate.transaction.synchronization.HibernateSynchronizationImpl@481efbaf >: javax.persistence.OptimisticLockException: org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [xxx.modules.domain.entity.User#118]
        at org.hibernate.ejb.AbstractEntityManagerImpl.wrapStaleStateException(AbstractEntityManagerImpl.java:1243) [:3.6.0.Final]
        at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1166) [:3.6.0.Final]
        at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1147) [:3.6.0.Final]
        at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1153) [:3.6.0.Final]
        at org.hibernate.ejb.AbstractEntityManagerImpl$3.mapManagedFlushFailure(AbstractEntityManagerImpl.java:1067) [:3.6.0.Final]
        at org.hibernate.transaction.synchronization.CallbackCoordinator.beforeCompletion(CallbackCoordinator.java:122) [:3.6.0.Final]
        at org.hibernate.transaction.synchronization.HibernateSynchronizationImpl.beforeCompletion(HibernateSynchronizationImpl.java:51) [:3.6.0.Final]
        at com.arjuna.ats.internal.jta.resources.arjunacore.SynchronizationImple.beforeCompletion(SynchronizationImple.java:97) [:6.0.0.Final]
        at com.arjuna.ats.arjuna.coordinator.TwoPhaseCoordinator.beforeCompletion(TwoPhaseCoordinator.java:274) [:6.0.0.Final]
        at com.arjuna.ats.arjuna.coordinator.TwoPhaseCoordinator.end(TwoPhaseCoordinator.java:94) [:6.0.0.Final]
        at com.arjuna.ats.arjuna.AtomicAction.commit(AtomicAction.java:159) [:6.0.0.Final]
        at com.arjuna.ats.internal.jta.transaction.arjunacore.TransactionImple.commitAndDisassociate(TransactionImple.java:1158) [:6.0.0.Final]
        at com.arjuna.ats.internal.jta.transaction.arjunacore.BaseTransaction.commit(BaseTransaction.java:119) [:6.0.0.Final]
        at com.arjuna.ats.jbossatx.BaseTransactionManagerDelegate.commit(BaseTransactionManagerDelegate.java:75) [:6.0.0.Final]
        at org.jboss.ejb3.tx2.impl.CMTTxInterceptor.endTransaction(CMTTxInterceptor.java:82) [:0.0.1]
        at org.jboss.ejb3.tx2.impl.CMTTxInterceptor.invokeInOurTx(CMTTxInterceptor.java:255) [:0.0.1]
        at org.jboss.ejb3.tx2.impl.CMTTxInterceptor.required(CMTTxInterceptor.java:349) [:0.0.1]
        at org.jboss.ejb3.tx2.impl.CMTTxInterceptor.invoke(CMTTxInterceptor.java:209) [:0.0.1]
        at org.jboss.ejb3.tx2.aop.CMTTxInterceptorWrapper.invoke(CMTTxInterceptorWrapper.java:52) [:0.0.1]
        at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102) [jboss-aop.jar:2.2.1.GA]
        at org.jboss.aspects.tx.TxPropagationInterceptor.invoke(TxPropagationInterceptor.java:76) [:1.0.0.GA]
        at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102) [jboss-aop.jar:2.2.1.GA]
        at org.jboss.ejb3.tx.NullInterceptor.invoke(NullInterceptor.java:42) [:1.0.3]
        at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102) [jboss-aop.jar:2.2.1.GA]
        at org.jboss.ejb3.security.Ejb3AuthenticationInterceptorv2.invoke(Ejb3AuthenticationInterceptorv2.java:182) [:1.7.17]
        at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102) [jboss-aop.jar:2.2.1.GA]
        at org.jboss.ejb3.ENCPropagationInterceptor.invoke(ENCPropagationInterceptor.java:41) [:1.7.17]
        at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102) [jboss-aop.jar:2.2.1.GA]
        at org.jboss.ejb3.BlockContainerShutdownInterceptor.invoke(BlockContainerShutdownInterceptor.java:67) [:1.7.17]
        at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102) [jboss-aop.jar:2.2.1.GA]
        at org.jboss.ejb3.core.context.CurrentInvocationContextInterceptor.invoke(CurrentInvocationContextInterceptor.java:47) [:1.7.17]
        at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102) [jboss-aop.jar:2.2.1.GA]
        at org.jboss.aspects.currentinvocation.CurrentInvocationInterceptor.invoke(CurrentInvocationInterceptor.java:67) [:1.0.1]
        at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102) [jboss-aop.jar:2.2.1.GA]
        at org.jboss.ejb3.interceptor.EJB3TCCLInterceptor.invoke(EJB3TCCLInterceptor.java:86) [:1.7.17]
        at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102) [jboss-aop.jar:2.2.1.GA]
        at org.jboss.ejb3.stateless.StatelessContainer.dynamicInvoke(StatelessContainer.java:392) [:1.7.17]
        at org.jboss.ejb3.session.InvokableContextClassProxyHack._dynamicInvoke(InvokableContextClassProxyHack.java:53) [:1.7.17]
        at org.jboss.aop.Dispatcher.invoke(Dispatcher.java:91) [jboss-aop.jar:2.2.1.GA]
        at org.jboss.aspects.remoting.AOPRemotingInvocationHandler.invoke(AOPRemotingInvocationHandler.java:82) [:1.0.1.GA]
        at org.jboss.remoting.ServerInvoker.invoke(ServerInvoker.java:898) [:6.0.0.Final]
        at org.jboss.remoting.transport.socket.ServerThread.completeInvocation(ServerThread.java:791) [:6.0.0.Final]
        at org.jboss.remoting.transport.socket.ServerThread.processInvocation(ServerThread.java:744) [:6.0.0.Final]
        at org.jboss.remoting.transport.socket.ServerThread.dorun(ServerThread.java:586) [:6.0.0.Final]
        at org.jboss.remoting.transport.socket.ServerThread.run(ServerThread.java:234) [:6.0.0.Final]
Caused by: org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [xxx.modules.domain.entity.User#118]
        at org.hibernate.persister.entity.AbstractEntityPersister.check(AbstractEntityPersister.java:1932) [:3.6.0.Final]
        at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:2576) [:3.6.0.Final]
        at org.hibernate.persister.entity.AbstractEntityPersister.updateOrInsert(AbstractEntityPersister.java:2476) [:3.6.0.Final]
        at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:2803) [:3.6.0.Final]
        at org.hibernate.action.EntityUpdateAction.execute(EntityUpdateAction.java:113) [:3.6.0.Final]
        at org.hibernate.engine.ActionQueue.execute(ActionQueue.java:273) [:3.6.0.Final]
        at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:265) [:3.6.0.Final]
        at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:185) [:3.6.0.Final]
        at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:321) [:3.6.0.Final]
        at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:51) [:3.6.0.Final]
        at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1216) [:3.6.0.Final]
        at org.hibernate.impl.SessionImpl.managedFlush(SessionImpl.java:383) [:3.6.0.Final]
        at org.hibernate.transaction.synchronization.CallbackCoordinator.beforeCompletion(CallbackCoordinator.java:117) [:3.6.0.Final]
        ... 39 more

如有任何帮助,我们不胜感激!

预先感谢,迈克尔

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2011-04-29 06:55:25

你是非常,非常你确定你已经设置TX为可序列化?因为这不应该发生在可序列化的事务上。

如果两个TX读取和修改可序列化事务中同一行,则oracle抛出ORA-08177

请检查hibernate是否真的将TX设置为可序列化。

编辑

您可以按照Jonas的建议进行操作,也可以通过获取底层连接并调用Connection.getIsolationLevel()从应用程序中进行检查。例如

代码语言:javascript
复制
Connection c = session.connection()
int level = c.getIsolationLevel()

编辑2

好的,既然您确认了连接上的隔离级别是SERIALILIZABLE,那么您是否可以检查:

  • 表示表正在使用innoDB引擎。
  • 按照Jonas的建议,从事务中的代码运行SELECT @@tx_isolation;。它应该返回SERIALIZABLE。这是为了检查连接是否确实在传播隔离级别。这有点偏执,但怎么办……
  • 检查您的代码是否只打开一个事务,并运行该TX中的所有事务。我刚刚手动测试了SERIALIZABLE隔离级别,它按预期工作(它会阻止任何试图读取同一行的TX )。
  • 最后手段:检查隔离级别SERIALIZABLE在您的MySQL installation.

上是否有效

备注:正如我之前提到的,MySQL将阻止任何试图从同一行读取数据的查询。这意味着,如果您有一些“公共表”,如国家、公司、用户等,许多TXs并发读取,这可能会使您的应用程序几乎按顺序运行,而不是并行运行。

票数 2
EN

Stack Overflow用户

发布于 2011-04-29 07:54:14

看起来您已经将Hibernate配置为使用optimistic concurrency control。这意味着您的用户表有一个版本字段,Hibernate会在每次行更新时增加该字段。

最有可能的情况是,您的事务从HTTP请求的开头开始,在HTTP响应的结尾结束。这意味着编辑用户的过程由两个事务组成:一个事务用于填充web表单,另一个事务用于保存更改。

在这种情况下,更改数据库的隔离级别不会有任何好处。你得到的最有可能的结果就是worse performance and scalability

拥有StaleObjectException并不是一件坏事,它反映了现实世界--人们确实偶尔会在同一件事情上工作,冲突可能会发生。问题是,当检测到冲突时,如何以最终用户满意的方式解决它?可以在没有用户帮助的情况下解决吗?

可能的策略可能是

  • 覆盖以前用户的更改(通常不是您想要的-因此需要并发控制),
  • 显示一条错误消息,要求用户刷新并再次执行他的更改,
  • 自动合并更改而不覆盖以前用户的更改(有时possible)
  • inform用户的数据过时,并为他提供一种手动合并其更改的方法

这完全取决于上下文。

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/5825161

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档