Spring Boot konfiguriert und verwendet zwei Datenquellen

Lesezeit: 11 Minuten

Benutzer-Avatar
Juventus

Wie kann ich zwei Datenquellen konfigurieren und verwenden?

Hier ist zum Beispiel, was ich für die erste Datenquelle habe:

application.properties

#first db
spring.datasource.url = [url]
spring.datasource.username = [username]
spring.datasource.password = [password]
spring.datasource.driverClassName = oracle.jdbc.OracleDriver

#second db ...

Anwendungsklasse

@SpringBootApplication
public class SampleApplication
{
    public static void main(String[] args) {
        SpringApplication.run(SampleApplication.class, args);
    }
}

Wie ändere ich application.properties eine weitere Datenquelle hinzufügen? Wie kann ich es automatisch verdrahten, damit es von einem anderen Repository verwendet wird?

Benutzer-Avatar
K. Shiva Prasad Reddy

Bitte schön.

Fügen Sie Ihre Datei application.properties hinzu:

#first db
spring.datasource.url = [url]
spring.datasource.username = [username]
spring.datasource.password = [password]
spring.datasource.driverClassName = oracle.jdbc.OracleDriver

#second db ...
spring.secondDatasource.url = [url]
spring.secondDatasource.username = [username]
spring.secondDatasource.password = [password]
spring.secondDatasource.driverClassName = oracle.jdbc.OracleDriver

Fügen Sie in jeder mit @Configuration annotierten Klasse die folgenden Methoden hinzu:

@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();
}

  • Schauen Sie sich an baeldung.com/spring-data-jpa-multiple-databases die das gleiche beschreibt, was Sie suchen.

    – K. Siva Prasad Reddy

    21. Mai 2015 um 2:34 Uhr

  • Manchmal müssen Sie möglicherweise datasource, transactionManager und SqlSessionFactory als primär all zuweisen.

    – Dai Kaixian

    15. Dezember 2016 um 10:37 Uhr

  • @K. Siva Prasad Reddy OK, aber ich habe 2 verschiedene JPARrepositories – woher weiß Spring Boot, welche DataSource verwendet werden soll? Jedes JPARepository sollte eine andere Datenbank verwenden

    – Matley

    18. März 2019 um 14:50 Uhr

  • @Matley Dieser Blogbeitrag javadevjournal.com/spring-boot/… könnte das sein, was Sie suchen.

    – K. Siva Prasad Reddy

    19. März 2019 um 9:52 Uhr

  • @K.SivaPrasadReddy zum Konfigurieren mehrerer Datenquellen sollten sich alle Datenbanken auf demselben Server befinden?

    – Krisch

    2. Juni 2019 um 5:13 Uhr

Benutzer-Avatar
Surasin Tancharoen

Update 2022-05-29 mit Spring Boot 1.5.8.RELEASE, das mit Spring Boot 2.x funktionieren sollte

Die meisten Antworten geben nicht an, wie sie verwendet werden (als Datenquelle selbst und als Transaktion), sondern nur, wie sie konfiguriert werden.

Außerdem sollten Sie wissen, wie Sie Transaktionen beider Datenquellen gleichzeitig festschreiben/zurücksetzen.

Sie können das ausführbare Beispiel und einige Erklärungen in sehen https://github.com/surasint/surasint-examples/tree/master/spring-boot-jdbi/10_spring-boot-two-databases (sehen Sie, was Sie in README.txt ausprobieren können)

Ich habe hier einen Code kopiert.

Zuerst müssen Sie application.properties so setzen

#Database
database1.datasource.url=jdbc:mysql://localhost/testdb
database1.datasource.username=root
database1.datasource.password=root
database1.datasource.driver-class-name=com.mysql.jdbc.Driver

database2.datasource.url=jdbc:mysql://localhost/testdb2
database2.datasource.username=root
database2.datasource.password=root
database2.datasource.driver-class-name=com.mysql.jdbc.Driver

