Spring 3.1 + Hibernate 4.1 JPA,Entity manager factory注册两次

Posted

技术标签:

【中文标题】Spring 3.1 + Hibernate 4.1 JPA,Entity manager factory注册两次【英文标题】:Spring 3.1 + Hibernate 4.1 JPA, Entity manager factory is registered twice 【发布时间】:2012-06-07 15:19:09 【问题描述】:

我使用 Spring Framework 3.1 和 Hibernate 4.1 作为 JPA 提供程序,并且我有一个功能齐全的设置,但是每次启动 Web 应用程序时我都会看到以下警告消息:

14:28:12,725  WARN pool-2-thread-12 internal.EntityManagerFactoryRegistry:80 - HHH000436: Entity manager factory name (something) is already registered.  If entity manager will be clustered or passivated, specify a unique value for property 'hibernate.ejb.entitymanager_factory_name'

应用程序运行良好,但这样的警告消息让我感到困扰,数小时的搜索、调整和试验让我无处可去。我尝试更改工厂名称并添加和省略配置块,但均无济于事。似乎 Spring 或 Hibernate 中的某些东西只是将实体管理器工厂初始化了两次。

仅供参考,我正在使用 LocalContainerEntityManagerFactoryBean 的 packagesToScan 功能来配置实体管理器,而无需使用 persistence.xml 文件。

我已将 Spring 上下文 XML 缩减为以下内容,但问题仍然存在:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="
           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
           http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

  <context:property-placeholder location="classpath:jdbc.properties"/>
  <bean id="dataSource" class="org.apache.tomcat.jdbc.pool.DataSource" destroy-method="close">
    <property name="driverClassName" value="$jdbc.nightsword.driverClassName"/>
    <property name="url" value="$jdbc.nightsword.url"/>
    <property name="username" value="$jdbc.nightsword.username"/>
    <property name="password" value="$jdbc.nightsword.password"/>
  </bean>

  <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="jpaVendorAdapter">
      <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"/>
    </property>
    <property name="dataSource" ref="dataSource"/>
    <property name="packagesToScan" value="x.y"/>
  </bean>
</beans>

为了完整起见,这里是 hibernate.properties:

hibernate.dialect=org.hibernate.dialect.mysql5Dialect
hibernate.ejb.entitymanager_factory_name=something

这里是 org.springframework.orm 和 org.hibernate 的调试级日志输出摘录。您可以在 14:40:06,911 看到 EntityManagerFactory 是如何从第一次注册的,随后 LocalContainerEntityManagerFactoryBean 似乎从头开始重新开始。呵呵。

INFO: Deploying web application archive /opt/local/share/java/tomcat7/webapps/nightsword.war
14:40:06,149  INFO pool-2-thread-13 jpa.LocalContainerEntityManagerFactoryBean:264 - Building JPA container EntityManagerFactory for persistence unit 'default'
14:40:06,219 DEBUG pool-2-thread-13 type.BasicTypeRegistry:143 - Adding type registration boolean -> org.hibernate.type.BooleanType@4cb91eff

...

14:40:06,882 DEBUG pool-2-thread-13 internal.SessionFactoryRegistry:62 - Initializing SessionFactoryRegistry : org.hibernate.internal.SessionFactoryRegistry@161bb7fe
14:40:06,882 DEBUG pool-2-thread-13 internal.SessionFactoryRegistry:75 - Registering SessionFactory: a3219dd8-7d59-45ac-9a5a-0d13e38dbb04 (<unnamed>)
14:40:06,882 DEBUG pool-2-thread-13 internal.SessionFactoryRegistry:82 - Not binding SessionFactory to JNDI, no JNDI name configured
14:40:06,882 DEBUG pool-2-thread-13 internal.SessionFactoryImpl:487 - Instantiated session factory
14:40:06,882 DEBUG pool-2-thread-13 internal.SessionFactoryImpl:1119 - Checking 0 named HQL queries
14:40:06,883 DEBUG pool-2-thread-13 internal.SessionFactoryImpl:1142 - Checking 0 named SQL queries
14:40:06,887 DEBUG pool-2-thread-13 internal.StatisticsInitiator:110 - Statistics initialized [enabled=false]
14:40:06,910 DEBUG pool-2-thread-13 internal.EntityManagerFactoryRegistry:56 - Initializing EntityManagerFactoryRegistry : org.hibernate.ejb.internal.EntityManagerFactoryRegistry@75cc9008
14:40:06,911 DEBUG pool-2-thread-13 internal.EntityManagerFactoryRegistry:66 - Registering EntityManagerFactory: something 
14:40:06,967  INFO pool-2-thread-13 jpa.LocalContainerEntityManagerFactoryBean:264 - Building JPA container EntityManagerFactory for persistence unit 'default'
14:40:06,967 DEBUG pool-2-thread-13 type.BasicTypeRegistry:143 - Adding type registration boolean -> org.hibernate.type.BooleanType@4cb91eff

