Aller au contenu

Configurer plusieurs sources de données dans un Spring Boot

Spring Database
Auteur
Harpal Singh
Software Engineer
Traduit par
Namastecode
Sommaire

1. Introduction

Dans ce tutoriel rapide, nous allons apprendre à configurer plusieurs sources de données dans une application Spring Boot.

Imaginons que nous travaillons sur une application de commerce électronique et que nous avons besoin de deux bases de données distinctes pour stocker les clients et les commandes. Nous allons configurer celles-ci comme des sources de données séparées et voir comment les utiliser dans notre application.

2. Configuration des Propriétés

Commençons par définir quelques propriétés dans application.properties pour nos sources de données Client et Commande :

#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

Au-dessus, nous définissons les sources de données pour les deux bases de données et leurs propriétés respectives. De plus, nous pouvons configurer différents types de bases de données.

Maintenant, nous pouvons utiliser ces propriétés dans notre application.

3. Configuration des sources de données

Ensuite, nous allons créer une classe de configuration où nous définissons deux beans DataSource pour chaque base de données :

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

}

Nous nommons explicitement nos beans afin de pouvoir décider de la source de données à laquelle nous référer en utilisant l’annotation @Qualifier. De plus, nous avons marqué le customerDataSource comme @Primary, et nous le considérerons comme la base de données principale de notre application. Donc, dans le cas où nous n’utiliserions pas l’annotation @Qualifier, Spring injectera la source de données client là où cela est nécessaire.

Voyons comment nous pouvons utiliser ces beans.

4. Utilisation des Sources de Données

Nous pouvons injecter les deux sources de données lorsque cela est nécessaire dans notre application. Par exemple, nous pouvons utiliser l’annotation @Autowired pour injecter la source de données à l’intérieur d’une classe de service :

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

Cependant, la plupart du temps, nous souhaitons connecter la source de données à un dépôt Spring Data JPA et utiliser cela au lieu d’interagir directement avec la source de données.

Voyons comment configurer cela ensuite.

5. Configuration de @Repository Avec une Source de Données Spécifique

Tout d’abord, nous devons créer une classe de configuration où nous utilisons l’annotation @EnableJpaRepositories pour scanner les dépôts et définir la fabrique d’entités pour chaque source de données :

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

Maintenant, nous devons créer le bean entityManagerFactory à l’intérieur de cette classe :

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

Ensuite, nous allons créer le bean transactionManager afin que chaque source de données dispose de son propre gestionnaire de transactions et qu’il n’y ait pas de conflit entre elles :

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

Au-dessus, nous définissons les deux beans comme @Primary, car la source de données des clients est celle par défaut. Nous n’avons pas besoin de faire cela pour la source de données des commandes.

Nous avons tout en place pour créer nos entités sous le paquet com.namastecode.domain.customer :

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

De la même manière, nous pouvons créer les dépôts sous le package com.namastecode.repositories.customer :

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

Maintenant, nous avons deux sources de données distinctes avec leurs ressources. Cependant, il peut y avoir une situation où nous souhaitons configurer les sources de données, afin d’utiliser une seule transaction sur les deux sources de données. Voyons maintenant comment faire cela.

6. Configurer un gestionnaire de transaction chaînée

Si nous voulons effectuer des transactions entre les deux sources de données, nous pouvons configurer un bean ChainedTransactionManager que nous pourrons utiliser par la suite dans nos opérations :

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

Ensuite, supposons que nous avons un CustomerOrderService où nous utilisons à la fois CustomerRepository et OrderRepository. Donc, pour utiliser notre gestionnaire de transactions chaînées dans cette classe de service, nous pouvons définir explicitement le gestionnaire de transactions :

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

}

Au-dessus, si l’une des opérations de sauvegarde échoue, la transaction sera annulée pour les deux sources de données.

7. Conclusion

Dans ce tutoriel, nous avons appris à configurer plusieurs sources de données dans une application Spring Boot.

Tout d’abord, nous avons défini les propriétés de chaque source de données, créé les beans de source de données, configuré le @Repository avec une source de données spécifique, et enfin, nous avons vu comment configurer un gestionnaire de transactions partagé pour les deux sources de données.

Articles connexes

Configurer un client Web Java pour les requêtes HTTPS
WebClient HTTPS Spring
Exécuter PostgreSQL dans un conteneur Docker
DevOps Postgres Docker Database
Testcontainers dans les tests d'intégration Spring Boot
Spring Testcontainers Testing