【问题标题】:Spring, JPA, Hibernate, Tomcat: Unable to find persistence unit when loading Spring application contextSpring、JPA、Hibernate、Tomcat:加载 Spring 应用程序上下文时找不到持久性单元
【发布时间】:2013-07-19 10:27:10
【问题描述】:

我有一个正在尝试设置 JPA 的应用程序上下文:

application-context.xml

<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor">
    <property name="persistenceUnits">
        <map>
            <entry key="pu1" value="pu1" />
            <entry key="pu2" value="pu2" />
        </map>
    </property>
    <property name="defaultPersistenceUnitName" value="pu1" />
</bean>

<bean id="emf1" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="jpaVendorAdapter">
        <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
            <property name="showSql" value="true" />
            <property name="generateDdl" value="false" />
            <property name="databasePlatform" value="org.hibernate.dialect.MySQLDialect" />
        </bean>
    </property>
    <property name="persistenceUnitName" value="pu1" />
    <property name="dataSource" ref="dataSource1" />
</bean>

<bean id="emf2" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="jpaVendorAdapter">
        <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
            <property name="showSql" value="true" />
            <property name="generateDdl" value="false" />
            <property name="databasePlatform" value="org.hibernate.dialect.SQLServer2005Dialect" />
        </bean>
    </property>
    <property name="persistenceUnitName" value="pu2" />
    <property name="dataSource" ref="dataSource2" />
</bean>

<!-- Enable annotation style of managing transactions -->
<tx:annotation-driven />

<bean id="transactionManager1" class="org.springframework.orm.jpa.JpaTransactionManager">
    <property name="entityManagerFactory" ref="emf1" />
    <property name="dataSource" ref="dataSource1" />
</bean>

<bean id="transactionManager2" class="org.springframework.orm.jpa.JpaTransactionManager">
    <property name="entityManagerFactory" ref="emf2" />
    <property name="dataSource" ref="dataSource2" />
</bean>

<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="locations">
        <list>
            <value>classpath:config/db/database.properties</value>
        </list>
    </property>
    <property name="ignoreUnresolvablePlaceholders" value="true" />
    <property name="ignoreResourceNotFound" value="true" />
    <property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE"/>  
</bean>

<!-- The actual config of the database is read from the properties file database.properties -->
<bean id="dataSource1" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close"
    p:acquireIncrement="5" p:idleConnectionTestPeriod="14400" p:maxPoolSize="50" p:maxStatements="15"
    p:minPoolSize="5" p:testConnectionOnCheckout="true" p:preferredTestQuery="SELECT 4;"
    p:driverClass="${db.system1.driver}" p:jdbcUrl="${db.system1.url}" p:user="${db.system1.user}" p:password="${db.system1.password}" />

<bean id="dataSource2" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close" p:acquireIncrement="5" p:idleConnectionTestPeriod="60" p:maxPoolSize="10"
    p:maxStatements="50" p:minPoolSize="3" p:testConnectionOnCheckout="true" p:preferredTestQuery="SELECT 4;"
    p:driverClass="${db.system2.driver}" p:jdbcUrl="${db.system2.url}" p:user="${db.system2.user}" p:password="${db.system2.password}" />

<context:annotation-config />
<context:component-scan base-package="com.myapp.model.manager"/>

persistence.xml

<persistence-unit name="pu1" transaction-type="RESOURCE_LOCAL">
    <provider>org.hibernate.ejb.HibernatePersistence</provider>
    <class>com.myapp.model.Address</class>
    <class>com.myapp.model.AgressoFile</class>
    <class>com.myapp.model.CustomerGroup</class>
    ...
</persistence-unit>

<persistence-unit name="pu2" transaction-type="RESOURCE_LOCAL">
    <provider>org.hibernate.ejb.HibernatePersistence</provider>
    <class>com.myapp.model.CompetenceArea</class>
    <class>com.myapp.model.CompetenceAreaCategory</class>
    ...
</persistence-unit>

我以这种方式在 web.xml 中加载应用程序上下文:

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>
        /WEB-INF/spring/application-context.xml
    </param-value>
</context-param>

<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
扫描包“com.myapp.model.manager”中的

CompetenceAreaManager类,其内容如下:

@Service
public class CompetenceAreaManager {

    @PersistenceUnit(unitName = "pu2")
    private EntityManagerFactory entityManagerFactory;

    @SuppressWarnings("unchecked")
    public List<CompetenceArea> getCompetenceAreas() {
        List<CompetenceArea> competenceAreaList = null;
        EntityManager em = entityManagerFactory.createEntityManager();
        Query q = em.createNamedQuery(CompetenceArea.FIND_ALL);
        competenceAreaList = q.getResultList();
        return competenceAreaList;
    }

    public void setEntityManagerFactory(EntityManagerFactory entityManagerFactory) {
        this.entityManagerFactory = entityManagerFactory;
    }
}

但是,当我尝试在 Tomcat 7.0 中运行应用程序时,出现以下错误:

