Spring и Hibernate неожиданно установили транзакцию только для чтения - PullRequest
12 голосов
/ 15 декабря 2010

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

Мы используем стандартный вид -> service -> dao Layering. Где каждый дао помечен @ Repository.

Это все работает 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.

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

Только перезапуск сервера восстанавливает работоспособность системы.

Вопрос в том, почему система переводит все транзакции в режим только для чтения / ручной сброс? Я гуглил это, но не смог найти решение.

Это наша весенняя и спящая конфигурация (показывается только соответствующая часть):

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

Это трассировка стека:

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)

Это все отлично работает

Ответы [ 7 ]

2 голосов
/ 02 марта 2012

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

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

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

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

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

2 голосов
/ 06 июля 2012

в вашем 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>
2 голосов
/ 07 января 2011

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

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

Звучит ли что-нибудь из этого?

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

1 голос
/ 16 июня 2012

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

Первый:

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

Второй:

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

, и мы сделали

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

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

1 голос
/ 23 августа 2011

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

1 голос
/ 15 декабря 2010

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

0 голосов
/ 31 октября 2011

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...