28

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();
}
Vlad Mihalcea
  • 142,745
  • 71
  • 566
  • 911
Xorty
  • 18,367
  • 27
  • 104
  • 155
  • Why are you not setting `entityManagerFactory` property of `transactionManager`? And where is your `persistence.xml` file? – Rohit Jain Mar 02 '15 at 19:15
  • `persistence.xml` is on classpath inside `META-INF`. I tried to set `entityManagerFactories` on `transactionManager` beans, but the results are exactly same. – Xorty Mar 02 '15 at 19:57
  • FYI I am pretty sure that `persistence.xml` is visible, if I fall back to using just one entityManager then Spring successfully assembles all the beans. – Xorty Mar 02 '15 at 19:58
  • That is strange. I have also used multiple entityManagers in my application, though I followed Java based config approach. And it worked perfectly, with different persistence unit names. There should be no reason it wouldn't work for 2, if it works for 1. Or may be, there is very minute thing missing somewhere.. I've slight doubt with the name of PUs.. This might sound silly, but could you just replace `.` with `-`, in `persistence.reading` and `persistence.writing`? – Rohit Jain Mar 02 '15 at 20:02
  • heh I already thought of that and I changed persistence unit names but unfortunately no luck :-/ I updated the original question with another interesting error though – Xorty Mar 02 '15 at 20:08
  • Can you try wiring `EntityManagerFactory` rather? Using `@PersistenceUnit` annotation, giving the unit name. I suspect it has something to do with the `transaction-type` attribute you used. And then get the `entityManager` from the factory. – Rohit Jain Mar 02 '15 at 20:29
  • thanks for trying Rohit, I updated my question with the second update. Still stuck. – Xorty Mar 02 '15 at 20:39
  • Strange.. I came across this post: http://stackoverflow.com/q/17331024/1679863, so I thought that might be the issue.. Still searching for something relevant.. – Rohit Jain Mar 02 '15 at 20:42
  • Nah I don't plan to span my transactions over 2 databases - just want dedicated one for writes and reads, so no JTA needed. – Xorty Mar 02 '15 at 20:43
  • You could probably get rid of the "No qualifying bean" exception by using Qualifiers. Create two annotations (ie.: ReadOnly and ReadWrite). Apply the Qualifier annotation on both of them. Use theese annotations on the Bean methods that return your entity managers. You should now be able to inject the entity managers by using a combination of Autowired and one of your new annotations. Sorry, but I cant test it right now. – Apokralipsa Mar 02 '15 at 20:55
  • @Apokralipsa this is the strange thing. Even if I use `Qualifier` Spring doesn't seem to notice it, it is still trying to inject EntityManager using the same bean name as is the name of persistence unit. – Xorty Mar 02 '15 at 21:08
  • I am also trying to create two entity manager factory using above approach, but in my case dataSource is same, but still I get same issue No qualifying bean of type 'javax.persistence.EntityManagerFactory' available: expected single matching bean but found 2. ANy Pointers? – user1575601 Nov 06 '18 at 20:56
  • @RohitJain, I am having issue with injecting entity manager. I have two data sources configured. And I used Qualifier to inject which entity manager in my class that uses EM. Now I am getting 'javax.persistence.TransactionRequiredException: no transaction is in progress' how to resolve this? – ssbh Jan 25 '21 at 19:44

1 Answers1

18

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.

Vlad Mihalcea
  • 142,745
  • 71
  • 566
  • 911
  • 1
    Thanks Vlad, I was finally able to make it work. Please see my update in the original question and fix your answer as well (so that it at least compiles :)) if other people encounter this problem as well. – Xorty Mar 03 '15 at 11:02
  • Point taken. I wrote this answer from a tablet and I was playing the compiler job in my head, while editing. I updated the answer with your Final Fix, to avoid any question-answer confusion. – Vlad Mihalcea Mar 03 '15 at 11:11
  • I am also trying to create two entity manager factory using above approach, but in my case dataSource is same, but still I get same issue No qualifying bean of type 'javax.persistence.EntityManagerFactory' available: expected single matching bean but found 2. Any pointers? – user1575601 Nov 06 '18 at 20:54