Definieren Sie sie dann wie folgt als Anbieter (@Bean):

@Bean(name = "datasource1")
@ConfigurationProperties("database1.datasource")
@Primary
public DataSource dataSource(){
    return DataSourceBuilder.create().build();
}

@Bean(name = "datasource2")
@ConfigurationProperties("database2.datasource")
public DataSource dataSource2(){
    return DataSourceBuilder.create().build();
}

Beachten Sie, dass ich habe @Bean(name="datasource1") und @Bean(name="datasource2")dann können Sie es verwenden, wenn wir eine Datenquelle als benötigen @Qualifier("datasource1") und @Qualifier("datasource2") zum Beispiel

@Qualifier("datasource1")
@Autowired
private DataSource dataSource;

Wenn Sie sich für Transaktionen interessieren, müssen Sie DataSourceTransactionManager für beide wie folgt definieren:

@Bean(name="tm1")
@Autowired
@Primary
DataSourceTransactionManager tm1(@Qualifier ("datasource1") DataSource datasource) {
    DataSourceTransactionManager txm  = new DataSourceTransactionManager(datasource);
    return txm;
}

@Bean(name="tm2")
@Autowired
DataSourceTransactionManager tm2(@Qualifier ("datasource2") DataSource datasource) {
    DataSourceTransactionManager txm  = new DataSourceTransactionManager(datasource);
    return txm;
}

Dann kannst du es gerne verwenden

@Transactional //this will use the first datasource because it is @primary

oder

@Transactional("tm2")

Der wichtigste Teil, von dem Sie kaum ein Beispiel finden werden: Wenn Sie eine Methode zum Commit/Rollback von Transaktionen beider Datenbanken wünschen, benötigen Sie ChainedTransactionManager für tm1 und tm2 wie folgt:

@Bean(name = "chainedTransactionManager")
public ChainedTransactionManager getChainedTransactionManager(@Qualifier ("tm1") DataSourceTransactionManager tm1, @Qualifier ("tm2") DataSourceTransactionManager tm2){
    return new ChainedTransactionManager(tm1, tm2);
}

Um sie zu verwenden, fügen Sie diese Annotation beispielsweise in einer Methode @Transactional(value=”chainedTransactionManager”) hinzu

@Transactional(value="chainedTransactionManager")
public void insertAll() {
    UserBean test = new UserBean();
    test.setUsername("username" + new Date().getTime());
    userDao.insert(test);

    userDao2.insert(test);
}

Das sollte reichen. Siehe Beispiel und Details im obigen Link.

  • Hallo @Surasin Tancharoen, wir versuchen, zwei Datenquellen mit denselben Daten zu behalten, damit die Anwendung bei einem Ausfall auf der anderen Datenquelle ausgeführt wird. Wird der obige Ansatz in Ordnung sein?

    – Arun Sudhakaran

    13. Mai 2020 um 5:02 Uhr

  • @ArunSudhakaran Nein. Diese Lösung funktioniert nicht als Backup. Wenn Sie nach hoher Verfügbarkeit suchen, verfügen die meisten Datenbanken bereits über Konfigurationen zum Ausführen mehrerer Datenbanken mit einer einzigen virtuellen IP. versuchen Sie es stattdessen.

    – Raja Anbazhagan

    8. Juli 2021 um 15:15 Uhr

  • Verknüpfung surasint.com/spring-boot-with-multiple-databases-example funktioniert nicht mehr.

    – Avec

    27. September 2021 um 11:49 Uhr

Benutzer-Avatar
Faraj Farook

Verweisen die offizielle Dokumentation


Das Erstellen von mehr als einer Datenquelle funktioniert genauso wie das Erstellen der ersten. Möglicherweise möchten Sie eine davon als @Primary markieren, wenn Sie die standardmäßige automatische Konfiguration für JDBC oder JPA verwenden (dann wird diese von allen @Autowired-Injektionen übernommen).

