Search code examples
javaspring-bootdatasource

Multiple Datasources fallback


Updated after Suggestion:

I have got a Springboot Java Application and two datasources defined in application.properties:

spring.jpa.hibernate.ddl-auto=update

#online
spring.datasource.url=jdbc:mysql://172.17.0.2:3306/gcdb?allowPublicKeyRetrieval=true&useSSL=false
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driverClassName=com.mysql.jdbc.Driver 

#offline
spring.secondDatasource.url=jdbc:h2:mem:gcdboff
spring.secondDatasource.username=root
spring.secondDatasource.password=root
spring.secondDatasource.driverClassName=org.h2.Driver

# Enabling H2 Console
spring.h2.console.enabled=true
spring.h2.console.path=/h2

Both connection work fine after changing the credential manually from "datasource" to "secondDatasource".

But now I would like like to add a fallback. If "datasource" is not availeable, please take "secondDatasource".

I have found following soulution.

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;

import javax.sql.DataSource;

@SpringBootApplication
public class GCApplication {

    public static void main(String[] args) {
        SpringApplication.run(GCApplication.class, args);
    }
/**/
    @Bean
    @Primary
    @ConfigurationProperties(prefix="spring.datasource")
    public DataSource primaryDataSource() {
        return DataSourceBuilder.create().build();
    }

    @Bean
    @ConfigurationProperties(prefix="spring.secondDatasource")
    public DataSource secondaryDataSource() {
        return DataSourceBuilder.create().build();
    }
/**/
}

But now no one will run. The Application ends with following error and I don't have a clue how to correct that.

2018-07-06 09:52:30.805 ERROR 28073 --- [           main]     o.s.boot.SpringApplication               : Application run failed

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]: Unsatisfied dependency expressed through method 'entityManagerFactory' parameter 0; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'entityManagerFactoryBuilder' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]: Unsatisfied dependency expressed through method 'entityManagerFactoryBuilder' parameter 0; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'jpaVendorAdapter' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.orm.jpa.JpaVendorAdapter]: Factory method 'jpaVendorAdapter' threw exception; nested exception is java.lang.IllegalArgumentException: dataSource or dataSourceClassName or jdbcUrl is required.
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:732) ~[spring-beans-5.0.7.RELEASE.jar:5.0.7.RELEASE]
[...]

Thank you for help!


Solution

  • I have found a solution for my problem. I hope it can help you too. With the following code the second data connection is choosen, after the first failed to be connected:

    In GCApplication:

    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
    import org.springframework.boot.context.properties.ConfigurationProperties;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Primary;
    
    import javax.sql.DataSource;
    
    @SpringBootApplication
    public class GCApplication 
    {
        public static void main(String[] args) 
        {
            SpringApplication.run(GCApplication.class, args);
        }
    
        @Primary
        @Bean
        public DataSource getDataSource(
            @Qualifier("first") DataSourceProperties first,
            @Qualifier("second") DataSourceProperties second)
        {
            int i = 0;
            final DataSource firstDataSource = first.initializeDataSourceBuilder().build();
            final DataSource secondDataSource = second.initializeDataSourceBuilder().build();
            try
            {
                firstDataSource.getConnection();
                return firstDataSource;
            } 
            catch (Exception e) 
            {
                return secondDataSource;
            }
        }
    
        @Primary
        @Bean("first")
        @ConfigurationProperties(prefix = "spring.datasource")
        public DataSourceProperties primaryDataSource()     
        {
            final DataSourceProperties dataSourceProperties = new DataSourceProperties();
            dataSourceProperties.setUrl("jdbc:mysql://172.17.0.2:3306/gcdb?allowPublicKeyRetrieval=true&useSSL=false");
            dataSourceProperties.setDriverClassName("com.mysql.jdbc.Driver");
            dataSourceProperties.setUsername("root");
            dataSourceProperties.setPassword("root");
            return dataSourceProperties;
        }
    
        @Bean("second")
        public DataSourceProperties secondaryDataSource() {
            final DataSourceProperties dataSourceProperties = new DataSourceProperties();
            dataSourceProperties.setUrl("jdbc:h2:mem:gcdboff");
            dataSourceProperties.setDriverClassName("org.h2.Driver");
            dataSourceProperties.setUsername("root");
            dataSourceProperties.setPassword("root");
            return dataSourceProperties;
        }
    }
    

    in application.properties:

    spring.jpa.hibernate.ddl-auto=update
    spring.jpa.database=default
    
    # Enabling H2 Console
    spring.h2.console.enabled=true
    spring.h2.console.path=/h2