简体   繁体   中英

Spring, JPA, Hibernate, Tomcat: Unable to find persistence unit when loading Spring application context

I have an application context where I am trying to setup 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>

I am loading application context in a web.xml this way:

<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>

CompetenceAreaManager class that locates in the scanned package "com.myapp.model.manager" has the following content:

@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;
    }
}

However, when I try to run the application in Tomcat 7.0, I get the following error:

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)
    ...

Any idea what I'm doing wrong?

If you configure the PersistenceAnnotationBeanPostProcessor with persistenceUnits, you are informing the PersistenceAnnotationBeanPostProcessor that PUs come from JNDI (as the javadoc for setPersistenceUnits() methods mentions it). The stack trace actually shows the failing JNDI lookup.

Since you are using <context:annotation-config/> in application-context.xml, you don't need to declare a PersistenceAnnotationBeanPostProcessor, because one will be automatically registered, which looks up PUs by reading your META-INF/persistence.xml files located in the classpath, which is actually what you expect.

Your configuration should be as simple as this :

persistence.xml remains unchanged

CompetenceAreaManager

As Sergey Makarov mentions it, just inject EntityManager with @PersistenceContext instead of injecting EntityManagerFactory with @PersistenceUnit. The em is transactional (thus bound to the thread, thus ensuring thread-safety for your DAO), and you can still configure @PersistenceContext with unitName in order to specify the PU the EM has to be bound to.

application-context.xml

Just drop the declaration of the PersistenceAnnotationBeanPostProcessor bean. The rest of the file remains unchanged.

I haven't tried your particular config (2 PUs), but the configuration I mention is the one I have always been using, with success.

As far I understood from my recent expirience of setting up 2 EntityManagerFactory in the same application - @PersistenceUnit(unitName="myPU") just doesn't work at all.

I would suggest to inject EntityManager, not EntityManagerFactory. This is clear since you always know which EMF is used. Same for specifying right TransactionManager.

Updated code of the service class:

@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;
    }
}

Such injection is safe since Spring injects a proxy of EntityManager, which guarantees thread-safety.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM