java - 高频更新时出现 StaleObjectStateException

标签 java mysql hibernate jboss6.x

我们将 Hibernate 3.6.3.Final 和 MySQL 5.5.8 用于 Web 应用程序。后端在 JBoss 6.0.0 Final 服务器上运行。大多数时候一切都很好,但偶尔我们会收到 StaleObjectStateException。经过一段时间的试验后,我们发现可以通过向后端发送高频请求(即点击尽可能快地触发请求的按钮)来重现它。

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

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

完整的堆栈跟踪是:

    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

感谢任何帮助!

提前致谢 迈克尔

最佳答案

您似乎已将 Hibernate 配置为使用 optimistic concurrency control .这意味着您的 User 表有一个版本字段,Hibernate 在每次更新行时递增。

您的事务很可能从 HTTP 请求开始,到 HTTP 响应结束。这意味着编辑用户的过程包含两个事务:一个事务用于填充 Web 表单,另一个事务用于保存更改。

在那种情况下,更改数据库的隔离级别将无济于事。您很可能会得到的是 worse performance and scalability .

StaleObjectException 并不是一件坏事。它反射(reflect)了现实世界——人们确实偶尔会做同样的事情,并且可能会发生冲突。问题是,当检测到冲突时,您如何以令最终用户满意的方式解决它?没有用户的协助能否解决?

可能的策略是

  • 覆盖之前用户的更改 (通常不是您想要的 - 因此需要并发控制),

  • 显示错误消息询问 用户刷新并执行他的 再次改变,

  • 自动合并更改而不覆盖以前用户的更改(有时可能)

  • 通知用户有关过时的信息 他的数据,并为他提供一种方法来手动合并他的更改

这完全取决于上下文。

关于java - 高频更新时出现 StaleObjectStateException,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5825161/

相关文章:

java - 存储口袋妖怪类型游戏信息的最佳方式是什么?

mysql - 如何对两个单独的表使用 mysql WHERE IN

MySQL - SELECT ...(子查询)AS 字段名

mysql - 查询 IN() 未按预期工作

java - Hibernate:保存多个关联问题

java - JPA/Hibernate 仅加入选定的一列

java - Spring mvc 映射问题

java - java中如何停止一个线程

java - 在 ListView 中实现搜索功能时出错

java - Java中的ExceptionInInitializerError