Spring neue HibernateSession durch ApplicationContext refresh

alvarano

Grünschnabel
Hallo zusammen,

ich hoffe hier kann mir jemand helfen.
Ich nutze Spring mit Hibernate sowie Struts in einer Webapplikation. Um lazy initialize von Hibernate nutzen zu können, habe ich den OpenSessionInViewFilter vom Springframework in die web.xml eingebunden. Läuft auch alles tadellos.

Die Konfiguration von Hibernate erfolgt ebenfalls in applicationContext.xml wobei die Datenbankparameter mittels eines PropertyPlaceholderConfigurer von einer externen conf Datei geliefert werden.

Code:
<bean id="hibernateSessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
  <property name="dataSource" ref="dataSource"/>
    <property name="mappingResources">
 	<list>
 	  <value>....hbm.xml</value>
	</list>
    </property>
  <property name="hibernateProperties">
    <props>
      <prop key="hibernate.dialect">${dialect}</prop>
      <prop key="hibernate.show_sql">false</prop>
      <prop key="hibernate.hbm2ddl.auto">update</prop>
    </props>
  </property>	
</bean>
...
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
		<property name="driverClassName" value="${driver}" />
		<property name="url" value="${url}" />
		<property name="username" value="${username}" />
		<property name="password" value="${password}" />
</bean>

Nun möchte ich jedoch zur Laufzeit die Daten in der externen conf Datei ändern und den ApplicationContext neu laden.

Code:
XmlWebApplicationContext ctx = (XmlWebApplicationContext)WebApplicationContextUtils.getWebApplicationContext(getServlet().getServletContext());
ctx.refresh();

Damit soll ein kompletter Datenbankschwenk möglich sein.
Der Context wird auch neu geladen aber im Anschluss bekomme ich folgendes:
Code:
org.hibernate.LazyInitializationException: failed to lazily initialize a collection of ..., no session or session was closed
Das Problem liegt darin, dass die HibernateSession geschlossen ist, wenn das System versucht mittels lazy initialize auf die collections zuzugreifen. Ich hab auch den OpenSessionInViewFilter debugged. Er liefert definitiv eine geöffnete HibernateSession.

Ich komm da gerade nicht weiter und hoffe auf eure Hilfe ...
 
Hm... grundsätzlich ist ein refresh eines AC mit einem Neustart der Applikation gleichzusetzen. Allerdings könnte man versuchen die für den Neustart relevanten Beans in einen extra AC zu laden und den als parent des normalen AC zu deklarieren. Für die Änderung der DB Verbindung brauchst du dann "nur noch" diesen AC refreshen, was sicherlich etwas flotter geht, als die komplette Anwendung neu hochzuziehen.

Wie das genau geht findest du hier:

http://forum.springframework.org/showthread.php?t=32360&highlight=ApplicationContext+hierarchy
http://forum.springframework.org/showpost.php?p=19758&postcount=2

Gruß
Ollie
 
Hallo Ollie und danke für deine schnelle Antwort.
Die Performance bei einem Datenbankschwenk ist bei meiner Applikation nicht kritisch. Blos leider verstehe ich noch nicht wie mir ein hierarchischer AC bei meinem LazyInitializationException helfen soll. Wie gesagt, vor dem AC resfresh funktioniert es tadellos. Ich kann mir nur vorstellen, dass mein OpenSessionInViewFilter noch Probleme hat, da er ja mit dem AC refresh nicht neu gestart wird.

Gruß,
alvarano
 
Sorry wegen der Verwirrung, ich dachte ein Timeout wäre das Problem. Nächstes mal muss ich geneuer lesen ;).

Du klaust halt deinem OSIVF die Session unter dem weg ;). Nach dem Refresh wird die natürlich nicht neu gesetzt, da der OSIVF unter der Kontrolle des Servletcontainers steht und nicht von Spring. D.h. er wird bei einem Refresh nicht neu initialisiert.

Im Allgemeinen bevorzuge ich den Einsatz des OpenSessionInViewInterceptors, den du an das HandlerMapping hängen kannst. Damit hast du eine Komponente unter Springkontrolle die genau das tut, was der OSIVF macht. Diese wird danütlich auch über den Refresh informiert.

