并发更新期间Hibernate StaleObjectStateException

lyn*_*ynn 2 spring hibernate java-ee

我在Java J2EE Web应用程序中使用Hibernate 3.5.2和Spring Core 3.0.1.当单独的用户同时更新同一记录时,我得到一个StaleObjectStateExcpetion.事务由javax.persistence.EntityManager管理.以下是我创建问题的步骤.

  1. User1登录到应用程序
  2. User2登录到应用程序
  3. User1开始编辑recordA
  4. User2开始编辑recordA
  5. User1保存记录A.
  6. User2保存记录A.
  7. 抛出org.hibernate.StaleObjectStateException(见下文)

我已经读过这是由数据库中更新的版本号与该特定实体当前在内存中的版本号之间的差异引起的.但是,当我尝试使用查询或使用EntityManager.find()方法从数据库中查找当前版本号以从数据库中获取最新信息时,我发现版本号没有区别.我还试图手动并使用entityManager.merge(object)方法合并旧实体和新实体之间的更改,但没有运气.有关如何解决此问题的任何想法?

13290 [http-8080-7] ERROR org.hibernate.event.def.AbstractFlushingEventListener  - Could not synchronize database state with session
org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [edu.pitt.nmrl.med.domain.medical.MedicalHistory#362]
    at org.hibernate.persister.entity.AbstractEntityPersister.check(AbstractEntityPersister.java:1934)
    at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:2578)
    at org.hibernate.persister.entity.AbstractEntityPersister.updateOrInsert(AbstractEntityPersister.java:2478)
    at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:2805)
    at org.hibernate.action.EntityUpdateAction.execute(EntityUpdateAction.java:114)
    at org.hibernate.engine.ActionQueue.execute(ActionQueue.java:268)
    at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:260)
    at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:180)
    at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:321)
    at org.hibernate.event.def.DefaultAutoFlushEventListener.onAutoFlush(DefaultAutoFlushEventListener.java:64)
    at org.hibernate.impl.SessionImpl.autoFlushIfRequired(SessionImpl.java:1175)
    at org.hibernate.impl.SessionImpl.list(SessionImpl.java:1251)
    at org.hibernate.impl.QueryImpl.list(QueryImpl.java:102)
    at org.hibernate.ejb.QueryImpl.getResultList(QueryImpl.java:241)
    at org.hibernate.ejb.criteria.CriteriaQueryCompiler$3.getResultList(CriteriaQueryCompiler.java:260)
    at edu.pitt.nmrl.med.services.SurveyService.checkMedicalDup(SurveyService.java:613)
    at edu.pitt.nmrl.med.services.SurveyService.save(SurveyService.java:790)
    at edu.pitt.nmrl.med.services.SurveyService$$FastClassByCGLIB$$b6424505.invoke(<generated>)
    at net.sf.cglib.proxy.MethodProxy.invoke(MethodProxy.java:191)
    at org.springframework.aop.framework.Cglib2AopProxy$CglibMethodInvocation.invokeJoinpoint(Cglib2AopProxy.java:692)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150)
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:110)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
    at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:89)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
    at org.springframework.aop.framework.Cglib2AopProxy$DynamicAdvisedInterceptor.intercept(Cglib2AopProxy.java:625)
    at edu.pitt.nmrl.med.services.SurveyService$$EnhancerByCGLIB$$5831613c.save(<generated>)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at org.jboss.el.util.ReflectionUtil.invokeMethod(ReflectionUtil.java:328)
    at org.jboss.el.util.ReflectionUtil.invokeMethod(ReflectionUtil.java:273)
    at org.jboss.el.parser.AstMethodSuffix.getValue(AstMethodSuffix.java:59)
    at org.jboss.el.parser.AstMethodSuffix.invoke(AstMethodSuffix.java:65)
    at org.jboss.el.parser.AstValue.invoke(AstValue.java:96)
    at org.jboss.el.MethodExpressionImpl.invoke(MethodExpressionImpl.java:276)
    at com.sun.facelets.el.TagMethodExpression.invoke(TagMethodExpression.java:68)
    at javax.faces.component.MethodBindingMethodExpressionAdapter.invoke(MethodBindingMethodExpressionAdapter.java:88)
    at com.sun.faces.application.ActionListenerImpl.processAction(ActionListenerImpl.java:102)
    at javax.faces.component.UICommand.broadcast(UICommand.java:387)
    at javax.faces.component.UIViewRoot.broadcastEvents(UIViewRoot.java:475)
    at javax.faces.component.UIViewRoot.processApplication(UIViewRoot.java:756)
    at com.sun.faces.lifecycle.InvokeApplicationPhase.execute(InvokeApplicationPhase.java:82)
    at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:100)
    at com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:118)
    at com.icesoft.faces.webapp.http.core.JsfLifecycleExecutor.apply(JsfLifecycleExecutor.java:18)
    at com.icesoft.faces.webapp.http.core.ReceiveSendUpdates.renderCycle(ReceiveSendUpdates.java:132)
    at com.icesoft.faces.webapp.http.core.ReceiveSendUpdates.service(ReceiveSendUpdates.java:74)
    at com.icesoft.faces.webapp.http.core.RequestVerifier.service(RequestVerifier.java:31)
    at com.icesoft.faces.webapp.http.common.standard.PathDispatcherServer.service(PathDispatcherServer.java:24)
    at com.icesoft.faces.webapp.http.servlet.BasicAdaptingServlet.service(BasicAdaptingServlet.java:16)
    at com.icesoft.faces.webapp.http.servlet.PathDispatcher.service(PathDispatcher.java:23)
    at com.icesoft.faces.webapp.http.servlet.SessionDispatcher.service(SessionDispatcher.java:53)
    at com.icesoft.faces.webapp.http.servlet.SessionVerifier.service(SessionVerifier.java:26)
    at com.icesoft.faces.webapp.http.servlet.PathDispatcher.service(PathDispatcher.java:23)
    at com.icesoft.faces.webapp.http.servlet.MainServlet.service(MainServlet.java:131)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
    at com.icesoft.faces.webapp.xmlhttp.BlockingServlet.service(BlockingServlet.java:56)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:343)
    at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:109)
    at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:83)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:355)
    at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:97)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:355)
    at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:100)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:355)
    at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:78)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:355)
    at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:54)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:355)
    at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:35)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:355)
    at org.springframework.security.web.authentication.www.BasicAuthenticationFilter.doFilter(BasicAuthenticationFilter.java:177)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:355)
    at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:188)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:355)
    at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:105)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:355)
    at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:79)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:355)
    at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:149)
    at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:237)
    at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:167)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:127)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:298)
    at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:857)
    at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:588)
    at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:489)
    at java.lang.Thread.run(Thread.java:619)
