How to inject multiple JPA EntityManager (persistence units) when using Spring
The EntityManageFactory
is not properly configured. You should use a LocalContainerEntityManagerFactoryBean
instead:
@Bean(name = "readingEntityManagerFactory")
public EntityManagerFactory readingEntityManagerFactory() {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setPersistenceUnitName("persistence.reading");
em.setDataSource(dataSource());
em.setPackagesToScan("com.company");
em.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
em.afterPropertiesSet();
return em.getObject();
}
Also the JpaTransactionManager
is miss-configured too. It should be something like:
@Bean(name = "readingTransactionManager")
public PlatformTransactionManager readingTransactionManager(){
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(readingEntityManagerFactory());
return transactionManager;
}
You need to do the same for both the reading and the writing EntityManager configurations.
Xorty
Freelance Java developer chronically obsessed with clean design. Focusing mainly on Spring Framework and Java EE. Dependency Injection worshipper.
Updated on July 09, 2022Comments
-
Xorty almost 2 years
I need to use one database for queries (non-modifying) and one for commands (modifying). I am using Spring Data JPA, so I have two configuration classes:
@Configuration @EnableJpaRepositories(value = "com.company.read", entityManagerFactoryRef = "readingEntityManagerFactory", transactionManagerRef = "readingTransactionManager") @EnableTransactionManagement public class SpringDataJpaReadingConfiguration { @Bean(name = "readingEntityManagerFactory") public EntityManagerFactory readingEntityManagerFactory() { return Persistence.createEntityManagerFactory("persistence.reading"); } @Bean(name = "readingExceptionTranslator") public HibernateExceptionTranslator readingHibernateExceptionTranslator() { return new HibernateExceptionTranslator(); } @Bean(name = "readingTransactionManager") public JpaTransactionManager readingTransactionManager() { return new JpaTransactionManager(); } } @Configuration @EnableJpaRepositories(value = "com.company.write", entityManagerFactoryRef = "writingEntityManagerFactory", transactionManagerRef = "writingTransactionManager") @EnableTransactionManagement public class SpringDataJpaWritingConfiguration { @Bean(name = "writingEntityManagerFactory") public EntityManagerFactory writingEntityManagerFactory() { return Persistence.createEntityManagerFactory("persistence.writing"); } @Bean(name = "writingExceptionTranslator") public HibernateExceptionTranslator writingHibernateExceptionTranslator() { return new HibernateExceptionTranslator(); } @Bean(name = "writingTransactionManager") public JpaTransactionManager writingTransactionManager() { return new JpaTransactionManager(); } }
In my repository I sometimes need to decide with EntityManager to use like so:
@Repository public class UserReadingRepository { @PersistenceContext(unitName = "persistence.reading") private EntityManager em; // some useful queries here }
I am using persistence unit's name as defined in my persistence.xml:
<persistence xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd" version="2.0"> <persistence-unit name="persistence.reading" transaction-type="RESOURCE_LOCAL"> <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider> <non-jta-data-source>ReadingDS</non-jta-data-source> <properties> <property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect" /> <property name="hibernate.show_sql" value="true" /> </properties> </persistence-unit> <persistence-unit name="persistence.writing" transaction-type="RESOURCE_LOCAL"> <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider> <non-jta-data-source>WritingDS</non-jta-data-source> <properties> <property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect" /> <property name="hibernate.show_sql" value="true" /> </properties> </persistence-unit> </persistence>
Spring throws
org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'persistence.reading'
is defined. Oddly, it looks like Spring tries to instantiate a bean with persistence unit name? Did I misconfigure something?UPDATE: When I remove
unitName = "persistence.reading"
from @PersistenceContext annotation, I will get following error instead:org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [javax.persistence.EntityManagerFactory] is defined: expected single matching bean but found 2: readingEntityManagerFactory,writingEntityManagerFactory
UPDATE 2: Rohit suggested (in the comment) to wire
EntityManagerFactory
instead. So I tried to do the following:@PersistenceUnit(unitName = "persistence.reading") private EntityManagerFactory emf;
but Spring only reports:
org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'persistence.reading' is defined
FINAL FIX: Thanks to Vlad's answer, I was able to update the code to use the following (just make sure you define your
dataSource
bean as well):@Bean(name = "readingEntityManagerFactory") public EntityManagerFactory readingEntityManagerFactory() { LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean(); em.setPersistenceUnitName("persistence.reading"); em.setDataSource(dataSource()); em.setPackagesToScan("com.company"); em.setJpaVendorAdapter(new HibernateJpaVendorAdapter()); em.afterPropertiesSet(); return em.getObject(); }