@Bean
@Primary
@ConfigurationProperties(prefix="datasource.primary")
public DataSource primaryDataSource() {
    return DataSourceBuilder.create().build();
}

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

  • Vielen Dank für den Link zur offiziellen Dokumentation dazu.

    – Manoj Shrestha

    10. September 2021 um 19:29 Uhr

Ich musste auch eine Verbindung zu 2 Datenquellen aus der Spring Boot-Anwendung herstellen, und es war nicht einfach – die in der erwähnte Lösung Spring Boot-Dokumentation hat nicht funktioniert. Nach langem Suchen im Internet habe ich es zum Laufen gebracht und die Hauptidee war übernommen Dieser Artikel und viele andere Orte.

Die folgende Lösung ist eingeschrieben Kotlin und arbeitet mit Springboot 2.1.3 und Hibernate Core 5.3.7. Das Hauptproblem war, dass es nicht ausreichte, einfach nur anders einzurichten Datenquelle configs, aber es war auch notwendig zu konfigurieren EntityManagerFactory und Transaktionsmanager für beide Datenbanken.

Hier ist die Konfiguration für die erste (primäre) Datenbank:

@Configuration
@EnableJpaRepositories(
    entityManagerFactoryRef = "firstDbEntityManagerFactory",
    transactionManagerRef = "firstDbTransactionManager",
    basePackages = ["org.path.to.firstDb.domain"]
)
@EnableTransactionManagement
class FirstDbConfig {

    @Bean
    @Primary
    @ConfigurationProperties(prefix = "spring.datasource.firstDb")
    fun firstDbDataSource(): DataSource {
        return DataSourceBuilder.create().build()
    }

    @Primary
    @Bean(name = ["firstDbEntityManagerFactory"])
    fun firstDbEntityManagerFactory(
        builder: EntityManagerFactoryBuilder,
        @Qualifier("firstDbDataSource") dataSource: DataSource
    ): LocalContainerEntityManagerFactoryBean {
        return builder
            .dataSource(dataSource)
            .packages(SomeEntity::class.java)
            .persistenceUnit("firstDb")
            // Following is the optional configuration for naming strategy
            .properties(
                singletonMap(
                    "hibernate.naming.physical-strategy",
                    "org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl"
                )
            )
            .build()
    }

    @Primary
    @Bean(name = ["firstDbTransactionManager"])
    fun firstDbTransactionManager(
        @Qualifier("firstDbEntityManagerFactory") firstDbEntityManagerFactory: EntityManagerFactory
    ): PlatformTransactionManager {
        return JpaTransactionManager(firstDbEntityManagerFactory)
    }
}

Und das ist die Konfiguration für die zweite Datenbank:

@Configuration
@EnableJpaRepositories(
    entityManagerFactoryRef = "secondDbEntityManagerFactory",
    transactionManagerRef = "secondDbTransactionManager",
    basePackages = ["org.path.to.secondDb.domain"]
)
@EnableTransactionManagement
class SecondDbConfig {

    @Bean
    @ConfigurationProperties("spring.datasource.secondDb")
    fun secondDbDataSource(): DataSource {
        return DataSourceBuilder.create().build()
    }

    @Bean(name = ["secondDbEntityManagerFactory"])
    fun secondDbEntityManagerFactory(
        builder: EntityManagerFactoryBuilder,
        @Qualifier("secondDbDataSource") dataSource: DataSource
    ): LocalContainerEntityManagerFactoryBean {
        return builder
            .dataSource(dataSource)
            .packages(EntityFromSecondDb::class.java)
            .persistenceUnit("secondDb")
            .build()
    }

    @Bean(name = ["secondDbTransactionManager"])
    fun secondDbTransactionManager(
        @Qualifier("secondDbEntityManagerFactory") secondDbEntityManagerFactory: EntityManagerFactory
    ): PlatformTransactionManager {
        return JpaTransactionManager(secondDbEntityManagerFactory)
    }
}