Jan 23, 2012 12:07:16 PM com.sun.faces.application.ActionListenerImpl processAction
SEVERE: javax.persistence.OptimisticLockException
javax.faces.el.EvaluationException: javax.persistence.OptimisticLockException
    at javax.faces.component.MethodBindingMethodExpressionAdapter.invoke(MethodBindingMethodExpressionAdapter.java:102)
    at com.sun.faces.application.ActionListenerImpl.processAction(ActionListenerImpl.java:102)
    at javax.faces.component.UICommand.broadcast(UICommand.java:387)
    at javax.faces.component.UIViewRoot.broadcastEvents(UIViewRoot.java:475)
    at javax.faces.component.UIViewRoot.processApplication(UIViewRoot.java:756)
    at com.sun.faces.lifecycle.InvokeApplicationPhase.execute(InvokeApplicationPhase.java:82)
    at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:100)
    at com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:118)
    at com.icesoft.faces.webapp.http.core.JsfLifecycleExecutor.apply(JsfLifecycleExecutor.java:18)
    at com.icesoft.faces.webapp.http.core.ReceiveSendUpdates.renderCycle(ReceiveSendUpdates.java:132)
    at com.icesoft.faces.webapp.http.core.ReceiveSendUpdates.service(ReceiveSendUpdates.java:74)
    at com.icesoft.faces.webapp.http.core.RequestVerifier.service(RequestVerifier.java:31)
    at com.icesoft.faces.webapp.http.common.standard.PathDispatcherServer.service(PathDispatcherServer.java:24)
    at com.icesoft.faces.webapp.http.servlet.BasicAdaptingServlet.service(BasicAdaptingServlet.java:16)
    at com.icesoft.faces.webapp.http.servlet.PathDispatcher.service(PathDispatcher.java:23)
    at com.icesoft.faces.webapp.http.servlet.SessionDispatcher.service(SessionDispatcher.java:53)
    at com.icesoft.faces.webapp.http.servlet.SessionVerifier.service(SessionVerifier.java:26)
    at com.icesoft.faces.webapp.http.servlet.PathDispatcher.service(PathDispatcher.java:23)
    at com.icesoft.faces.webapp.http.servlet.MainServlet.service(MainServlet.java:131)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
    at com.icesoft.faces.webapp.xmlhttp.BlockingServlet.service(BlockingServlet.java:56)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:343)
    at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:109)
    at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:83)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:355)
    at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:97)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:355)
    at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:100)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:355)
    at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:78)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:355)
    at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:54)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:355)
    at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:35)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:355)
    at org.springframework.security.web.authentication.www.BasicAuthenticationFilter.doFilter(BasicAuthenticationFilter.java:177)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:355)
    at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:188)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:355)
    at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:105)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:355)
    at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:79)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:355)
    at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:149)
    at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:237)
    at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:167)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:127)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:298)
    at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:857)
    at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:588)
    at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:489)
    at java.lang.Thread.run(Thread.java:619)
