Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using Multiple DataSources With Spring

Tags:

spring

jpa

I am developing a Spring Boot application that uses Spring Data JPA and will need to connect to two different databases and I've run into an issue. When the application starts up it's throwing an exception basically stating that it doesn't know which datasource to use. Here is a snippet of my configuration class. The database properties(e.g. url, username, password) are defined in the application.properties file.

@Configuration
@ComponentScan(basePackages = {"com.test"})
@EnableJpaRepositories(basePackages = {"com.test.jpa.repository"})
@EnableAutoConfiguration
@EnableTransactionManagement
public class AppConfig {

@Bean(name = "dataSource1")
DataSource dataSource1(@Value("${db1.driverClassName}") String db1DriverClassName,  @Value("${db1.url}") String db1Url, @Value("${db1.username}") String db1Username, @Value("${db1.password}") String db1Password) {
    DataSource dataSource = new DataSource();

    dataSource.setDriverClassName(db1DriverClassName);
    dataSource.setUrl(db1Url);
    dataSource.setUsername(db1Username);
    dataSource.setPassword(db1Password);

    return dataSource;
}

@Bean(name = "dataSource2")
DataSource dataSource2(@Value("${db2.driverClassName}") String db2DriverClassName, @Value("${db2.url}") String db2Url, @Value("${db2.username}") String db2Username, @Value("${db2.password}") String db2Password) {
    DataSource dataSource = new DataSource();

    dataSource.setDriverClassName(db2DriverClassName);
    dataSource.setUrl(db2Url);
    dataSource.setUsername(db2Username);
    dataSource.setPassword(db2Password);

    return dataSource;
}

@Bean(name = "jpaVendorAdapter")
HibernateJpaVendorAdapter jpaVendorAdapter() {
    HibernateJpaVendorAdapter jpaVendorAdapter = new HibernateJpaVendorAdapter();

    jpaVendorAdapter.setDatabase(Database.ORACLE);
    jpaVendorAdapter.setShowSql(false);
    jpaVendorAdapter.setDatabasePlatform("org.hibernate.dialect.Oracle10gDialect");

    return jpaVendorAdapter;
}

@Bean(name = "entityManagerFactory")
@Autowired
LocalContainerEntityManagerFactoryBean entityManagerFactory(@Qualifier("dataSource1") DataSource dataSource, HibernateJpaVendorAdapter jpaVendorAdapter) {
    LocalContainerEntityManagerFactoryBean entityManagerFactory = new LocalContainerEntityManagerFactoryBean();

    entityManagerFactory.setDataSource(dataSource);
    entityManagerFactory.setPersistenceXmlLocation("classpath:persistence.xml");
    entityManagerFactory.setPersistenceUnitName("springdatajpa");
    entityManagerFactory.setJpaVendorAdapter(jpaVendorAdapter);

    return entityManagerFactory;
}

@Bean(name = "transactionManager")
JpaTransactionManager transactionManager(LocalContainerEntityManagerFactoryBean entityManagerFactory) {
    JpaTransactionManager transactionManager = new JpaTransactionManager();
    transactionManager.setEntityManagerFactory(entityManagerFactory.getObject());

    return transactionManager;
}

Here is the exception that is being thrown:

Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private javax.sql.DataSource org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration.dataSource; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [javax.sql.DataSource] is defined: expected single matching bean but found 2: dataSource1,dataSource2
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:292)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1185)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:537)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:475)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:304)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:228)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:300)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:195)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:703)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:760)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:482)
at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:120)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:648)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:311)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:909)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:898)
at com.wth.service.logging.Application.main(Application.java:11)

I have the entityManagerFactory method annotated with the @Autowired annotation and the dataSource parameter preceded with the @Qualifier("dataSource1") annotation so this should work. I've done this before in other Spring applications and haven't had any issues. This is the first time I've come across this exception.

Does anyone have any ideas/thoughts as to what could be causing this?

Thanks, Chris

like image 252
user2634876 Avatar asked Jul 12 '26 07:07

user2634876


1 Answers

To get this working I used the following:

@SpringBootApplication(exclude = { 
  DataSourceAutoConfiguration.class, 
  HibernateJpaAutoConfiguration.class, 
  JpaRepositoriesAutoConfiguration.class, 
  DataSourceTransactionManagerAutoConfiguration.class})
public class App...

This is equivalent to:

@Configuration
@ComponentScan
@EnableAutoConfiguration(exclude = { 
  DataSourceAutoConfiguration.class, 
  HibernateJpaAutoConfiguration.class,
  JpaRepositoriesAutoConfiguration.class, 
  DataSourceTransactionManagerAutoConfiguration.class})

Then I had a separate Configuration class per data source that was defined as follows:

@Configuration
@EnableJpaRepositories(
  entityManagerFactoryRef="fooEntityManagerFactory", 
  transactionManagerRef="fooTransactionManager")
public class FooConfig {

  public static final String FOO_QUALIFIER = "Foo";

  @Bean @Qualifier(FOO_QUALIFIER)
  public DataSource fooDataSource() { ... }

  @Bean @Qualifier(FOO_QUALIFIER)
  public EntityManagerFactory fooEntityManagerFactory() { ... }

  @Bean @Qualifier(FOO_QUALIFIER)
  public PlatformTransactionManager fooTransactionManager(
    @Qualifier(FOO_QUALIFIER) EntityManagerFactory emf) { ... }
}
like image 167
Basil Vandegriend Avatar answered Jul 19 '26 01:07

Basil Vandegriend



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!