...

14:40:07,128 DEBUG pool-2-thread-13 internal.SessionFactoryRegistry:75 - Registering SessionFactory: 81a9b5a6-83aa-46ee-be68-d642e6fda584 (<unnamed>)
14:40:07,128 DEBUG pool-2-thread-13 internal.SessionFactoryRegistry:82 - Not binding SessionFactory to JNDI, no JNDI name configured
14:40:07,129 DEBUG pool-2-thread-13 internal.SessionFactoryImpl:487 - Instantiated session factory
14:40:07,129 DEBUG pool-2-thread-13 internal.SessionFactoryImpl:1119 - Checking 0 named HQL queries
14:40:07,129 DEBUG pool-2-thread-13 internal.SessionFactoryImpl:1142 - Checking 0 named SQL queries
14:40:07,129 DEBUG pool-2-thread-13 internal.StatisticsInitiator:110 - Statistics initialized [enabled=false]
14:40:07,130 DEBUG pool-2-thread-13 internal.EntityManagerFactoryRegistry:66 - Registering EntityManagerFactory: something 
14:40:07,130  WARN pool-2-thread-13 internal.EntityManagerFactoryRegistry:80 - HHH000436: Entity manager factory name (something) is already registered.  If entity manager will be clustered or passivated, specify a unique value for property 'hibernate.ejb.entitymanager_factory_name'

【问题讨论】:

你是如何初始化你的 Spring 应用程序上下文的?你的 web.xml 中声明了 org.springframework.web.context.ContextLoaderListener 吗?你在使用 Spring MVC 吗?我有时会看到导入其他应用程序的 Spring MVC XML 配置。上下文 XML,导致一些 bean 实例化两次,因为它们是在应用程序上下文和 Web 应用程序上下文中声明的。 是的,我是,你成功了。 DispatcherServlet 正在加载与 ContextLoaderListener 相同的上下文文件,并且 bam,bean 加载了两次。新手误区,好尴尬!如果您想将您的评论改写为答案,我很乐意接受。谢谢! 我们都是初学者,没什么好害羞的。 【参考方案1】:

我遇到了同样的问题,但情况不同。在从我的 IDE 启动的同一运行(即同一 JVM)中执行多个测试时,EntityManagerFactoryRegistry 会产生相同的 HHH000436 警告。

如果有至少两个测试类使用SpringJUnit4ClassRunner 加载不同 Spring 测试应用程序上下文,每个都包含EntityManagerFactory,问题就会浮出水面。

根本原因是 Hibernate 维护了一个EntityManagerFactory 实例的静态注册表,其中第二个实例的创建可能会导致日志消息所涉及的冲突。那么为什么在第一个测试完成执行后第一个实例没有被注销呢?它通常会在包含该 EntityManagerFactory 实例的应用程序上下文被破坏时发生。在测试执行期间它不会发生的原因是 Spring 测试上下文框架缓存了所有加载的上下文,以避免重新加载多个测试可能需要的完全相同的上下文。因此,这些上下文中的 bean 直到最后一个测试完成执行后才会被销毁,并且 Hibernate 只会收集所有已创建的 EntityManagerFactory 实例。

这确实不是问题,但如果有人真的对警告消息感到恼火,有几种可能的方法可以避免看到它:

    确保EntityManagerFactory 实例获得不同的名称(它们在注册表中按名称键入)。查看EntityManagerFactoryImpl 的构造函数,了解名称的派生方式。 在测试类上使用@DirtiesContext 会导致SpringJUnit4ClassRunner 在执行测试类后立即关闭上下文并将其从上下文缓存中删除。 只需将EntityManagerFactoryRegistry的日志级别设置为错误...

希望这对某人有所帮助。

【讨论】:

【参考方案2】:

你是如何初始化你的 Spring 应用程序上下文的?你在使用 Spring MVC 吗?

我有时会看到导入其他应用程序的 Spring MVC XML 配置。上下文 XML,导致一些 bean 实例化两次,因为它们是在应用程序上下文和 Web 应用程序上下文中声明的。

【讨论】:

以上是关于Spring 3.1 + Hibernate 4.1 JPA,Entity manager factory注册两次的主要内容,如果未能解决你的问题,请参考以下文章

事务不适用于 Spring 3.1 – H2 – junit 4 – hibernate 3.2

Spring 3.1 + Hibernate 4.1 JPA,Entity manager factory注册两次

Mule 3.3 spring 3.1 hibernate 3.6 事务管理

Spring Security 3.1 xsd 和 jars 不匹配问题

java利用myeclipse自带三大框架搭建三大框架(Hibernate+Struts2+Spring)过程详解

在Spring中使用Hibernate 4