Caused by: javax.persistence.OptimisticLockException
    at org.hibernate.ejb.AbstractEntityManagerImpl.wrapStaleStateException(AbstractEntityManagerImpl.java:1261)
    at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1187)
    at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1168)
    at org.hibernate.ejb.QueryImpl.getResultList(QueryImpl.java:250)
    at org.hibernate.ejb.criteria.CriteriaQueryCompiler$3.getResultList(CriteriaQueryCompiler.java:260)
    at edu.pitt.nmrl.med.services.SurveyService.checkMedicalDup(SurveyService.java:613)
    at edu.pitt.nmrl.med.services.SurveyService.save(SurveyService.java:790)
    at edu.pitt.nmrl.med.services.SurveyService$$FastClassByCGLIB$$b6424505.invoke(<generated>)
    at net.sf.cglib.proxy.MethodProxy.invoke(MethodProxy.java:191)
    at org.springframework.aop.framework.Cglib2AopProxy$CglibMethodInvocation.invokeJoinpoint(Cglib2AopProxy.java:692)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150)
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:110)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
    at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:89)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
    at org.springframework.aop.framework.Cglib2AopProxy$DynamicAdvisedInterceptor.intercept(Cglib2AopProxy.java:625)
    at 
Run Code Online (Sandbox Code Playgroud)

Tom*_*icz 13

以下是乐观锁定的工作原理:

  1. User1开始编辑recordA - recordA从数据库加载版本V
  2. User2开始编辑recordA - recordA从数据库加载版本V- 与上面完全相同
  3. User1保存记录A - recordA已保存,但Hibernate透明地添加:

    UPDATE
    ...
    SET V = V + 1
    WHERE version = V
    
    Run Code Online (Sandbox Code Playgroud)

    此时,Hibernate会验证从数据库返回的已修改记录的数量是否正好为1.如果查询修改了0记录,则表示该WHERE version = V子句未得到满足.在这种情况下一切都很好,版本设置为V + 1(重要!)

  4. User2保存记录A - Hibernate完全相同,仍然V在内存中保留旧版本号.不幸的是,在这种情况下,WHERE version = V由于版本现在不满足条件V + 1.查询返回0个修改过的记录,这使得Hibernate相信(正确地)在此期间修改了记录.这就是例外情况.

所以你对此能做些什么?只需捕获异常并:

  1. 重复整个事务,但重新加载实体(使用新版本V + 1),以便在更新后不会丢失最后一个更改

  2. 或者在此期间通知用户该对象已被修改,以便她可以检查新版本.

根据您的使用情况,两种解决方案都很好.