Mehrere Datenbanken mit Spring Boot und Spring JDBC anbinden

22. Mai 2018

Ohne manuelle Konfiguration kann nur eine Datenbank mit Spring Boot genutzt werden. Soll eine Anwendung mehrere Datenbanken anbinden, ist hingegen etwas Handarbeit nötig. Dieser Artikel zeigt die dazu notwendige Konfiguration unter Verwendung von Spring Boot 2, Spring JDBC und zwei MySQL Datenbanken.

Konfiguration

Zunächst müssen die Einstellungen für die anzubindenden Datenbanken in der application.properties hinterlegt werden:

db1.datasource.jdbc-url=jdbc:mysql://localhost:3306/db1
db1.datasource.username=root
db1.datasource.password=login

db2.datasource.jdbc-url=jdbc:mysql://localhost:3306/db2
db2.datasource.username=root
db2.datasource.password=login

Um die Einstellungen einzubinden, wird folgende Konfiguration benötigt:

package example;

import javax.sql.DataSource;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@Configuration
@EnableTransactionManagement
class DatabaseConfig {

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

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

    @Bean
    @Primary
    public PlatformTransactionManager transactionManager1() {
        return new DataSourceTransactionManager(this.dataSource1());
    }

    @Bean
    public PlatformTransactionManager transactionManager2() {
        return new DataSourceTransactionManager(this.dataSource2());
    }

    @Bean
    public JdbcTemplate jdbc1() {
        return new JdbcTemplate(this.dataSource1());
    }

    @Bean
    public JdbcTemplate jdbc2() {
        return new JdbcTemplate(this.dataSource2());
    }

}

Bei der Konfiguration der DataSources ist zu beachten, dass das Präfix bei der @ConfigurationProperties Annotation mit dem in der Konfigurationsdatei übereinstimmt.

@EnableTransactionManagement aktiviert die annotationsbasierte Transaktionssteuerung von Spring. Ohne die Angabe eines primären (@Primary) Transaktionsmanagers quittiert Spring den Dienst. Es wird bewusst auf die Konfiguration eines Transaktionsmanagers verzichtet, der Transaktionen über mehrere Datenbanken hinweg unterstützt. Dieses Thema wird in einem folgenden Artikel behandelt und nach Veröffentlichung hier verlinkt.

Des Weiteren wird je DataSource ein JdbcTemplate konfiguriert. Zusätzlich dazu oder stattdessen kann natürlich auch ein NamedParameterJdbcTemplate konfiguriert werden.

Verwendung

Das folgende Beispiel zeigt, wie die beiden Datenbanken nun abgefragt werden können:

package example;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class ExampleService {

    private final JdbcTemplate jdbc1;
    private final JdbcTemplate jdbc2;

    @Autowired
    public ExampleService(final JdbcTemplate jdbc1, final JdbcTemplate jdbc2) {
        this.jdbc1 = jdbc1;
        this.jdbc2 = jdbc2;
    }

    @Transactional
    public int queryDatabase1() {
        return this.jdbc1.queryForObject("SELECT 1", int.class);
    }

    @Transactional(transactionManager = "transactionManager2")
    public int queryDatabase2() {
        return this.jdbc2.queryForObject("SELECT 1", int.class);
    }

}

Die JdbcTemplates werden anhand ihres Namens injiziert. Beide Methoden werden jeweils in einer Transaktion ausgeführt. Ohne explizite Angabe des Transaktionsmanagers wird der primäre Transaktionsmanager verwendet, weshalb die Angabe bei der Methode queryDatabase1 weggelassen werden kann.