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.