Spring и Hibernate неожиданно установили транзакцию только для чтения

У нас есть приложение, работающее на 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. Где каждый дао аннотируется с @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.

Мы проверили всю систему, и есть одно место в системе, где флешмод временно установлен в MANUAL, после чего блок finally вернется к исходному значению. Это связано с тем, что мы не хотим очищать состояние до базы данных до запуска этого запроса. Поэтому мы не можем изменить это очень легко. Нормальный FlushMode установлен в AUTO, и в нескольких местах мы временно установили его в COMMIT и снова вернули его по умолчанию.

Только перезагрузка сервера возвращает систему обратно в рабочий режим.

Вопрос в следующем: почему система устанавливает все транзакции для режима readonly/manual flush? Я googled это, но не мог найти решение.

Это наша конфигурация Spring и спящий режим (отображается только часть релевантов):

<bean id="sessionFactory">
 <property name="dataSource">
 <ref bean="datasourceName">
 </ref></property>
 <property name="configLocation">
 <value>classpath:hibernate.cfg.xml</value>
 </property>
 </bean>
 <bean id="hibernateInterceptor">
 <property name="sessionFactory" ref="sessionFactory">
 </property></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:method></tx:method></tx:method></tx:method></tx:method></tx:attributes>
 </tx:advice>
 <aop:config>
 <aop:pointcut id="serviceMethods" expression="execution(* com.myapplication.service.*.*(..))">
 <aop:advisor advice-ref="txAdvice" pointcut-ref="serviceMethods">
 </aop:advisor></aop:pointcut></aop:config>
 <bean id="txManager">
 <property name="sessionFactory" ref="sessionFactory">
 </property></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>
 </hibernate-configuration>
</tx:annotation-driven>

Это 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$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 $********.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.*************************.invoke(*************************.java:107)
at org.acegisecurity.intercept.web.*************************.doFilter(*************************.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)

Все это прекрасно работает

7 ответов

в вашем web.xml put:

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


aseesing, я проверю вашу конфигурацию. в контексте spring, вы используете

<!-- other methods use the default transaction settings (see below) -->

обычно, только некоторые обращения доступны только для чтения, такие как get/find/query и т.д. Я предлагаю использовать

Другое дело, используете ли вы шаблон opensessioninview? режим флеша может быть установлен в opensessioninview должным образом. вы можете использовать фильтр в web.xml или использовать перехватчик spring в конфигурации контекста приложения.


Это исключение исходит из следующего кода в 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.

Вызов HibernateTemplate сохранять/обновлять/удалять Spring управляемая сессия в FlushMode.NEVER потенциально Опасно: это означает, что вы:

  • либо делает это в Spring -управленном транзакции только для чтения, которая будет никогда не сбрасывайте сеанс Hibernate, т.е. никогда не сохранять/обновлять/удалять вызовы. Новый проверьте, что вы не знаете сохраняйте свои изменения в этом ситуация.

  • или работа с некоторыми другими связанная с потоком сессия в FlushMode.NEVER, например OpenSessionInViewFilter. Если вы переопределить closeSession там для флеша после рендеринга представления, вы должны переопределить getSession, установив Сессия для FlushMode.AUTO.

Я сильно рекомендую против последнего. Если вы используете OpenSessionInViewFilter, объединить его с транзакциями среднего уровня чем разрешить фильтрацию по запросу завершение.

Есть ли какое-либо из этих звонков?

Возможно, что есть ошибка в коде или в Spring ORM. Чтобы отключить эту проверку, вы можете вызвать setCheckWriteOperations(false).


Я столкнулся с этим, когда 2 "baseTransactionProxy" были использованы из 1 bean:

Сначала:

<bean id="generalDao" parent="baseTransactionProxy">
 <property name="target">
 <bean parent="baseDAO">
 </bean></property>
</bean>

Второе:

<bean id="ruleDao" parent="baseTransactionProxy">
 <property name="target">
 <bean parent="baseDAO">
 
 </bean>
 </property>
</bean>

и мы сделали

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

Не уверен, что это помогает, но кажется, что его неплохо смешивать 2 разных "baseTransactionProxy" при одном и том же прокси-вызове...


Это из памяти, поэтому он не очень подробный. Существует два способа установки флешмода. Я думаю, что способ Spring и способ Hibernate. Поскольку мы использовали способ (Spring?) Только один раз, а режим Hibernate - все остальные времена, это сделало внутреннее состояние Hibernate как-то неправильным. Который создал мои проблемы. Когда мы все согласовали, проблема исчезла.


Я предполагаю, что это Spring делает это для вас. Кажется, я помню, что это делается с помощью OpenSessionInViewFilter в Spring. Вы используете это?


Поскольку вы используете pointcut выполнения для реализации и используете общий DAO, возможно, это не обработка транзакций, как вы ожидали бы, и это всего лишь проявление реальной проблемы.

Пожалуйста, убедитесь, что Spring проксирует ваш DAO, как вы ожидаете в этом вызове. Возможно, вам понадобится использовать синтаксис target(beanid), чтобы включить надлежащие транзакции для вашего общего DAO.

licensed under cc by-sa 3.0 with attribution.