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


你可能感兴趣的:(spring,jpa,Data)