Grundsätzlich würde ich aber doch darüber nachdenken, den Datenbankteil in einen eigenen ApplicationContext zu tun. Gerade wenn die Anwendung wächst, kann so ein Restart schon mal ein paar Sekündchen dauern, wenn lediglich eine Datasource umzukonfigurieren und eine SessionFactory hochzuziehen ist, macht es Sinn, nur diesen Teil zu refreshen.

Gruß
Ollie
 
Hallo Ollie,

ich hab nun meinen OpenSessionInViewFilter in der web.xml auskommentiert und folgenden Teil in die action-servlet.xml eingefügt

Code:
 <bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
    <property name="interceptors">
      <list>
        <ref bean="openSessionInViewInterceptor"/>
      </list>
    </property>
    <property name="mappings">
      <props>
        <prop key="/*">strutsWrappingController</prop>
      </props>
   </property>
  </bean>
 
  <bean id="strutsWrappingController" class="org.springframework.web.servlet.mvc.ServletWrappingController">
    <property name="servletClass">
      <value>org.apache.struts.action.ActionServlet</value>
    </property>
    <property name="servletName">
      <value>action</value>
    </property>
    <property name="initParameters">
      <props>
        <prop key="config">/WEB-INF/struts-config.xml</prop>
      </props>
    </property>
  </bean>
  
  <bean name="openSessionInViewInterceptor" class="org.springframework.orm.hibernate3.support.OpenSessionInViewInterceptor">
    <property name="sessionFactory"><ref bean="hibernateSessionFactory"/></property>
  </bean>

Nun meldet er zu beginn, dass er die beans meiner BusinessServices nicht auflösen kann. Das mag ja sein, da der ServletWrappingController den ich verwende ja nur die action-servlet.xml kennt. Der eigentliche AC (also applicationContext.xml) ist ihm ja völlig unbekannt. Kannst du mir noch sagen wie ich ihm den AC bekannt machen kann.

Vielen Dank + Gruß,
alvarano
 
Hm, ich seh grad nicht richtig, wie das miteinander zusammenhängen soll. Hast du den ContextLoaderListener in der web.xml deklariert? Der sucht per default unter WEB-INF/applicationContext.xml nach dem Konfigurationsfile für die eigentliche Anwendung. Der AC den dieses KOnfigurationsfile bildet ist dann Parent des AC deines DispatcherServlets (in deinem Fall action-servlet.xml). D.h. du solltest in action-servlet.xml beans referenzieren können, die in deiner applicationContext.xml deklariert sind.

Gruß
Ollie
 
Der ContextLoaderListener ist in der web.xml definiert. Bisher hat das auch funktioniert, dass die beans in der action-servlet.xml auf die von applicationContext.xml zugreifen können. Sobald ich jedoch den openSessionInViewInterceptor (wie in vorherigem Beitrag beschrieben) in der action-servlet.xml einbinde, bekomme ich folgende Exception

Code:
2008-09-05 12:49:12,540 ERROR [main] org.springframework.web.struts.ContextLoaderPlugIn: Context initialization failed
org.springframework.beans.factory.BeanCreationException: Error creating bean with name '/index' defined in ServletContext resource [/WEB-INF/action-servlet.xml]: Cannot resolve reference to bean 'userBS' while setting bean property 'userBS'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'userBS' is defined
	at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:275)
	at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:104)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1172)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:940)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:437)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory$1.run(AbstractAutowireCapableBeanFactory.java:383)
	at java.security.AccessController.doPrivileged(Native Method)

Gruß,
alvarano
 
Hm, das klingt strange. Referenzierst du userBS per <ref local="userBS" />. Wenn userBS im Parentcontext liegt musst du <ref bean="userBS" /> oder explizit <ref parent="userBS" /> benutzen. Ich hab mir angewöhnt, einfach <property name="foo" ref="bar" /> zu benutzen, weil das erheblich kürzer ist und auch nicht auf einen gewissen scope beschränkt.

Trotzdem ist schon weird. Bist du dir sicher, dass der parent ApplicationContext überhaupt geladen wird?

REINHAUN!
 
Hallo Ollie,

ich hab den Fehler gefunden. Ich hatte noch das ContextLoaderPlugIn in der struts-config.xml drin. Hab es nun raugelöscht und es t. Vielen Dank für deine Hilfe.

Gruß,
alvarano
 

Neue Beiträge

Zurück