Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring Boot Multiple Databse : No qualifying bean of type EntityManagerFactoryBuilder

We have two databases in our Spring Boot Application called source and target. Here is the configuration for those

Source Configuration

package com.alex.myapp.config;

import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.env.Environment;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
        entityManagerFactoryRef = "sourceManagerFactory",
        transactionManagerRef = "sourceTransactionManager",
        basePackages = {"com.alex.myapp.source.repository"}
)
public class SourceDbConfiguration {

    @Autowired
    private Environment env;

    @Primary
    @Bean(name = "sourceManagerFactory")
    public LocalContainerEntityManagerFactoryBean
    sourceManagerFactory(EntityManagerFactoryBuilder builder) {
        LocalContainerEntityManagerFactoryBean em = builder
                .dataSource(sourceDataSource())
                .packages("com.alex.myapp.source.entity")
                .persistenceUnit("source")
                .build();
        return em;
    }

    @Primary
    @Bean
    public DataSource sourceDataSource() {
        DriverManagerDataSource dataSource
                = new DriverManagerDataSource();
        dataSource.setDriverClassName(
                env.getProperty("spring.datasource.driver-class-name"));
        dataSource.setUrl(env.getProperty("spring.datasource.url"));
        dataSource.setUsername(env.getProperty("spring.datasource.username"));
        dataSource.setPassword(env.getProperty("spring.datasource.password"));

        return dataSource;
    }

    @Primary
    @Bean(name = "sourceTransactionManager")
    public PlatformTransactionManager sourceTransactionManager(
            @Qualifier("sourceManagerFactory") EntityManagerFactory
                    sourceManagerFactory
    ) {
        return new JpaTransactionManager(sourceManagerFactory);
    }
}

Target Configuration

package com.alex.myapp.config;

import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.env.Environment;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
        entityManagerFactoryRef = "targetManagerFactory",
        transactionManagerRef = "targetTransactionManager",
        basePackages = {"com.alex.myapp.target.repository"}
)
public class TargetDbConfiguration {

    @Autowired
    private Environment env;

    @Primary
    @Bean(name = "targetManagerFactory")
    public LocalContainerEntityManagerFactoryBean
    targetManagerFactory(EntityManagerFactoryBuilder builder) {
        LocalContainerEntityManagerFactoryBean em = builder
                .dataSource(targetDataSource())
                .packages("com.alex.myapp.target.entity")
                .persistenceUnit("target")
                .build();
        return em;
    }

    @Primary
    @Bean
    public DataSource targetDataSource() {
        DriverManagerDataSource dataSource
                = new DriverManagerDataSource();
        dataSource.setDriverClassName(
                env.getProperty("target.datasource.driver-class-name"));
        dataSource.setUrl(env.getProperty("target.datasource.url"));
        dataSource.setUsername(env.getProperty("target.datasource.username"));
        dataSource.setPassword(env.getProperty("target.datasource.password"));

        return dataSource;
    }

    @Bean(name = "targetTransactionManager")
    public PlatformTransactionManager targetTransactionManager(
            @Qualifier("targetManagerFactory") EntityManagerFactory
                    targetManagerFactory) {
        return new JpaTransactionManager(targetManagerFactory);
    }
}

When I try to start server it throws below mentioned error

Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2018-09-19 13:30:53 - Application run failed
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'sourceManagerFactory' defined in class path resource [com/alex/myapp/config/SourceDbConfiguration.class]: Unsatisfied dependency expressed through method 'sourceManagerFactory' parameter 0; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
    at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:732)
    at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:474)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1247)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1096)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:535)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:495)
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:317)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:315)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
    at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1089)
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:859)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:550)

Jut in case if I comment out target configuration's class level annotation everything works fine. It seems both Database Configurations are conflicting with each other.

like image 746
Alex Avatar asked Sep 19 '18 08:09

Alex


Video Answer


2 Answers

@Primary must be used exactly on one bean among the required types.

Extract from @Primary javadoc

Indicates that a bean should be given preference when multiple candidates are qualified to autowire a single-valued dependency. If exactly one 'primary' bean exists among the candidates, it will be the autowired value.

like image 138
Sukhpal Singh Avatar answered Oct 05 '22 23:10

Sukhpal Singh


You have an error in your code. You specify

@Primary 

Annotation for both of datasources, therefore Spring claims. So you need to remove this annotation from one of your class and all will be ok.

Also please note that Primary annotation is helpful whenever we’re going to implicitly or explicitly inject the transaction manager without specifying which one by name.

like image 34
Alexander Petrov Avatar answered Oct 05 '22 23:10

Alexander Petrov