в разработке программного обеспечения,Несколько источников данныхПрименение,особенно вМикросервисная архитектураиМодульность бизнесаВ сцене。Несколько источников данных позволяет различным бизнес-модулям и микросервисам иметь независимое хранилище данных, что значительно повышает гибкость и удобство обслуживания системы. В этой статье мы подробно рассмотрим несколько источников данныхиз配置и实施,и вSpring Bootсреда,как пройтиSpring Data JPAиGradleосознать Несколько источников Управление и применение данных.
Несколько источников данных, обсуждаемые в этой статье, относятся к реляционным базам данных, то есть одна служба имеет несколько таких баз данных. 1.1 Необходимость нескольких источников данных
По мере роста и развития бизнеса,Одного источника данных уже недостаточноРазнообразные и сложные потребности бизнеса。Несколько источников данныхПриложение может не только лучше поддерживать развитие бизнеса,также может быть эффективно реализованоИзоляция ресурсови管理,систематически сокращатьСтепень сцепления,提高服务из稳定性и可用性。использовать Несколько источников данные могут иметь следующие преимущества:
Бизнес-сценарии нескольких распространенных масштабных проектов таковы:
Все следующие примеры загружены на Github. Вы можете загрузить проект локально и запустить его.
Пример Github (если вы еще не знакомы с Gradle),Рекомендуется прочитать мои предыдущие статьи.):gradle-spring-boot-demo
В этой главе будет подробно описано, как реализовать несколько источников данных в проекте Spring Boot. Мы шаг за шагом продемонстрируем, как настроить два экземпляра базы данных H2 в качестве источников данных.
первый,Мы создаем два класса сущностей,Один для основного источника данных,один для вторичного источника данных。Мы здесь сUser
Сущность в качестве примера。Убедитесь, что ваши классы сущностей находятся в правильном пакете.。
// Объект источника основных данных
@Entity
@Data
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String email;
}
// объект вторичного источника данных
@Entity
@Table(name = "orders")
@Data
public class Order {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String orderNumber;
private Double amount;
}
Следующий,нам нужно быть внутриapplication.yml
Настройте два источника данных в:
spring:
datasource:
primary:
jdbc-url: jdbc:h2:file:./multi-datasource/data/testdb1
driver-class-name: org.h2.Driver
username: sa
password: password
secondary:
jdbc-url: jdbc:h2:file:./multi-datasource/data/testdb2
driver-class-name: org.h2.Driver
username: sa
password: password
h2:
console:
enabled: true
jpa:
hibernate:
ddl-auto: update
show-sql: true
Здесь мы настроили два экземпляра базы данных H2: один в качестве основного источника данных, а другой — в качестве вторичного источника данных.
Для достижения нескольких источников данных,Нам нужно создать два класса конфигурации,PrimaryDataSourceConfig
иSecondaryDataSourceConfig
,и определено в немDataSource
、EntityManagerFactory
иTransactionManager
изbeans。
@Configuration
@EnableJpaRepositories(
basePackages = "org.kfaino.datasource.repository.primary")
@EntityScan(basePackages = {"org.kfaino.datasource.entity.primary"})
public class PrimaryDataSourceConfig {
@Primary
@Bean(name = "primaryDataSource")
@ConfigurationProperties(prefix = "spring.datasource.primary")
public DataSource primaryDataSource() {
return DataSourceBuilder.create().build();
}
@Primary
@Bean(name = "entityManagerFactory")
public LocalContainerEntityManagerFactoryBean primaryEntityManagerFactory(
EntityManagerFactoryBuilder builder,
@Qualifier("primaryDataSource") DataSource dataSource) {
return builder
.dataSource(dataSource)
.packages("org.kfaino.datasource.entity.primary") // Установите путь к пакету класса сущности
.persistenceUnit("primary")
.properties(hibernateProperties())
.build();
}
private Map<String, Object> hibernateProperties() {
Map<String, Object> properties = new HashMap<>();
properties.put("hibernate.hbm2ddl.auto", "update");
properties.put("hibernate.show_sql", true);
return properties;
}
@Primary
@Bean(name = "transactionManager")
public PlatformTransactionManager transactionManager(
@Qualifier("entityManagerFactory") EntityManagerFactory entityManagerFactory) {
return new JpaTransactionManager(entityManagerFactory);
}
}
@Configuration
@EnableJpaRepositories(
basePackages = "org.kfaino.datasource.repository.secondary",
entityManagerFactoryRef = "secondaryEntityManagerFactory",
transactionManagerRef = "secondaryTransactionManager")
@EntityScan(basePackages = {"org.kfaino.datasource.entity.primary"})
public class SecondaryDataSourceConfig {
@Bean(name = "secondaryDataSource")
@ConfigurationProperties(prefix = "spring.datasource.secondary")
public DataSource secondaryDataSource() {
return DataSourceBuilder.create().build();
}
@Bean(name = "secondaryEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean secondaryEntityManagerFactory(
EntityManagerFactoryBuilder builder,
@Qualifier("secondaryDataSource") DataSource dataSource) {
return builder
.dataSource(dataSource)
.packages("org.kfaino.datasource.entity.secondary") // Установите путь к пакету класса сущности
.persistenceUnit("secondary")
.properties(hibernateProperties())
.build();
}
private Map<String, Object> hibernateProperties() {
Map<String, Object> properties = new HashMap<>();
properties.put("hibernate.hbm2ddl.auto", "update");
properties.put("hibernate.show_sql", true);
return properties;
}
@Bean(name = "secondaryTransactionManager")
public PlatformTransactionManager secondaryTransactionManager(
@Qualifier("secondaryEntityManagerFactory") EntityManagerFactory secondaryEntityManagerFactory) {
return new JpaTransactionManager(secondaryEntityManagerFactory);
}
}
💡 Добрые советы: пожалуйста, обрати внимание,В этом классе конфигурации,Мы определили отдельно для двух источников данныхDataSource
、EntityManagerFactory
иTransactionManager
。@Primary
注解用于指定主数据源相关изbeans。
Нам нужно создать два класса репозитория, каждый из которых работает с объектом источника данных. Здесь мы можем использовать Spring Data JPAизJpaRepository
интерфейс。
// Основной источник данных Репозиторий
@Repository
public interface UserRepository extends JpaRepository<User, Long> {}
// Вторичный источник данных Репозиторий
@Repository
public interface OrderRepository extends JpaRepository<Order, Long> {}
Запустите приложение Spring Boot, и вы увидите в консоли, что оба источника данных успешно настроены.
А операции с данными можно выполнять через соответствующий класс Repository. Пишем Контроллер для взаимодействия с базой данных. Из-за ограничений места я опустил другие коды. Если вам нужен полный пример, вы можете скачать его прямо из репозитория GitHub:
@RestController
@RequestMapping("user")
public class UserController {
@Resource
private UserService userService;
@Resource
private OrderService orderService;
@Resource
private UserOrderService userOrderService;
@PostMapping("/createUser")
public User createUser(@Valid @RequestBody UserDTO userDTO) {
return userService.createUser(userDTO);
}
@PostMapping("/createOrder")
public Order createOrder(@Valid @RequestBody OrderDTO orderDTO) {
return orderService.createOrder(orderDTO);
}
@PostMapping("/createMix")
public User createMix(@Valid @RequestBody UserOrderDTO userOrderDTO) {
return userOrderService.createUserAndOrder(userOrderDTO);
}
}
Затем выполните эти три запроса:
Были созданы две таблицы из разных источников данных:
💡 Уведомление: При выполнении реальных операций с данными, если требуется специальное управление транзакциямиустройство,МожетсуществоватьServiceкласс илиRepository类上использовать@Transactional(transactionManager = "指定изуправление транзакциямиустройство")
указать。
существовать Несколько источников данныхсередина,управление транзакциями是至关重要из,Это гарантирует, что наша система поддерживает целостность и согласованность данных при выполнении нескольких операций. Давайте продемонстрируем случай, используя код,我们существоватьUserOrderService
середина故意Писатьвстреча Сообщить об ошибкекод:
@Transactional("transactionManager")
public User createUserAndOrder(UserOrderDTO userOrderDTO) {
OrderDTO orderDTO = new OrderDTO();
BeanUtils.copyProperties(userOrderDTO, orderDTO);
orderService.createOrder(orderDTO);
// Сообщить об ошибке
int i = 1/0;
User user = new User();
BeanUtils.copyProperties(userOrderDTO, user);
userRepository.save(user);
return user;
}
Мы называем этот метод:
Поскольку использование другого управления транзакциямиустройство,хотяtransactionManager
управление транзакциямиустройство回滚了,我们依然Может看到orders
таблица отправлена:
💡В этом разделе рассматривается простое управление глобальными транзакциями. Для облегчения демонстрации и тестирования не используется никакое другое промежуточное программное обеспечение, кроме самого Seata.
Чтобы использовать Seata, вам необходимо сначала Seata-сервер,Путь загрузки следующий:https://github.com/seata/seata/releases Я использую здесь оконную систему, просто скачайте zip-архив:
После загрузки дважды щелкните, чтобы запустить:
В консоли видно, что сеата запускается нормально:
implementation 'io.seata:seata-spring-boot-starter:1.7.1' // Пожалуйста, проверьте номер последней версии
seata:
enabled: true
application-id: multi-datasource
tx-service-group: my_test_tx_group
enable-auto-data-source-proxy: true
config:
type: file
file:
data-file: classpath:file.conf
registry:
type: file
file:
data-file: classpath:registry.conf
service {
# сопоставление tx-service-group
vgroupMapping.my_test_tx_group = "default"
vgroupMapping.default = "default"
# Seata Адрес Сервера - ваш Seata IP-адрес и порт сервера
default.grouplist = "127.0.0.1:8091"
# Другие связанные конфигурации
enableDegrade = false
disableGlobalTransaction = false
}
store {
mode = "file"
file {
# Каталог, в котором хранятся локальные журналы транзакций
dir = "sessionStore"
}
}
registry {
type = "file"
file {
name = "file.conf"
}
}
config {
type = "file"
file {
name = "file.conf"
}
}
UserOrderService
Добавьте следующие методы: @GlobalTransactional
public void createUserAndOrderByGlobalTransaction(UserOrderDTO userOrderDTO) {
OrderDTO orderDTO = new OrderDTO();
BeanUtils.copyProperties(userOrderDTO, orderDTO);
orderService.createOrder(orderDTO);
// Сообщить об ошибке
int i = 1/0;
UserDTO userDTO = new UserDTO();
BeanUtils.copyProperties(userOrderDTO, userDTO);
userService.createUser(userDTO);
}
@PostMapping("/createMixGlobalTransaction")
public void createMixByGlobalTransaction(@Valid @RequestBody UserOrderDTO userOrderDTO) {
userOrderService.createUserAndOrderByGlobalTransaction(userOrderDTO);
}
Результаты выполнения следующие:
Глобальная транзакция вступает в силу, и транзакция откатывается.
На этом глава заканчивается. В этой статье подробно рассматриваются преимущества и распространенные применения нескольких источников данных, а также показано, как использовать и интегрировать несколько источников данных в Spring Boot. В то же время мы также обсудили проблемы транзакций, существующие в нескольких источниках данных, и решили эту проблему с помощью глобального управления транзакциями Seata.