Zum Hauptinhalt springen

Konfiguration mehrerer Datenquellen in einem Spring Boot-Projekt

Spring Database
Autor
Harpal Singh
Software Engineer
Übersetzt von
Namastecode
Inhaltsverzeichnis

1. Einführung

In diesem kurzen Tutorial werden wir lernen, wie man mehrere Datenquellen in einer Spring Boot-Anwendung konfiguriert.

Stellen wir uns vor, wir arbeiten an einer E-Commerce-App und benötigen zwei separate Datenbanken, um Kunden und Bestellungen zu speichern. Wir werden diese als separate Datenquellen konfigurieren und sehen, wie wir sie in unserer Anwendung nutzen können.

2. Konfigurieren von Eigenschaften

Lassen Sie uns damit beginnen, einige Eigenschaften in application.properties für unsere Customer- und Order-Datenquellen zu definieren:

#Customer db
spring.datasource.customer.url = [url]
spring.datasource.customer.username = [username]
spring.datasource.customer.password = [password] 
spring.datasource.customer.driverClassName = org.postgresql.Driver

#Order db
spring.datasource.order.url = [url]
spring.datasource.order.username = [username]
spring.datasource.order.password = [password]
spring.datasource.order.driverClassName = org.sqlite.JDBC

Oben definieren wir die Datenquellen für die beiden Datenbanken und deren jeweilige Eigenschaften. Darüber hinaus können wir verschiedene Arten von Datenbanken konfigurieren.

Nun können wir diese Eigenschaften in unserer Anwendung verwenden.

3. Konfigurieren von Datenquellen

Als nächstes erstellen wir eine Konfigurationsklasse, in der wir zwei DataSource Beans für jede Datenbank definieren:

@Configuration
public class CustomerOrderDataSourcesConfiguration {

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

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

}

Wir benennen unsere Beans explizit, damit wir mithilfe der @Qualifier Annotation entscheiden können, auf welche Datenquelle wir uns beziehen möchten. Zusätzlich haben wir die customerDataSource als @Primary markiert und werden sie als Hauptdatenbank für unsere Anwendung betrachten. Falls wir also die @Qualifier Annotation nicht verwenden, wird Spring die Kundendatenquelle dort einfügen, wo sie benötigt wird.

Lassen Sie uns sehen, wie wir diese Beans verwenden können.

4. Verwendung der Datenquellen

Wir können die beiden Datenquellen bei Bedarf in unsere Anwendung injizieren. Zum Beispiel können wir die @Autowired Annotation verwenden, um die Datenquelle in eine Serviceklasse zu injizieren:

@Autowired  
@Qualifier("orderDataSource")
private DataSource orderDataSource;

Allerdings möchten wir meistens die Datenquelle mit einem Spring Data JPA-Repository verbinden und dieses verwenden, anstatt direkt mit der Datenquelle zu interagieren.

Schauen wir uns an, wie wir das als Nächstes konfigurieren können.

5. Konfigurieren von @Repository mit einer spezifischen Datenquelle

Zuerst müssen wir eine Konfigurationsklasse erstellen, in der wir die Annotation @EnableJpaRepositories verwenden, um die Repositories zu scannen und die Entity Manager Factory für jede Datenquelle zu definieren:

@Configuration
@EnableJpaRepositories(
  basePackages = "com.namastecode.repositories.customer",
  entityManagerFactoryRef = "customerEntityManagerFactory", 
  transactionManagerRef = "customerTransactionManager"
)
public class CustomerDataSourceConfiguration{
  // More code
}

Nun müssen wir den entityManagerFactory Bean innerhalb dieser Klasse erstellen:

@Primary
@Bean(name = "customerEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean customerEntityManagerFactory(
  EntityManagerFactoryBuilder builder,
  @Qualifier("customerDataSource") DataSource dataSource) {
  return builder
    .dataSource(dataSource)
    .packages("com.namastecode.domain.customer") // entities package
    .persistenceUnit("customers")
    .build();
}

Als nächstes werden wir den transactionManager-Bean erstellen, sodass jede Datenquelle ihren eigenen Transaktionsmanager hat und sie sich nicht gegenseitig stören:

@Primary
@Bean(name = "customerTransactionManager")
public PlatformTransactionManager customerTransactionManager(
  @Qualifier("customerEntityManagerFactory") EntityManagerFactory customerEntityManagerFactory) {
  return new JpaTransactionManager(customerEntityManagerFactory);
}

Oben definieren wir beide Beans als @Primary, da die Kundendatenquelle die Standardquelle ist. Für die Bestelldatenquelle müssen wir das nicht tun.

Wir haben alles bereit, um unsere Entitäten im Paket com.namastecode.domain.customer zu erstellen:

@Entity
@Table(name = "customers")
public class Customer {
  // More code
}

Ähnlich können wir die Repositories unter dem Paket com.namastecode.repositories.customer erstellen:

@Repository
public interface CustomerRepository extends JpaRepository<Customer, Long> {
  // More code
}

Nun haben wir zwei separate Datenquellen mit ihren Ressourcen. Es könnte jedoch eine Situation auftreten, in der wir die Datenquellen so konfigurieren möchten, dass wir eine einzige Transaktion über beide Datenquellen hinweg verwenden. Lassen Sie uns als Nächstes sehen, wie wir das tun können.

6. Konfiguration eines Chained Transaction Manager

Wenn wir Transaktionen über beide Datenquellen hinweg durchführen möchten, können wir einen ChainedTransactionManager-Bean einrichten, den wir später in unseren Operationen verwenden können:

@Bean(name = "chainedTransactionManager")
public ChainedTransactionManager chainedTransactionManager(
  @Qualifier("customerTransactionManager") PlatformTransactionManager customerTransactionManager,
  @Qualifier("orderTransactionManager") PlatformTransactionManager orderTransactionManager) {
  return new ChainedTransactionManager(customerTransactionManager, orderTransactionManager);
}

Angenommen, wir haben einen CustomerOrderService, in dem wir sowohl CustomerRepository als auch OrderRepository verwenden. Um unseren verketteten Transaktionsmanager in dieser Service-Klasse zu nutzen, können wir den Transaktionsmanager explizit definieren:

@Service
public class CustomerOrderService {

  @Autowired
  private CustomerRepository customerRepository;
  
  @Autowired
  private OrderRepository orderRepository;

  @Transactional(transactionManager = "chainedTransactionManager") 
  public void updateCustomerAndOrder(Customer customer, Order order) {
    customerRepository.save(customer);
    orderRepository.save(order);
  }

}

Oben, wenn eine der Speichervorgänge fehlschlägt, wird die Transaktion für beide Datenquellen zurückgesetzt.

7. Fazit

In diesem Tutorial haben wir gelernt, wie wir mehrere Datenquellen in einer Spring Boot-Anwendung konfigurieren können.

Zuerst haben wir die Eigenschaften jeder Datenquelle definiert, die Datenquellen-Beans erstellt, das @Repository mit einer spezifischen Datenquelle konfiguriert und schließlich gesehen, wie man einen gemeinsamen Transaktionsmanager für beide Datenquellen konfiguriert.

Verwandte Artikel

Konfigurieren eines Java Web Clients für HTTPS-Anfragen
WebClient HTTPS Spring
# Ausführen von PostgreSQL in einem Docker-Container
DevOps Postgres Docker Database
Testcontainers in Spring Boot Integrationstests
Spring Testcontainers Testing