1. Введение
В этом коротком руководстве мы узнаем, как настроить несколько data sources в приложении на Spring Boot.
Представим, что мы работаем над e-commerce app и нам нужны две отдельные databases для хранения customers и orders. Мы настроим их как отдельные data sources и посмотрим, как использовать их в нашем application.
2. Настройка Properties
Давайте начнём с определения некоторых свойств в application.properties для наших источников данных Customer и Order:
#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
Выше мы определяем data sources для двух databases и их соответствующие properties. Кроме того, мы можем настроить разные типы databases.
Теперь мы можем использовать эти properties внутри нашего приложения.
3. Настройка Data Sources
Далее мы создадим класс конфигурации, в котором определим по два DataSource bean’а для каждой 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();
}
}
Мы явно даём имена нашим beans, чтобы мы могли выбирать, к какому data source обращаться с помощью аннотации @Qualifier. Кроме того, мы пометили customerDataSource как @Primary и считаем его основной базой данных для нашего приложения. Таким образом, если мы не используем аннотацию @Qualifier, Spring внедрит customerDataSource там, где он требуется.
Давайте посмотрим, как мы можем использовать эти beans.
4. Использование Data Sources
Мы можем внедрять оба источника данных (data sources) по мере необходимости в нашем приложении.
Например, мы можем использовать аннотацию @Autowired для внедрения data source внутрь service class:
@Autowired
@Qualifier("orderDataSource")
private DataSource orderDataSource;
Однако чаще всего мы хотим подключить data source к Spring Data JPA repository и использовать его вместо непосредственного взаимодействия с data source.
Давайте посмотрим, как это настроить дальше.
5. Конфигурирование @Repository с конкретным источником данных
Сначала нам нужно создать configuration class, где мы будем использовать аннотацию @EnableJpaRepositories для сканирования repositories и определять entity manager factory для каждого data source:
@Configuration
@EnableJpaRepositories(
basePackages = "com.namastecode.repositories.customer",
entityManagerFactoryRef = "customerEntityManagerFactory",
transactionManagerRef = "customerTransactionManager"
)
public class CustomerDataSourceConfiguration{
// More code
}
Теперь нам нужно создать entityManagerFactory bean внутри этого класса:
@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();
}
Далее мы создадим bean transactionManager, чтобы у каждого источника данных был свой менеджер транзакций и они не конфликтовали друг с другом:
@Primary
@Bean(name = "customerTransactionManager")
public PlatformTransactionManager customerTransactionManager(
@Qualifier("customerEntityManagerFactory") EntityManagerFactory customerEntityManagerFactory) {
return new JpaTransactionManager(customerEntityManagerFactory);
}
Выше мы помечаем оба beans как @Primary, поскольку customer data source используется по умолчанию. Для orders data source этого делать не нужно.
У нас есть всё необходимое, чтобы создать наши сущности в пакете com.namastecode.domain.customer:
@Entity
@Table(name = "customers")
public class Customer {
// More code
}
Аналогично, мы можем создать репозитории в пакете com.namastecode.repositories.customer:
@Repository
public interface CustomerRepository extends JpaRepository<Customer, Long> {
// More code
}
Теперь у нас есть два отдельных data sources с их resources. Однако может возникнуть ситуация, когда мы захотим настроить data sources так, чтобы использовать single transaction для обоих data sources. Далее посмотрим, как это сделать.
6. Настройка Chained Transaction Manager
Если мы хотим выполнять transactions одновременно в обоих data sources, мы можем настроить bean ChainedTransactionManager, который позже сможем использовать в наших operations:
@Bean(name = "chainedTransactionManager")
public ChainedTransactionManager chainedTransactionManager(
@Qualifier("customerTransactionManager") PlatformTransactionManager customerTransactionManager,
@Qualifier("orderTransactionManager") PlatformTransactionManager orderTransactionManager) {
return new ChainedTransactionManager(customerTransactionManager, orderTransactionManager);
}
Далее предположим, что у нас есть CustomerOrderService, в которой мы используем и CustomerRepository, и OrderRepository. Чтобы использовать наш chained transaction manager в этом классе сервиса, мы можем явно определить transaction manager:
@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);
}
}
Как показано выше, если любая из save operations завершится неудачей, transaction будет откатан для обоих data sources.
7. Заключение
В этом руководстве мы узнали, как настроить несколько источников данных в приложении Spring Boot.
Сначала мы определили свойства каждого data source, создали data source beans, настроили @Repository на использование конкретного data source и, наконец, рассмотрели, как настроить shared transaction manager для обоих data sources.


