0

I need to use multiple databases. I am using Spring Boot + Spring Data JPA, so I have two configuration classes:

@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(basePackages="com.rest.dao.first",
        entityManagerFactoryRef = "firstEntityManagerFactory", transactionManagerRef = "firstTransactionManager")
public static class DnbbJdbcConfig {

    @Primary
    @Bean
    @ConfigurationProperties(prefix="datasource.first")
    public DataSource dataSource() {
        return DataSourceBuilder.create().build();
    }

    @Primary
    @Bean(name = "firstEntityManagerFactory")
    public LocalContainerEntityManagerFactoryBean entityManagerFactory(EntityManagerFactoryBuilder builder) {
        return builder
                .dataSource(dataSource())
                .packages("com.rest.dao.first")
                .persistenceUnit("first")
                .build();
    }

    @Primary
    @Bean(name = "firstTransactionManager")
    PlatformTransactionManager transactionManager(EntityManagerFactoryBuilder builder) {
        return new JpaTransactionManager(entityManagerFactory(builder).getObject());
    }

}

@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(basePackages="com.rest.dao.second",
    entityManagerFactoryRef = "secondEntityManagerFactory", transactionManagerRef = "secondTransactionManager")
public static class SmsJdbcConfig {

    @Bean
    @ConfigurationProperties(prefix="datasource.second")
    public DataSource dataSource() {
        return DataSourceBuilder.create().build();
    }

    @Bean(name = "secondEntityManagerFactory")
    @PersistenceContext(unitName = "second")
    public LocalContainerEntityManagerFactoryBean entityManagerFactory(EntityManagerFactoryBuilder builder) {
        return builder
                .dataSource(dataSource())
                .packages("com.rest.dao.second")
                .persistenceUnit("second")
                .build();
    }

    @Bean(name = "secondTransactionManager")
    PlatformTransactionManager transactionManager(EntityManagerFactoryBuilder builder) {
        return new JpaTransactionManager(entityManagerFactory(builder).getObject());
    }
}

I guess there is not error and correct. So, when I use default repository then not error. (e.g userRepository.findById() - not error in multi datasources)

But, When I use custom repository then error occur. (https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories.custom-implementations)

Custom Implements

public class FirstRepositoryImpl extends QueryDslRepositorySupport implements FirstCustomRepository {

public FirstRepositoryImpl() {
    super(First.class);
}

@PersistenceContext(unitName = "first")
private EntityManager entityManager;

private QFirst first = QFirst.first;

@Override
public List<String> messages() {
    JPAQuery query = new JPAQuery(entityManager);
    return query.from(first).list(first.message);
}   
}

ExceptionTrace

Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [javax.persistence.EntityManagerFactory] is defined: expected single matching bean but found 2: firstEntityManagerFactory,secondEntityManagerFactory
at org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor.findDefaultEntityManagerFactory(PersistenceAnnotationBeanPostProcessor.java:582) ~[spring-orm-4.2.3.RELEASE.jar:4.2.3.RELEASE]
at org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor.findEntityManagerFactory(PersistenceAnnotationBeanPostProcessor.java:541) ~[spring-orm-4.2.3.RELEASE.jar:4.2.3.RELEASE]
at org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor$PersistenceElement.resolveEntityManager(PersistenceAnnotationBeanPostProcessor.java:707) ~[spring-orm-4.2.3.RELEASE.jar:4.2.3.RELEASE]
at org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor$PersistenceElement.getResourceToInject(PersistenceAnnotationBeanPostProcessor.java:680) ~[spring-orm-4.2.3.RELEASE.jar:4.2.3.RELEASE]
at org.springframework.beans.factory.annotation.InjectionMetadata$InjectedElement.inject(InjectionMetadata.java:178) ~[spring-beans-4.2.3.RELEASE.jar:4.2.3.RELEASE]
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:88) ~[spring-beans-4.2.3.RELEASE.jar:4.2.3.RELEASE]
at org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor.postProcessPropertyValues(PersistenceAnnotationBeanPostProcessor.java:354) ~[spring-orm-4.2.3.RELEASE.jar:4.2.3.RELEASE]
... 45 common frames omitted

Did I misconfigure something?

I wrote sample code in my github repository.. https://github.com/okihouse/spring-boot-multiple-datasource-with-querydsl

Neil Stockton
  • 11,383
  • 3
  • 34
  • 29
OKIHOUSE
  • 33
  • 6
  • 1
    the exception that you get is straight forward. You define two bean which are same type. to avoid such exceptio you can use @Qualifier(secondEntityManagerFactory) (where you want this bean is autowired) and @Qualifier(firstEntityManagerFactory) – Vaseph Aug 19 '16 at 06:02
  • @Vaseph I try `@PersistenceUnit(name = "firstEntityManagerFactory", unitName = "first") @Qualifier("firstEntityManagerFactory") private EntityManagerFactory entityManagerFactory;` but still error. – OKIHOUSE Aug 19 '16 at 06:12
  • what error did you get now? – Vaseph Aug 19 '16 at 06:23
  • @Vaseph It is same error `No qualifying bean of type [javax.persistence.EntityManagerFactory] is defined: expected single matching bean but found 2: firstEntityManagerFactory,secondEntityManagerFactory` I run debug and I found some code snippet in `PersistenceAnnotationBeanPostProcessor.java:582` `findDefaultEntityManagerFactory` method is default use EMF when not exist unitName, is that right? Why spring not excute `protected EntityManagerFactory findNamedEntityManagerFactory(String unitName, String requestingBeanName)` method?? – OKIHOUSE Aug 19 '16 at 06:40

1 Answers1

2

Solved myself:

I'm check QueryDslRepositorySupport.class and found out.

@PersistenceContext
public void setEntityManager(EntityManager entityManager) {
    Assert.notNull(entityManager);
    this.querydsl = new Querydsl(entityManager, builder);
    this.entityManager = entityManager;
}

@PersistenceContext have not "unitName" So, Spring can't inject EntityManager.

I create QueryDslRepositorySupportWrapper.java and inject EntityManager manually.

And It works.

https://github.com/okihouse/spring-boot-multiple-datasource-with-querydsl

Glorfindel
  • 21,988
  • 13
  • 81
  • 109
OKIHOUSE
  • 33
  • 6