Salta al contenuto principale

Configurare più sorgenti di dati in un Spring Boot

Spring Database
Autore
Harpal Singh
Software Engineer
Tradotto da
Namastecode
Indice dei contenuti

1. Introduzione

In questo breve tutorial, impareremo come configurare più fonti di dati in un’applicazione Spring Boot.

Immaginiamo di lavorare su un’app di e-commerce e di aver bisogno di due database separati per memorizzare i clienti e gli ordini. Configureremo questi come fonti di dati separate e vedremo come usarle nella nostra applicazione.

2. Configurazione delle Proprietà

Iniziamo definendo alcune proprietà in application.properties per i nostri dati sui Clienti e sugli Ordini:

#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

Sopra, definiamo le fonti di dati per i due database e le loro rispettive proprietà. Inoltre, possiamo configurare diversi tipi di database.

Ora, possiamo utilizzare queste proprietà all’interno della nostra applicazione.

3. Configurazione delle Fonti di Dati

Successivamente, creeremo una classe di configurazione in cui definiamo due bean DataSource per ciascun database:

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

}

Stiamo nominando esplicitamente i nostri bean in modo da poter decidere a quale sorgente di dati fare riferimento utilizzando l’annotazione @Qualifier. Inoltre, abbiamo contrassegnato customerDataSource come @Primary, e lo considereremo il database principale per la nostra applicazione. Quindi, nel caso in cui non utilizziamo l’annotazione @Qualifier, Spring inietterà la sorgente di dati del cliente dove è necessario.

Vediamo come possiamo utilizzare questi bean.

4. Utilizzo delle Fonti di Dati

Possiamo iniettare le due fonti di dati quando necessario nella nostra applicazione. Ad esempio, possiamo utilizzare l’annotazione @Autowired per iniettare la fonte di dati all’interno di una classe di servizio:

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

Tuttavia, la maggior parte delle volte, vogliamo collegare la fonte di dati a un repository Spring Data JPA e utilizzare quello invece di interagire direttamente con la fonte di dati.

Vediamo come configurarlo adesso.

5. Configurare @Repository Con una Fonte Dati Specifica

Prima di tutto, dobbiamo creare una classe di configurazione in cui utilizziamo l’annotazione @EnableJpaRepositories per scansionare i repository e definire la factory dell’entity manager per ogni sorgente di dati:

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

Ora, dobbiamo creare il bean entityManagerFactory all’interno di questa 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();
}

Successivamente, creeremo il bean transactionManager in modo che ogni sorgente dati abbia il proprio gestore delle transazioni e non ci siano conflitti tra di loro:

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

Sopra, definiamo entrambi i bean come @Primary, poiché la sorgente dati del cliente è quella predefinita. Non dobbiamo farlo per la sorgente dati degli ordini.

Abbiamo tutto pronto per creare le nostre entità sotto il pacchetto com.namastecode.domain.customer:

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

Allo stesso modo, possiamo creare i repository sotto il pacchetto com.namastecode.repositories.customer:

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

Ora, abbiamo due fonti di dati separate con le loro risorse. Tuttavia, potrebbe esserci una situazione in cui desideriamo configurare le fonti di dati, quindi vogliamo utilizzare una singola transazione su entrambe le fonti di dati. Vediamo ora come fare.

6. Configurare un Gestore di Transazioni Collegato

Se vogliamo eseguire transazioni su entrambe le fonti di dati, possiamo configurare un bean ChainedTransactionManager che, in seguito, possiamo utilizzare nelle nostre operazioni:

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

Successivamente, supponiamo di avere un CustomerOrderService dove stiamo utilizzando sia CustomerRepository che OrderRepository. Quindi, per utilizzare il nostro gestore di transazioni concatenato in questa classe di servizio, possiamo definire esplicitamente il gestore di transazioni:

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

}

Sopra, se una qualsiasi delle operazioni di salvataggio fallisce, la transazione verrà annullata per entrambe le fonti di dati.

7. Conclusione

In questo tutorial, abbiamo imparato come configurare più fonti di dati in un’applicazione Spring Boot.

Prima di tutto, abbiamo definito le proprietà di ciascuna sorgente dati, creato i bean della sorgente dati, configurato il @Repository con una sorgente dati specifica e, infine, abbiamo visto come configurare un gestore di transazioni condiviso per entrambe le sorgenti dati.

Related

Configurare un Client Web Java per Richieste HTTPS
WebClient HTTPS Spring
Eseguire PostgreSQL in un Contenitore Docker
DevOps Postgres Docker Database
Testcontainers nei Test di Integrazione di Spring Boot
Spring Testcontainers Testing