Die Eigenschaften für Datenquellen lauten wie folgt:

spring.datasource.firstDb.jdbc-url=
spring.datasource.firstDb.username=
spring.datasource.firstDb.password=

spring.datasource.secondDb.jdbc-url=
spring.datasource.secondDb.username=
spring.datasource.secondDb.password=

Problem mit Eigenschaften war, dass ich definieren musste jdbc-url Anstatt von URL denn sonst hatte ich eine ausnahme.

p.s
Außerdem haben Sie möglicherweise unterschiedliche Namensschemata in Ihren Datenbanken, was bei mir der Fall war. Da Hibernate 5 nicht alle vorherigen Benennungsschemata unterstützt, musste ich die Lösung aus dieser Antwort verwenden – vielleicht hilft es auch jemandem.

Benutzer-Avatar
Chandra Shekhar Goka

Hier ist die Komplettlösung

#First Datasource (DB1)
db1.datasource.url: url
db1.datasource.username:user
db1.datasource.password:password

#Second Datasource (DB2)
db2.datasource.url:url
db2.datasource.username:user
db2.datasource.password:password

Da wir Zugriff auf zwei verschiedene Datenbanken (db1, db2) erhalten, müssen wir jede Datenquellenkonfiguration separat konfigurieren:

public class DB1_DataSource {
@Autowired
private Environment env;
@Bean
@Primary
public LocalContainerEntityManagerFactoryBean db1EntityManager() {
    LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
    em.setDataSource(db1Datasource());
    em.setPersistenceUnitName("db1EntityManager");
    HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
    em.setJpaVendorAdapter(vendorAdapter);
    HashMap<string, object=""> properties = new HashMap<>();
    properties.put("hibernate.dialect",
            env.getProperty("hibernate.dialect"));
    properties.put("hibernate.show-sql",
            env.getProperty("jdbc.show-sql"));
    em.setJpaPropertyMap(properties);
    return em;
}

@Primary
@Bean
public DataSource db1Datasource() {

    DriverManagerDataSource dataSource
            = new DriverManagerDataSource();
    dataSource.setDriverClassName(
            env.getProperty("jdbc.driver-class-name"));
    dataSource.setUrl(env.getProperty("db1.datasource.url"));
    dataSource.setUsername(env.getProperty("db1.datasource.username"));
    dataSource.setPassword(env.getProperty("db1.datasource.password"));

    return dataSource;
}

@Primary
@Bean
public PlatformTransactionManager db1TransactionManager() {

    JpaTransactionManager transactionManager
            = new JpaTransactionManager();
    transactionManager.setEntityManagerFactory(
            db1EntityManager().getObject());
    return transactionManager;
}
}

Zweite Datenquelle:

public class DB2_DataSource {

@Autowired
private Environment env;

@Bean
public LocalContainerEntityManagerFactoryBean db2EntityManager() {
    LocalContainerEntityManagerFactoryBean em
            = new LocalContainerEntityManagerFactoryBean();
    em.setDataSource(db2Datasource());
    em.setPersistenceUnitName("db2EntityManager");
    HibernateJpaVendorAdapter vendorAdapter
            = new HibernateJpaVendorAdapter();
    em.setJpaVendorAdapter(vendorAdapter);
    HashMap<string, object=""> properties = new HashMap<>();
    properties.put("hibernate.dialect",
            env.getProperty("hibernate.dialect"));
    properties.put("hibernate.show-sql",
            env.getProperty("jdbc.show-sql"));
    em.setJpaPropertyMap(properties);
    return em;
}

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

    return dataSource;
}

@Bean
public PlatformTransactionManager db2TransactionManager() {
    JpaTransactionManager transactionManager
            = new JpaTransactionManager();
    transactionManager.setEntityManagerFactory(
            db2EntityManager().getObject());
    return transactionManager;
}
}

