1. 程式人生 > >No qualifying bean of type [javax.persistence.EntityManage] 異常問題的解決

No qualifying bean of type [javax.persistence.EntityManage] 異常問題的解決

引言: 在Spring Web專案中一般都會使用OpenEntityManagerInViewFilter來保證JPA session的正常關閉,在筆者的專案中,使用了Spring + Spring Data + JPA + Hibernate來的架構來組織專案,碰到了org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'entityManagerFactory' is defined 的異常資訊,將過一番查詢之後,方才發現問題是載入順序的問題....

1. 專案背景介紹

   專案中使用的技術有: Spring+Spring Data, JPA, Hibernate等來貫穿專案的主題架構。

2.  Session異常關閉的處理機制

在Java Web專案中使用Hibernate經常會遇到LazyInitializationException 。這是因為controller和model層(java程式碼)將通過JPA的一些啟用了延遲載入功能 的領域(如用getRefrence() 方法或者在關聯關係中採用fetch=FetchType.LAZY )返回給view層(jsp程式碼)的時候,由於載入領域物件的JPA Session已經關閉,導致這些延遲載入的資料訪問異常。

這時就可以使用OpenEntityManagerInViewFilter來將一個JPAsession與一次完整的請求過程對應的執行緒相繫結。

   解決辦法:

<filter>  
        <filter-name>Spring OpenEntityManagerInViewFilter</filter-name>  
        <filter-class>org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter</filter-class>  
        <init-param>  
        <!-- 指定org.springframework.orm.jpa.LocalEntityManagerFactoryBean在spring配置檔案中的名稱,預設值為entityManagerFactory  
        如果LocalEntityManagerFactoryBean在spring中的名稱不是entityManagerFactory,該引數一定要指定,否則會出現找不到entityManagerFactory的例外 -->  
            <param-name>entityManagerFactoryBeanName</param-name>  
            <param-value>entityManagerFactory</param-value>  
        </init-param>   
    </filter>  
    <filter-mapping>  
        <filter-name>Spring OpenEntityManagerInViewFilter</filter-name>  
        <url-pattern>/*</url-pattern>  
    </filter-mapping>  
3. 異常問題的出現

   在解決了Session異常關閉之後,在啟動伺服器的時候,就出現瞭如下異常資訊:

org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'entityManagerFactory' is defined
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanDefinition(DefaultListableBeanFactory.java:575)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getMergedLocalBeanDefinition(AbstractBeanFactory.java:1111)
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:276)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:195)
	at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1121)
	at org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter.lookupEntityManagerFactory(OpenEntityManagerInViewFilter.java:222)
	at org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter.lookupEntityManagerFactory(OpenEntityManagerInViewFilter.java:205)
	at org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter.doFilterInternal(OpenEntityManagerInViewFilter.java:150)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:106)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
	at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:88)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:106)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:220)
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:122)
	at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:501)
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:170)
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:98)
	at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:950)
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:116)
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:408)
	at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1040)
	at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:607)
	at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:313)
	at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
	at java.lang.Thread.run(Thread.java:662)
從異常資訊的分析可知道,OpenEntityManagerInViewFilter中對entityManagerFactory有依賴,需要對其在Spring中宣告的例項依賴。 但是沒有找到合適的例項。

  可是entityManagerFactory的確已經宣告在了spring的配置檔案之中了。問題在哪裡呢?

4. 問題分析以及定位

   專案中entityManagerFactory在Spring中的配置檔案定義的,經過檢查,工作正常,不存在問題。

	<beans:bean id="entityManagerFactory"
		class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
		<beans:property name="dataSource" ref="dataSource" />
       <!-- 
        <beans:property name="persistenceProviderClass" value="org.hibernate.ejb.HibernatePersistence"/>
         -->
		<beans:property name="jpaVendorAdapter">
			<beans:bean
				class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
				<!-- hibernate properties definition -->
			</beans:bean>
		</beans:property>

		<beans:property name="persistenceUnitName" value="myPersistenceUnit" />
		<beans:property name="packagesToScan">
			<beans:list>
				<beans:value>com.creditease.bsettle.pay.model</beans:value>
			</beans:list>
		</beans:property>

		<beans:property name="loadTimeWeaver">
			<beans:bean
				class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver" />
		</beans:property>
		<beans:property name="jpaPropertyMap">
			<beans:map>
				<beans:entry key="showSql" value="true"></beans:entry>
				<beans:entry key="generateDdl" value="false"></beans:entry>
				<beans:entry key="hibernate.format_sql" value="true"></beans:entry>
				<beans:entry key="hibernate.dialect"
					value="org.hibernate.dialect.Oracle10gDialect"></beans:entry>
			</beans:map>
		</beans:property>
	</beans:bean>
  於是把問題的懷疑點聚焦在web.xml初始化檔案上,是否其中存在什麼問題呢?

  在web.xml中存在2個地方進行spring例項的初始化操作:

    <context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>classpath:META-INF/spring-config.xml</param-value>
	</context-param>
  第二個位置是:
<servlet>
		<servlet-name>appServlet</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<init-param>
			<param-name>contextConfigLocation</param-name>
			<param-value>classpath:META-INF/applicationContext.xml</param-value>
		</init-param>
		<load-on-startup>1</load-on-startup>
	</servlet>
 存在錯誤異常的載入順序中,entityManagerFactory是在第二個位置中進行載入的。但是OpenEntityManagerInViewFilter是否載入的順序在其之前,然後就造成了這樣的問題呢?

 經過試驗,果然是載入順序的問題,將資料庫載入的順序提前即可。

5. 總結

  基於上面的問題,我們可以看到, OpenEntityManagerInViewFilter是在系統啟動過程中,優先被載入的,同時其對entityManagerFactory有依賴,就要求同時可以初始化entityManagerFactory的例項。

   通過調整初始化的順序,即可很好的修正上述的問題。

參考資料:

1. http://whoosh.iteye.com/blog/1300721