Spring和Hibernate突然将事务设置为readonly

我们有一个在JBoss 4.2.3上运行的应用程序,使用Spring 2.5.2和Hibernate 3.2.6.ga. 这是在Linux JEE01 2.6.16.60-0.54.5-smp上运行,使用自己的用户。 在另一台机器上写入Oracle 10G数据库。

我们使用标准视图 – >服务 – > dao分层。 每个dao都用@Repository注释。

这一切都是全天候运行而没有太多问题,但是每隔几天,有时几天,整个系统都会进入一个糟糕的状态,再也无法将任何内容写入数据库。 这些堆栈跟踪显示在日志中:

org.springframework.dao.InvalidDataAccessApiUsageException: Write operations are not allowed in read-only mode (FlushMode.NEVER/MANUAL): Turn your Session into FlushMode.COMMIT/AUTO or remove 'readOnly' marker from transaction definition. 

我们扫描了整个系统,系统中有一个位置,其中flushmode临时设置为MANUAL,之后finally块将其设置回原始值。 这是因为我们不希望在运行此查询之前将状态刷新到数据库。 所以我们不能轻易改变这一点。 普通的FlushMode设置为AUTO,在几个地方我们暂时将其设置为COMMIT并再次将其切换回默认值。

只有服务器重新启动才能将系统恢复到正常工作状态。

问题是:为什么系统将所有事务设置为只读/手动刷新模式? 我用Google搜索了但无法找到解决方案。

这是我们的spring和hibernate配置(仅显示相关部分):

       classpath:hibernate.cfg.xml                          -- end of spring config -- -- hibernate configuation --   org.hibernate.dialect.Oracle10gDialect false false true true org.hibernate.cache.EhCacheProvider true 0    

这是堆栈跟踪:

 org.springframework.dao.InvalidDataAccessApiUsageException: Write operations are not allowed in read-only mode (FlushMode.NEVER/MANUAL): Turn your Session into FlushMode.COMMIT/AUTO or remove 'readOnly' marker from transaction definition. at org.springframework.orm.hibernate3.HibernateTemplate.checkWriteOperationAllowed(HibernateTemplate.java:1137) at org.springframework.orm.hibernate3.HibernateTemplate$16.doInHibernate(HibernateTemplate.java:701) at org.springframework.orm.hibernate3.HibernateTemplate.execute(HibernateTemplate.java:374) at org.springframework.orm.hibernate3.HibernateTemplate.saveOrUpdate(HibernateTemplate.java:699) at nl.company.myapp.dao.impl.GenericDAOImpl.save(GenericDAOImpl.java:94) at nl.company.myapp.dao.impl.CallDAOImpl.save(CallDAOImpl.java:266) at nl.company.myapp.dao.impl.CallDAOImpl.save(CallDAOImpl.java:47) at nl.company.myapp.service.impl.CallServiceImpl.saveCall(CallServiceImpl.java:98) 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:592) at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:310) at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:182) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:149) at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:106) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:171) at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:90) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:171) at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:204) at $Proxy142.saveCall(Unknown Source) at nl.company.myapp.view.bean.call.CallDetailBean.doSave(CallDetailBean.java:319) at nl.company.myapp.view.bean.EditModeAwareBean.save(EditModeAwareBean.java:151) at sun.reflect.GeneratedMethodAccessor472.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:592) at org.apache.el.parser.AstValue.invoke(AstValue.java:131) at org.apache.el.MethodExpressionImpl.invoke(MethodExpressionImpl.java:276) at com.sun.facelets.el.TagMethodExpression.invoke(TagMethodExpression.java:68) at org.apache.myfaces.trinidad.component.MethodExpressionMethodBinding.invoke(MethodExpressionMethodBinding.java:46) at com.sun.faces.application.ActionListenerImpl.processAction(ActionListenerImpl.java:102) at org.apache.myfaces.trinidad.component.UIXCommand.broadcast(UIXCommand.java:190) at javax.faces.component.UIViewRoot.broadcastEvents(UIViewRoot.java:458) at javax.faces.component.UIViewRoot.processApplication(UIViewRoot.java:763) 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 javax.faces.webapp.FacesServlet.service(FacesServlet.java:265) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206) at nl.company.myapp.view.audit.AuditFilter.doFilter(AuditFilter.java:88) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206) at org.apache.myfaces.trinidadinternal.webapp.TrinidadFilterImpl._invokeDoFilter(TrinidadFilterImpl.java:238) at org.apache.myfaces.trinidadinternal.webapp.TrinidadFilterImpl._doFilterImpl(TrinidadFilterImpl.java:195) at org.apache.myfaces.trinidadinternal.webapp.TrinidadFilterImpl.doFilter(TrinidadFilterImpl.java:138) at org.apache.myfaces.trinidad.webapp.TrinidadFilter.doFilter(TrinidadFilter.java:92) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206) at org.acegisecurity.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:265) at org.acegisecurity.intercept.web.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:107) at org.acegisecurity.intercept.web.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:72) at org.acegisecurity.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:275) at org.acegisecurity.ui.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:124) at org.acegisecurity.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:275) at org.acegisecurity.providers.anonymous.AnonymousProcessingFilter.doFilter(AnonymousProcessingFilter.java:125) at org.acegisecurity.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:275) at org.acegisecurity.wrapper.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:81) at org.acegisecurity.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:275) at org.acegisecurity.ui.AbstractProcessingFilter.doFilter(AbstractProcessingFilter.java:271) at org.acegisecurity.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:275) at org.acegisecurity.ui.logout.LogoutFilter.doFilter(LogoutFilter.java:110) at org.acegisecurity.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:275) at org.acegisecurity.context.HttpSessionContextIntegrationFilter.doFilter(HttpSessionContextIntegrationFilter.java:249) at org.acegisecurity.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:275) at org.acegisecurity.util.FilterChainProxy.doFilter(FilterChainProxy.java:149) at org.acegisecurity.util.FilterToBeanProxy.doFilter(FilterToBeanProxy.java:98) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206) at org.jboss.web.tomcat.filters.ReplyHeaderFilter.doFilter(ReplyHeaderFilter.java:96) 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:230) at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:175) at org.jboss.web.tomcat.security.SecurityAssociationValve.invoke(SecurityAssociationValve.java:182) at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:432) at org.jboss.web.tomcat.security.JaccContextValve.invoke(JaccContextValve.java:84) at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:127) at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102) at org.jboss.web.tomcat.service.jca.CachedConnectionValve.invoke(CachedConnectionValve.java:157) at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109) at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:262) at org.apache.coyote.ajp.AjpProcessor.process(AjpProcessor.java:437) at org.apache.coyote.ajp.AjpProtocol$AjpConnectionHandler.process(AjpProtocol.java:366) at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:446) at java.lang.Thread.run(Thread.java:595) 