SEVERE: Exception sending context initialized event to listener instance of class org.springframework.web.context.ContextLoaderListener
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'competenceAreaManager': Injection of persistence dependencies failed; nested exception is java.lang.IllegalStateException: Could not obtain EntityManagerFactory [pu2] from JNDI
    at org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor.postProcessPropertyValues(PersistenceAnnotationBeanPostProcessor.java:343)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1122)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:522)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:461)
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:295)
    ...
Caused by: java.lang.IllegalStateException: Could not obtain EntityManagerFactory [pu2] from JNDI
    at org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor.getPersistenceUnit(PersistenceAnnotationBeanPostProcessor.java:435)
    at org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor$PersistenceElement.resolveEntityManagerFactory(PersistenceAnnotationBeanPostProcessor.java:643)
    at org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor$PersistenceElement.getResourceToInject(PersistenceAnnotationBeanPostProcessor.java:637)
    at org.springframework.beans.factory.annotation.InjectionMetadata$InjectedElement.inject(InjectionMetadata.java:150)
    at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:87)
    ...
Caused by: javax.naming.NameNotFoundException: Name [pu2] is not bound in this Context. Unable to find [pu2].
    at org.apache.naming.NamingContext.lookup(NamingContext.java:820)
    at org.apache.naming.NamingContext.lookup(NamingContext.java:168)
    at org.apache.naming.SelectorContext.lookup(SelectorContext.java:158)
    at javax.naming.InitialContext.lookup(Unknown Source)
    at org.springframework.jndi.JndiTemplate$1.doInContext(JndiTemplate.java:154)
    ...

知道我做错了什么吗?

【问题讨论】:

    标签: java spring hibernate jpa tomcat7


    【解决方案1】:

    如果您使用persistenceUnits 配置PersistenceAnnotationBeanPostProcessor,您将通知PersistenceAnnotationBeanPostProcessor PU 来自JNDI(正如setPersistenceUnits() 方法的javadoc 中提到的那样)。堆栈跟踪实际上显示了失败的 JNDI 查找。

    由于您在 application-context.xml 中使用&lt;context:annotation-config/&gt;,因此您不需要声明 PersistenceAnnotationBeanPostProcessor,因为它会自动注册,它通过读取位于的 META-INF/persistence.xml 文件来查找 PU在类路径中,这实际上是您所期望的。

    你的配置应该像这样简单:

    persistence.xml保持不变

    CompetenceAreaManager

    正如 Sergey Makarov 所提到的,只需使用 @PersistenceContext 注入 EntityManager,而不是使用 @PersistenceUnit 注入 EntityManagerFactory。 em 是事务性的(因此绑定到线程,从而确保 DAO 的线程安全),您仍然可以使用 unitName 配置 @PersistenceContext 以指定 EM 必须绑定到的 PU。

    application-context.xml

    只需删除 PersistenceAnnotationBeanPostProcessor bean 的声明。文件的其余部分保持不变。

    我没有尝试过您的特定配置(2 个 PU),但我提到的配置是我一直使用的配置,并且成功。

    【讨论】:

    • 非常感谢您的回答和详细的解释!它就像一个魅力! :)
    【解决方案2】:

    据我最近在同一个应用程序中设置 2 个 EntityManagerFactory 的经验了解,@PersistenceUnit(unitName="myPU") 根本不起作用。

    我建议注入 EntityManager,而不是 EntityManagerFactory。这一点很清楚,因为您总是知道使用的是哪个 EMF。指定正确的 TransactionManager 也是如此。

    服务类的更新代码:

    @Service
    public class CompetenceAreaManager 
    {
        @PersistenceContext(unitName = "emf1")
        private EntityManager em;
    
        @SuppressWarnings("unchecked")
        @Transactional(transactionManager="transactionManager1", readOnly=true)
        public List<CompetenceArea> getCompetenceAreas() 
        {
            List<CompetenceArea> competenceAreaList = null;
            Query q = em.createNamedQuery(CompetenceArea.FIND_ALL);
            competenceAreaList = q.getResultList();
            return competenceAreaList;
        }
    }
    

    这种注入是安全的,因为 Spring 注入了 EntityManager 的代理,保证了线程安全。

    【讨论】:

    • 感谢您的回复!如果我将这些行从应用程序上下文移动到 DispatherServlet 的 servlet 上下文,我的设置确实有效:&lt;context:annotation-config /&gt; &lt;context:component-scan base-package="com.myapp.model.manager"/&gt;。问题是我想将它们保留在应用程序上下文中。
    • 我猜你需要用 DispatcherServlet 配置的描述更新你的帖子。
    • 嗯...坦率地说,我将 DispatherServlet 添加到应用程序中只是为了测试目的,以便查看如果我将“组件扫描”移动到其上下文中会发生什么。和我发的问题其实没有关系,所以为了不让读者混淆,我可能不会在这里发布配置。
    猜你喜欢
    • 2011-04-21
    • 2013-10-31
    • 1970-01-01
    • 2015-05-27
    • 2011-09-22
    • 1970-01-01
    • 2011-07-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多