Hier finden Sie das vollständige Beispiel auf meinem Blog:
Spring Boot mit Konfiguration mehrerer Datenquellen

  • Dieser Code wird nicht ausgeführt, wenn 2 @primary-Anmerkungen in derselben Datenbank zusammenarbeiten.

    – Rohit Chaurasiya

    24. Juli 2020 um 2:51 Uhr

  • in der Tat, aber dieser Code erwähnt einen wichtigen Teil mit manueller Einstellung der Eigenschaften anstelle von schlicht und einfach return DataSourceBuilder.create().build(); was anscheinend nicht funktioniert, daher geht meine positive Bewertung hierher

    – im_berüchtigt

    20. Oktober 2020 um 15:38 Uhr

Benutzer-Avatar
Raj Ranjan

# Here '1stDB' is the database name
spring.datasource.url=jdbc:mysql://localhost/A
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.jdbc.Driver


# Here '2ndDB' is the database name
spring.second-datasourcee.url=jdbc:mysql://localhost/B
spring.second-datasource.username=root
spring.second-datasource.password=root
spring.second-datasource.driver-class-name=com.mysql.jdbc.Driver


    @Bean
    @Primary
    @ConfigurationProperties(prefix = "spring.datasource")
    public DataSource firstDataSource() {
        return DataSourceBuilder.create().build();
    }

    @Bean
    @ConfigurationProperties(prefix = "spring.second-datasource")
    public DataSource secondDataSource() {
       return DataSourceBuilder.create().build();
    }

  • Dieser Code wird nicht ausgeführt, wenn 2 @primary-Anmerkungen in derselben Datenbank zusammenarbeiten.

    – Rohit Chaurasiya

    24. Juli 2020 um 2:51 Uhr

  • in der Tat, aber dieser Code erwähnt einen wichtigen Teil mit manueller Einstellung der Eigenschaften anstelle von schlicht und einfach return DataSourceBuilder.create().build(); was anscheinend nicht funktioniert, daher geht meine positive Bewertung hierher

    – im_berüchtigt

    20. Oktober 2020 um 15:38 Uhr

Benutzer-Avatar
Anil Konduru

Meine Anforderung war etwas anders, verwendete aber zwei Datenquellen.

Ich habe zwei Datenquellen für dieselben JPA-Entitäten aus demselben Paket verwendet. Einer zum Ausführen von DDL beim Serverstart zum Erstellen/Aktualisieren von Tabellen und ein weiterer für DML zur Laufzeit.

Die DDL-Verbindung sollte geschlossen werden, nachdem DDL-Anweisungen ausgeführt wurden, um die weitere Verwendung von Superuser-Vorrechten an beliebiger Stelle im Code zu verhindern.

Eigenschaften

spring.datasource.url=jdbc:postgresql://Host:port
ddl.user=ddluser
ddl.password=ddlpassword
dml.user=dmluser
dml.password=dmlpassword
spring.datasource.driver-class-name=org.postgresql.Driver

Konfigurationsklassen für Datenquellen