一切正常

此exception来自Spring的HibernateTemplate类中的以下代码:

 protected void checkWriteOperationAllowed(Session session) throws InvalidDataAccessApiUsageException { if (isCheckWriteOperations() && getFlushMode() != FLUSH_EAGER && session.getFlushMode().lessThan(FlushMode.COMMIT)) { throw new InvalidDataAccessApiUsageException( "Write operations are not allowed in read-only mode (FlushMode.NEVER/MANUAL): "+ "Turn your Session into FlushMode.COMMIT/AUTO or remove 'readOnly' marker from transaction definition."); } } 

此检查的基本原理解释如下:

这是Spring 1.1中引入的新一致性检查。

在FlushMode.NEVER中的Spring管理的会话上调用HibernateTemplate的保存/更新/删除方法是有潜在危险的:这意味着您:

  • 或者在Spring管理的只读事务中执行此操作,该事务永远不会刷新Hibernate会话,即永远不会刷新您的保存/更新/删除调用。 新检查会让您意识到在这种情况下您不会保留更改。

  • 或者使用FlushMode.NEVER中的其他线程绑定Session,例如OpenSessionInViewFilter。 如果你在视图渲染后重写了closeSession,那么你应该覆盖getSession,将Session设置为FlushMode.AUTO。

不过,我强烈建议不要使用后者。 如果您正在使用OpenSessionInViewFilter,请将其与中间层事务相结合,而不是在请求完成时让filter刷新。

这有什么响铃?

您的代码或Spring ORM中可能存在一些错误。 要禁用此检查,您可以调用setCheckWriteOperations(false)

aseesing,我检查你的配置。 在spring的上下文部分,你使用

   

通常,只有一些访问是只读类型,例如get / find / query等。我建议使用

   

另一件事是,你使用opensessioninview模式吗? 刷新模式可以在opensessioninview中正确设置。 您可以在web.xml中使用filter或在应用程序上下文配置中使用spring拦截器。

在你的web.xml中放:

  openSessionInViewFilter org.springframework.orm.hibernate3.support.OpenSessionInViewFilter  flushMode AUTO   

我的猜测是Spring会为你做这件事。 我似乎记得这是由Spring中的OpenSessionInViewFilter完成的。 你在用它吗?

这是来自记忆所以它不是很详细。 有两种设置flushmode的方法。 我认为是Spring方式和Hibernate方式。 因为我们只使用(Spring?)方式一次,而Hibernate方式所有其他时间都以某种方式使Hibernate的内部状态错误。 这造成了我的问题。 当我们使一切都保持一致时,问题就消失了。

当从1个bean使用2个“baseTransactionProxy”时我遇到了这个:

第一:

      

第二:

        

我们做到了

 class ruleDao{ generalDao.generalSaveOrUpdateAll(hbms); // OK HERE saveOrUpdateAll(otherHbms); //Exception here } 

不确定它是否有帮助,但似乎在同一个代理调用中混合2个不同的“baseTransactionProxy”并不好……

因为您在实现上使用执行切入点并且您正在使用通用DAO,所以这可能不像您期望的那样处理事务,这只是真实问题的表现。

请确保Spring按照您的电话预期代理您的DAO。 您可能需要使用target(beanid)语法为通用DAO启用正确的事务。