Spring et Hibernate ont soudainement défini la transaction en lecture seule

Nous avons une application fonctionnant sur JBoss 4.2.3, utilisant Spring 2.5.2 et Hibernate 3.2.6.ga. Cela fonctionne sur Linux JEE01 2.6.16.60-0.54.5-smp, en utilisant son propre utilisateur. Écrire dans une firebase database Oracle 10G sur une autre machine.

Nous utilisons une vue standard -> service -> dao layering. Où chaque dao est annoté avec @Repository.

Tout cela fonctionne 24 heures sur 24, 7 jours sur 7, sans beaucoup de problèmes, mais tous les jours et parfois plusieurs fois en un jour, tout le système entre dans un état déplorable et ne peut plus rien écrire dans la firebase database. Ces stacktraces apparaissent dans les journaux:

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. 

Nous avons balayé le système complet et il existe un emplacement dans le système où le mode flush est temporairement défini sur MANUAL, après quoi un bloc final a été remis à sa valeur initiale. En effet, nous ne voulons pas vider l’état de la firebase database avant que cette requête ne soit exécutée. Nous ne pouvons donc pas changer cela très facilement. Le FlushMode normal est réglé sur AUTO et, à plusieurs endroits, nous le réglons temporairement sur COMMIT et le rétablissons à nouveau par défaut.

Seul un redémarrage du serveur rétablit le fonctionnement du système.

La question qui se pose est la suivante: pourquoi le système définit-il toutes les transactions en mode lecture seule / manuel? J’ai cherché cela sur Google mais je n’ai pas trouvé de solution.

Voici notre configuration de spring et d’hibernation (seule la partie pertinente est affichée):

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

C’est le 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 $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.sortingnidad.component.MethodExpressionMethodBinding.invoke(MethodExpressionMethodBinding.java:46) at com.sun.faces.application.ActionListenerImpl.processAction(ActionListenerImpl.java:102) at org.apache.myfaces.sortingnidad.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.sortingnidadinternal.webapp.TrinidadFilterImpl._invokeDoFilter(TrinidadFilterImpl.java:238) at org.apache.myfaces.sortingnidadinternal.webapp.TrinidadFilterImpl._doFilterImpl(TrinidadFilterImpl.java:195) at org.apache.myfaces.sortingnidadinternal.webapp.TrinidadFilterImpl.doFilter(TrinidadFilterImpl.java:138) at org.apache.myfaces.sortingnidad.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) 

Tout cela fonctionne bien

Cette exception provient du code suivant de la classe HibernateTemplate de Spring:

 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."); } } 

La justification de cette vérification est expliquée comme suit :

Ceci est une nouvelle vérification de cohérence introduite dans Spring 1.1.

L’appel des méthodes de sauvegarde / mise à jour / suppression de HibernateTemplate sur une session gérée par Spring dans FlushMode.NEVER est potentiellement dangereux. Cela signifie que vous êtes:

  • soit cela dans une transaction en lecture seule gérée par Spring, qui ne videra jamais la session Hibernate, c’est-à-dire ne vide jamais vos appels de sauvegarde / mise à jour / suppression. La nouvelle vérification vous indique que vous ne conserverez pas vos modifications dans cette situation.

  • ou travailler avec une autre session liée au thread dans FlushMode.NEVER, par exemple OpenSessionInViewFilter. Si vous substituez à closeSession de vider après le rendu de la vue, vous devez également remplacer getSession, en définissant la session sur FlushMode.AUTO.

Je recommande fortement contre ce dernier, cependant. Si vous utilisez OpenSessionInViewFilter, combinez-le avec des transactions de niveau intermédiaire plutôt que de laisser le filtre vider à la fin de la demande.

Est-ce que tout ça vous dit quelque chose?

Il est possible qu’il y ait un bogue dans votre code ou dans Spring ORM. Pour désactiver cette vérification, vous pouvez appeler setCheckWriteOperations(false) .

aseesing, je vérifie votre configuration. dans la partie contexte de spring, vous utilisez

   

généralement, seuls certains access sont en lecture seule, tels que get / find / query, etc. Je suggère d’utiliser

   

Une autre chose est, utilisez-vous le modèle opensessioninview? le mode flush peut être défini correctement dans opensessioninview. vous pouvez utiliser le filtre dans web.xml ou Spring Interceptor dans le contexte d’application.

dans votre web.xml, mettez:

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

J’imagine que c’est le spring qui fait cela pour vous. Je crois me souvenir que cela a été fait par OpenSessionInViewFilter au spring. Utilisez-vous cela?

C’est de mémoire, donc ce n’est pas très détaillé. Il y a deux façons de régler le mode flush. Je pense un moyen de spring et un moyen d’hibernation. Parce que nous utilisions la méthode (Spring?) Une seule fois et la méthode Hibernate toutes les autres fois, l’état interne d’Hibernate a été altéré. Ce qui a créé mes problèmes. Lorsque nous avons tout mis en place, le problème a disparu.

J’ai rencontré cela lorsque 2 “baseTransactionProxy” ont été utilisés à partir d’un haricot:

Premier :

      

Seconde :

        

et nous avons fait

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

Pas sûr que ça aide, mais il semble que ce ne soit pas bon de mélanger 2 “baseTransactionProxy” différents au même appel proxy …

Étant donné que vous utilisez un point d’exécution sur l’implémentation et un DAO générique, il ne s’agit peut-être pas de traiter les transactions comme vous le souhaiteriez et ceci n’est que la manifestation du problème réel.

Assurez-vous que Spring envoie la procuration de votre DAO comme prévu à cet appel. Vous devrez peut-être utiliser la syntaxe target(beanid) pour activer les transactions appropriées pour votre DAO générique.