Spring Boot - Multiple Data-Sources

This article is about configuring multiple data-sources in Spring Boot applications.

The convention over configuration in Spring Boot is to configure a single data-source. This is done via the spring.datasource.* properties and the configuraton classes from the Spring Boot package org.springframework.boot.autoconfigure.jdbc.*.

When having a look at class org.springframework.boot.autoconfigure.jdbc.DataSourceConfiguration you will see that it defines multiple @Configuration classes for the various environments where data-sources can be defined (e.g. with Tomcat, in a DBCP2 connection pool etc.). Each of the @Bean methods configure a data-source. The data-source properties are set with class org.springframework.boot.autoconfigure.jdbc.DataSourceProperties which is basically a @ConfigurationProperties container for the property values:

@ConfigurationProperties(prefix = "spring.datasource")
public class DataSourceProperties
        implements BeanClassLoaderAware, EnvironmentAware, InitializingBean {

    private ClassLoader classLoader;

    private Environment environment;

    /**
     * Name of the datasource.
     */
    private String name = "testdb";

    /**
     * Generate a random datasource name.
     */
    private boolean generateUniqueName;

    /**
     * Fully qualified name of the connection pool implementation to use. By default, it
     * is auto-detected from the classpath.
     */
    private Class<? extends DataSource> type;

    /**
     * Fully qualified name of the JDBC driver. Auto-detected based on the URL by default.
     */
    private String driverClassName;

    /**
     * JDBC url of the database.
     */
    private String url;

    /**
     * Login user of the database.
     */
    private String username;

    /**
     * Login password of the database.
     */
    private String password;

    /**
     * JNDI location of the datasource. Class, url, username & password are ignored when
     * set.
     */
    private String jndiName;
    
    // ...

As you can see above, all the properties found with prefix spring.datasource are bound against an instance of this class.

Now let’s say we want to introduce a second data-source besides the Spring Boot configured one. In order to do so, we have to overwrite the default DataSource bean and mark it as primary one to be used by Spring and we have to define a second DataSource bean.

When configuring the beans, we can reuse Spring Boot’s DataSourceProperties in that we use it in a @Bean method we annotate with @ConfigurationProperties. In such a way we can have exactly the same property names for configuring the data-source bean as Spring Boot, but mapped to another prefix:

@Configuration
public class DataSourceConfiguration {
    
    @Bean
    @Primary
    @ConfigurationProperties("app.datasource")
    public DataSourceProperties appDataSourceProperties() {
        return new DataSourceProperties();
    }
    
    @Bean
    @Primary
    public DataSource appDataSource() {
        return appDataSourceProperties().initializeDataSourceBuilder().build();
    }
}

The custom DataSourceConfiguration from above defines a new primary java.sql.DataSource bean. The @Primary annotation is important so that this data-source is treated as primary data-source that has precedence for Spring Boot’s auto-configuration mechanism e.g. when creating an EntityManagerFactoryBean etc.

Let’s add the second data-source:

@Configuration
public class DataSourceConfiguration {
    
    @Bean
    @Primary
    @ConfigurationProperties("app.datasource")
    public DataSourceProperties appDataSourceProperties() {
        return new DataSourceProperties();
    }
    
    @Bean
    @Primary
    public DataSource appDataSource() {
        return appDataSourceProperties().initializeDataSourceBuilder().build();
    }
    
    @Configuration
    @ConditionalOnProperty(name = "app.opt.datasource.active")
    public static class SomeOtherConfiguration {
        
        @Bean
        public DataSourceProperties appOptDataSourceProperties() {
            return new DataSourceProperties();
        }
        
        @Bean
        public DataSource appOptDataSource() {
            return appOptDataSourceProperties().initializeDataSourceBuilder().build();
        }
    }
}

We do so by adding a nested @Configuration being only active if the property value app.opt.datasource.active is set to true. That’s basically what @ConditionalOnProperty(name = "app.optdatasource.active") does. It checks the app.opt.datasource.active property and if it is present and true, the @Configuration will get executed and the second data-source bean will be defined.

If course, you could even add more data-sources, for example, you could add data-sources depending on the current Spring environment/profile currently active. But the general scheme would be the same.

Summary

This article showed how to configure multiple data-sources in a Spring Boot application. Besides the default data-source, we add an optional data-source that can be used from within the application code for special purposes.