Java Spring和Hibernate突然将事务设置为只读

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4448520/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-14 17:38:25  来源:igfitidea点击:

Spring and Hibernate suddenly set the transaction to readonly

javahibernatespringjbossspring-transactions

提问by aseesing

We have an application running on JBoss 4.2.3, using Spring 2.5.2 and Hibernate 3.2.6.ga. This is running on Linux JEE01 2.6.16.60-0.54.5-smp, using its own user. Writing to a Oracle 10G database on another machine.

我们有一个在 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 数据库。

We're using a standard view -> service -> dao layering. Where each dao is annotated with @Repository.

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

This is all running 24/7 without many problems, but every several days and sometimes a couple of times in one day the whole system goes into a bad state where nothing can be written to the database anymore. These stacktraces appear in the logs:

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

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.

We scanned the complete system, and there is one place in the system where the flushmode is temporarely set to MANUAL after which a finally block set its back to its original value. This is because we do not want to flush the state to the database before this query runs. So we can't change this very easily. The normal FlushMode is set to AUTO and on several places we temporarily set it to COMMIT and switching it back to the default again.

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

Only a server restart restores the system back to working order.

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

The question is: why does the system set all transactions to readonly/manual flush mode? I googled this but could not find a solution.

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

This is our spring and hibernate configuration (only relevants part showing):

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

     <bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
  <property name="dataSource">
   <ref bean="datasourceName" />
  </property>
  <property name="configLocation">
   <value>classpath:hibernate.cfg.xml</value>
  </property>
 </bean>

 <bean id="hibernateInterceptor"
  class="org.springframework.orm.hibernate3.HibernateInterceptor">
  <property name="sessionFactory" ref="sessionFactory" />
 </bean>

 <tx:annotation-driven transaction-manager="txManager" /> 

 <tx:advice id="txAdvice" transaction-manager="txManager" >
  <!-- the transactional semantics... -->
  <tx:attributes >
   <!-- all methods starting with 'get' are read-only -->
   <tx:method name="approve*" read-only="false"
    propagation="REQUIRED" rollback-for="java.lang.Exception" />
   <tx:method name="update*" read-only="false"   
    propagation="REQUIRED" rollback-for="java.lang.Exception"/>
   <tx:method name="save*" read-only="false"
    propagation="REQUIRED" rollback-for="java.lang.Exception"/>
            <tx:method name="delete*" read-only="false"
    propagation="REQUIRED" rollback-for="java.lang.Exception" />
   <!-- other methods use the default transaction settings (see below) -->
   <tx:method name="*" read-only="true" propagation="REQUIRED" />
  </tx:attributes>
 </tx:advice>

 <aop:config>
  <aop:pointcut id="serviceMethods"
   expression="execution(* com.myapplication.service.*.*(..))"  />
  <aop:advisor advice-ref="txAdvice" 
   pointcut-ref="serviceMethods" />
 </aop:config>

 <bean id="txManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager" >
  <property name="sessionFactory" ref="sessionFactory"  />
 </bean>

-- end of spring config --

-- hibernate configuation --
<hibernate-configuration>
    <session-factory name="">
        <property name="dialect">org.hibernate.dialect.Oracle10gDialect</property>
        <property name="show_sql">false</property>
        <property name="use_outer_join">false</property>
        <property name="hibernate.cache.use_query_cache">true</property>
        <property name="hibernate.cache.use_second_level_cache">true</property>
        <property name="cache.provider_class">org.hibernate.cache.EhCacheProvider</property>
        <property name="hibernate.connection.SetBigStringTryClob">true</property>
        <property name="hibernate.jdbc.batch_size">0</property>
    </session-factory>
    <mapping ----/>
</hibernate-configuration>

This is the stacktrace:

这是堆栈跟踪:

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.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)

This all works fine

这一切正常

回答by Konstantin

My guess would be that it is Spring that does this for you. I seem to remember this being done by the OpenSessionInViewFilter in Spring. Are you using that?

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

回答by Binil Thomas

This exception comes from the following code in Spring's HibernateTemplate class:

此异常来自 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.");
    }
}

The rationale for this check is explainedas:

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

This is a new consistency check introduced in Spring 1.1.

Invoking HibernateTemplate's save/update/delete methods on a Spring-managed Session in FlushMode.NEVER is potentially dangerous: It means that you are:

  • either doing this in a Spring-managed read-only transaction, which will never flush the Hibernate Session, i.e. never flush your save/update/delete calls. The new check makes you aware that you won't persist your changes in that situation.

  • or working with some other thread-bound Session in FlushMode.NEVER, for example the OpenSessionInViewFilter. If you're overriding closeSession there to flush after view rendering, you should override getSession too, setting the Session to FlushMode.AUTO.

I strongly recommend against the latter, though. If you're using OpenSessionInViewFilter, combine it with middle tier transactions rather than let the filter flush at request completion.

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

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

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

  • 或在 FlushMode.NEVER 中使用其他一些线程绑定会话,例如 OpenSessionInViewFilter。如果您在视图渲染后覆盖 closeSession 以刷新那里,您也应该覆盖 getSession,将 Session 设置为 FlushMode.AUTO。

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

Does any of this ring a bell?

这有没有敲响警钟?

It is possible that there is some bug in the your code or in Spring ORM. To disable this check you can call setCheckWriteOperations(false).

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

回答by aseesing

This is from memory so it's not very detailed. There are two ways of setting the flushmode. I think a Spring way and a Hibernate way. Because we were using the (Spring?) way only once and the Hibernate way all the other times it made the internal state of Hibernate wrong somehow. Which created my problems. When we made everything consistent the problem went away.

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

回答by smp7d

Because you are using an execution pointcut on the implementation and you are utilizing a generic DAO, perhaps this is not handling transactions as you would expect and this is just a manifestation of the real issue.

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

Please ensure that Spring is proxying your DAO as you expect on that call. You may need to use the target(beanid)syntax to enable the proper transactions for your generic DAO.

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

回答by horaceman

aseesing, i check your configuration. in spring context part, you use

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

<!-- other methods use the default transaction settings (see below) -->
   <tx:method name="*" read-only="true" propagation="REQUIRED" />

usually, only some accesses are read-only types, such as get/find/query, etc. i suggest use

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

<!--default is required transaction-->
<tx:method name="*"/>

another thing is, do you use opensessioninview pattern? flush mode could be set in opensessioninview properly. you can use filter in web.xml or use spring interceptor in application context config.

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

回答by erez

I encountered this when 2 "baseTransactionProxy" were used from 1 bean :

我在 1 个 bean 中使用了 2 个“baseTransactionProxy”时遇到了这个问题:

First :

第一的 :

<bean id="generalDao" parent="baseTransactionProxy">
    <property name="target">
        <bean class="com...GeneralDao" parent="baseDAO" />
    </property>
</bean>

Second :

第二 :

<bean id="ruleDao" parent="baseTransactionProxy">
    <property name="target">
        <bean class="com...RuleDao" parent="baseDAO">
            <constructor-arg ref="generalDao"></constructor-arg>
        </bean>
    </property>
</bean>

and we did

我们做到了

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

Not sure if it helps, but it seems that its not good to mix 2 different "baseTransactionProxy" at the same proxy call...

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

回答by nabil

in your web.xml put :

在您的 web.xml 中放置:

<filter>
    <filter-name>openSessionInViewFilter</filter-name>
    <filter-class>org.springframework.orm.hibernate3.support.OpenSessionInViewFilter</filter-class>
    <init-param>
        <param-name>flushMode</param-name>
        <param-value>AUTO</param-value>
    </init-param>
</filter>