StaleObjectStateException高频更新 [英] StaleObjectStateException on high frequency updates
问题描述
我们使用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.beComplete - SynchronizationImple失败0:ffff7f000001:126a:4db9c7b0:74d,org.hibernate.transaction.synchronization.HibernateSynchronizationImpl@481efbaf>:javax.persistence.OptimisticLockException:org.hibernate.StaleObjectStateException:行被另一个事务更新或删除(或未保存的值映射不正确):[xxx.modules.domain.entity.User#118]
在org.hibernate.ejb.AbstractEntityManagerImpl.wrapStaleStateException(AbstractEntityManagerImpl.java:1243)[:3.6.0.Final]
在org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1147)[:3.6。org.hibernate.ejb.AbstractEntityManagerImpl.java:1166] [:3.6.0.Final]
。 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]
在org.hibernate.tran saction.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(Base Transaction.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.CMTTInterceptor.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]
在org.jboss.ejb3.BlockContainerShutdownInterceptor.invoke(BlockContainerShutdownInterceptor.java:67)[:1.7.17]
在org.jboss.aop .joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)[jboss-aop.jar:2.2.1.GA]
在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]
在org.jboss.aspects.currentinvocation.CurrentInvocationInterceptor.invoke(CurrentInvocationInterceptor.java:67)[:1.0.1]
在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 ]
在o rg.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]
在org.jboss.remoting.ServerInvoker.invoke(ServerInvoker.java:898)[:6.0.0.Final]
在org.jboss.remoting。 transport.socket.ServerThread.completeInvocation(ServerThread.java:791)[:6.0.0.Final]
在org.jboss.remoting.transport.socket.ServerThread.processInvocation(ServerThread.java:744)[:6.0 .0.Final]
在org.jboss.remoting.transport.socket.ServerThread.dorun(ServerThread.java:586)[:6.0.0.Final]
在org.jboss.remoting.transport .socket.ServerThread.run(ServerThread.java:234)[:6.0.0.Final]
导致:org.hibernate.StaleObjectStateException:行被其他事务更新或删除(或未保存值映射不正确):[xxx.modules.domain.entity.User#118]
org.hibernate.persister.entity.Abstract EntityPersister.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) [1: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
任何帮助表示感谢!
在此先感谢
Michael
非常,非常确定您已将TX设置为可序列化?因为这不应该发生在可序列化的事务上。如果两个TX在一个可序列化的事务中读取和修改同一行,那么oracle会抛出一个 ORA-08177 。
请检查hibernate是否将TX设置为可序列化。
Edit
您可以执行Jonas建议的操作,也可以通过获取底层连接并调用Connection.getIsolationLevel ()。例如
连接c = session.connection()
int level = c.getIsolationLevel()
编辑2
好的,既然您确认隔离级别的连接是SERIALILIZABLE,你可以检查:
$ b $ ul
SELECT @@ tx_isolation;
。它应该返回 SERIALIZABLE 。这是为了检查Connection是否实际传播隔离级别。这有点偏执,但该怎么办... We're using Hibernate 3.6.3.Final and MySQL 5.5.8 for a web application. The backend is running on a JBoss 6.0.0 Final server. Most of the time things work really well but occasionally we're getting a StaleObjectStateException. After a while of experimenting we figured out that it can be reproduced by sending requests to the backend with a high frequency (ie. clicking a button which triggers the request as fast as possible).
As far as I know the exception means that a domain object got fetched from the database and when Hibernate tried to persist it again it noticed that another transaction changed it in the meantime.
However as far as I understand databases the conflicting transactions should be isolated to an extent which prevents exactly this behavior. I explicitly changed the isolation level to SERIALIZABLE which guarantees repeatable reads and I disabled Hibernate caching. This should prevent the situation where one transaction sees different versions of the same domain object.
The full stack trace is:
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
Any help is appreciated!
Thanks in advance Michael
Are you very, very sure that you've set the TX to serializable? Because that should never happen on a serializable transaction.
If two TX read and modify the same row in a serializable transaction, then oracle throws an ORA-08177.
Please check that hibernate is actually setting the TX as serializable.
Edit
You can do what Jonas suggested or you can also check it from you application by getting the underlying connection and invoking Connection.getIsolationLevel(). For example
Connection c = session.connection()
int level = c.getIsolationLevel()
Edit 2
Ok, since you confirmed that the isolation level on the Connection is SERIALILIZABLE, could you check:
- That the tables are using the innoDB engine.
- As Jonas suggested, run
SELECT @@tx_isolation;
from your code while it is in the transaction. It should return SERIALIZABLE. This is to check that the Connection is actually propagating the isolation level. It's a bit paranoid, but what to do... - Check that your code only opens one transaction, and runs everything in that TX. I just tested the SERIALIZABLE isolation level manually and it works as expected (it blocks any TX trying to read the same row).
- Last resort: check that the isolation level SERIALIZABLE works on your MySQL installation.
NOTE: As I mentioned before, MySQL will block any queries trying to read from the same row. That means that if you have some "common tables" such as country, company, user, etc. that many TXs read concurrently, it might make your app run almost sequencially rather than parallelly.
这篇关于StaleObjectStateException高频更新的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!