Spring, JPA, Hibernate, Tomcat: Unable to find save block when loading Spring application context

I have an application context where I try to configure JPA:

context.xml applications :

<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 load the application context into web.xml as follows:

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

The CompetenceAreaManager class , which is located 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?

+4
source share
2 answers

If you configure PersistenceAnnotationBeanPostProcessor using persistenceUnits, you tell PersistenceAnnotationBeanPostProcessor that the PU files come from JNDI (as the javadoc method for setPersistenceUnits () mentions). The stack trace actually shows an unsuccessful JNDI lookup.

Since you are using <context:annotation-config/> in application-context.xml, you do not need to declare PersistenceAnnotationBeanPostProcessor, because it will be automatically registered, which searches for PU files, reading your META-INF / persistence.xml files located in class paths that you actually expect.

Your configuration should be as simple as this:

persistence.xml remains unchanged

CompetenceAreaManager

As Sergey Makarov mentions, just add an EntityManager with @PersistenceContext instead of injecting EntityManagerFactory with @PersistenceUnit. Em is transactional (thus thread-related, thereby ensuring thread safety for your DAO), and you can still configure @PersistenceContext with unitName to indicate the PU to which EM should bind.

context.xml applications

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

I have not tried your specific config (2 PU), but in the configuration that I mention, it is the one that I always used with success.

+2
source

As far as I understood from my recent experience of creating 2 EntityManagerFactory in one application - @PersistenceUnit (unitName = "myPU") just doesn't work at all.

I would suggest introducing EntityManager, not EntityManagerFactory. This is understandable since you always know which EMF is being used. The same goes for indicating the correct TransactionManager.

Service class code updated:

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

This injection is safe because Spring introduces the EntityManager proxy, which guarantees thread safety.

+1
source

Source: https://habr.com/ru/post/1492278/


All Articles