1. Introducción
En este breve tutorial, aprenderemos cómo configurar múltiples fuentes de datos en una aplicación Spring Boot.
Imaginemos que estamos trabajando en una aplicación de comercio electrónico y necesitamos dos bases de datos separadas para almacenar clientes y pedidos. Configuraremos estas como fuentes de datos separadas y veremos cómo utilizarlas en nuestra aplicación.
2. Configuración de Propiedades
Comencemos definiendo algunas propiedades en application.properties
para nuestras fuentes de datos de Cliente y Pedido:
#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
Arriba, definimos las fuentes de datos para las dos bases de datos y sus respectivas propiedades. Además, podemos configurar diferentes tipos de bases de datos.
Ahora, podemos usar estas propiedades dentro de nuestra aplicación.
3. Configuración de Fuentes de Datos
A continuación, crearemos una clase de configuración donde definiremos dos beans de DataSource
para cada base de datos:
@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();
}
}
Estamos nombrando explícitamente nuestros beans para poder decidir a qué fuente de datos referirnos utilizando la anotación @Qualifier
. Además, hemos marcado el customerDataSource
como @Primary
, y lo consideraremos la base de datos principal para nuestra aplicación. Así que, en caso de que no utilicemos la anotación @Qualifier
, Spring inyectará la fuente de datos del cliente donde sea necesario.
Veamos cómo podemos usar estos beans.
4. Usando las Fuentes de Datos
Podemos inyectar las dos fuentes de datos cuando sea necesario en nuestra aplicación.
Por ejemplo, podemos usar la anotación @Autowired
para inyectar la fuente de datos dentro de una clase de servicio:
@Autowired
@Qualifier("orderDataSource")
private DataSource orderDataSource;
Sin embargo, la mayor parte del tiempo, queremos conectar la fuente de datos a un repositorio de Spring Data JPA y usar eso en lugar de interactuar directamente con la fuente de datos.
Veamos cómo configurar eso a continuación.
5. Configuración de @Repository
Con una Fuente de Datos Específica
Primero, necesitamos crear una clase de configuración donde utilizamos la anotación @EnableJpaRepositories
para escanear los repositorios y definir la fábrica del administrador de entidades para cada fuente de datos:
@Configuration
@EnableJpaRepositories(
basePackages = "com.namastecode.repositories.customer",
entityManagerFactoryRef = "customerEntityManagerFactory",
transactionManagerRef = "customerTransactionManager"
)
public class CustomerDataSourceConfiguration{
// More code
}
Ahora, necesitamos crear el bean entityManagerFactory
dentro de esta clase:
@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();
}
A continuación, crearemos el bean transactionManager
para que cada fuente de datos tenga su propio gestor de transacciones y no haya conflictos entre ellas:
@Primary
@Bean(name = "customerTransactionManager")
public PlatformTransactionManager customerTransactionManager(
@Qualifier("customerEntityManagerFactory") EntityManagerFactory customerEntityManagerFactory) {
return new JpaTransactionManager(customerEntityManagerFactory);
}
Arriba, definimos ambos beans como @Primary
, ya que la fuente de datos del cliente es la predeterminada. No necesitamos hacer eso para la fuente de datos de los pedidos.
Tenemos todo listo para crear nuestras entidades bajo el paquete com.namastecode.domain.customer
:
@Entity
@Table(name = "customers")
public class Customer {
// More code
}
De manera similar, podemos crear los repositorios bajo el paquete com.namastecode.repositories.customer
:
@Repository
public interface CustomerRepository extends JpaRepository<Customer, Long> {
// More code
}
Ahora, tenemos dos fuentes de datos separadas con sus recursos. Sin embargo, podría haber una situación en la que queramos configurar las fuentes de datos, por lo que deseamos utilizar una única transacción en ambas fuentes de datos. A continuación, veamos cómo hacerlo.
6. Configurando un Gestor de Transacciones Enlazadas
Si queremos realizar transacciones en ambas fuentes de datos, podemos configurar un bean ChainedTransactionManager
que, más adelante, podremos utilizar en nuestras operaciones:
@Bean(name = "chainedTransactionManager")
public ChainedTransactionManager chainedTransactionManager(
@Qualifier("customerTransactionManager") PlatformTransactionManager customerTransactionManager,
@Qualifier("orderTransactionManager") PlatformTransactionManager orderTransactionManager) {
return new ChainedTransactionManager(customerTransactionManager, orderTransactionManager);
}
A continuación, supongamos que tenemos un CustomerOrderService
donde estamos utilizando tanto CustomerRepository
como OrderRepository
. Así que, para usar nuestro gestor de transacciones encadenadas en esta clase de servicio, podemos definir explícitamente el gestor de transacciones:
@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);
}
}
Si alguna de las operaciones de guardado falla, la transacción se revertirá para ambas fuentes de datos.
7. Conclusión
En este tutorial, aprendimos cómo configurar múltiples fuentes de datos en una aplicación de Spring Boot.
Primero, definimos las propiedades de cada fuente de datos, creamos los beans de fuente de datos, configuramos el @Repository
con una fuente de datos específica y, finalmente, vimos cómo configurar un administrador de transacciones compartido para ambas fuentes de datos.