//1. Konfigurationsklasse für DDL-Datenquelle

  public class DatabaseDDLConfig {
        @Bean
        public LocalContainerEntityManagerFactoryBean ddlEntityManagerFactoryBean() {
            LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
            PersistenceProvider persistenceProvider = new 
            org.hibernate.jpa.HibernatePersistenceProvider();
            entityManagerFactoryBean.setDataSource(ddlDataSource());
            entityManagerFactoryBean.setPackagesToScan(new String[] { 
            "com.test.two.data.sources"});
            HibernateJpaVendorAdapter vendorAdapter = new 
            HibernateJpaVendorAdapter();
            entityManagerFactoryBean.setJpaVendorAdapter(vendorAdapter);
            HashMap<String, Object> properties = new HashMap<>();
            properties.put("hibernate.dialect", 
            "org.hibernate.dialect.PostgreSQLDialect");
            properties.put("hibernate.physical_naming_strategy", 
            "org.springframework.boot.orm.jpa.hibernate.
            SpringPhysicalNamingStrategy");
            properties.put("hibernate.implicit_naming_strategy", 
            "org.springframework.boot.orm.jpa.hibernate.
            SpringImplicitNamingStrategy");
            properties.put("hibernate.hbm2ddl.auto", "update");
            entityManagerFactoryBean.setJpaPropertyMap(properties);
            entityManagerFactoryBean.setPersistenceUnitName("ddl.config");
            entityManagerFactoryBean.setPersistenceProvider(persistenceProvider);
            return entityManagerFactoryBean;
        }


    @Bean
    public DataSource ddlDataSource() {
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName(env.getProperty("spring.datasource.driver-class-name"));
        dataSource.setUrl(env.getProperty("spring.datasource.url"));
        dataSource.setUsername(env.getProperty("ddl.user");
        dataSource.setPassword(env.getProperty("ddl.password"));
        return dataSource;
    }

    @Bean
    public PlatformTransactionManager ddlTransactionManager() {
        JpaTransactionManager transactionManager = new JpaTransactionManager();
        transactionManager.setEntityManagerFactory(ddlEntityManagerFactoryBean().getObject());
        return transactionManager;
    }
}

//2. Konfigurationsklasse für DML-Datenquelle

public class DatabaseDMLConfig {

    @Bean
    @Primary
    public LocalContainerEntityManagerFactoryBean dmlEntityManagerFactoryBean() {
        LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
        PersistenceProvider persistenceProvider = new org.hibernate.jpa.HibernatePersistenceProvider();
        entityManagerFactoryBean.setDataSource(dmlDataSource());
        entityManagerFactoryBean.setPackagesToScan(new String[] { "com.test.two.data.sources" });
        JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
        entityManagerFactoryBean.setJpaVendorAdapter(vendorAdapter);
        entityManagerFactoryBean.setJpaProperties(defineJpaProperties());
        entityManagerFactoryBean.setPersistenceUnitName("dml.config");
        entityManagerFactoryBean.setPersistenceProvider(persistenceProvider);
        return entityManagerFactoryBean;
    }

    @Bean
    @Primary
    public DataSource dmlDataSource() {
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName(env.getProperty("spring.datasource.driver-class-name"));
        dataSource.setUrl(envt.getProperty("spring.datasource.url"));
        dataSource.setUsername("dml.user");
        dataSource.setPassword("dml.password");
        return dataSource;
    }

    @Bean
    @Primary
    public PlatformTransactionManager dmlTransactionManager() {
        JpaTransactionManager transactionManager = new JpaTransactionManager();
        transactionManager.setEntityManagerFactory(dmlEntityManagerFactoryBean().getObject());
        return transactionManager;
    }


  }

//Verwendung von DDL-Datenquellen im Code.

public class DDLServiceAtStartup {

//Import persistence unit ddl.config for ddl purpose.

@PersistenceUnit(unitName = "ddl.config")
private EntityManagerFactory entityManagerFactory;

public void executeDDLQueries() throws ContentServiceSystemError {
    try {
        EntityManager entityManager = entityManagerFactory.createEntityManager();
        entityManager.getTransaction().begin();
        entityManager.createNativeQuery("query to create/update table").executeUpdate();
        entityManager.flush();
        entityManager.getTransaction().commit();
        entityManager.close();

        //Close the ddl data source to avoid from further use in code.
        entityManagerFactory.close();
    } catch(Exception ex) {}
}

//Verwendung der DML-Datenquelle im Code.

public class DDLServiceAtStartup {
  @PersistenceUnit(unitName = "dml.config")
  private EntityManagerFactory entityManagerFactory;

  public void createRecord(User user) {
     userDao.save(user);
  }
}

1019140cookie-checkSpring Boot konfiguriert und verwendet zwei Datenquellen

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy