Spring | Практика работы с несколькими источниками данных на основе SpringBoot — использование Seata для реализации глобального управления транзакциями с несколькими источниками данных
Spring | Практика работы с несколькими источниками данных на основе SpringBoot — использование Seata для реализации глобального управления транзакциями с несколькими источниками данных

введение

в разработке программного обеспечения,Несколько источников данныхПрименение,особенно вМикросервисная архитектураиМодульность бизнесаВ сцене。Несколько источников данных позволяет различным бизнес-модулям и микросервисам иметь независимое хранилище данных, что значительно повышает гибкость и удобство обслуживания системы. В этой статье мы подробно рассмотрим несколько источников данныхиз配置и实施,и вSpring Bootсреда,как пройтиSpring Data JPAиGradleосознать Несколько источников Управление и применение данных.

Несколько источников данных, обсуждаемые в этой статье, относятся к реляционным базам данных, то есть одна служба имеет несколько таких баз данных. 1.1 Необходимость нескольких источников данных

По мере роста и развития бизнеса,Одного источника данных уже недостаточноРазнообразные и сложные потребности бизнесаНесколько источников данныхПриложение может не только лучше поддерживать развитие бизнеса,также может быть эффективно реализованоИзоляция ресурсови管理,систематически сокращатьСтепень сцепления,提高服务из稳定性и可用性。использовать Несколько источников данные могут иметь следующие преимущества:

  1. Оптимизация производительности:不同из关系型数据库有各自из优势и特性,Некоторые базы данных более подходятчитатьПолучить операцию,Хотя другие базы данных более подходятПисатьВведите операцию。добавивчитать Писать Распределите нагрузку на разные экземпляры базы данных,Может Оптимизация производительности。
  2. изоляция данных:в системе,Некоторые данные могут быть более важными,Требуется большая безопасность и надежность; другие данные могут быть менее критичными;,Определенную потерю данных можно допустить. В это время,Храните разные типы данных в разных базах данных,Можно добиться изоляции данных,Удовлетворение различных потребностей в безопасности и надежности данных.
  3. Изоляция бизнес-логики:в сложных системах,Различные модули или подсистемы могут иметь разные требования к бизнес-логике и обработке данных. Используйте разные базы данных для разных модулей или подсистем.,Может упростить проектирование и обслуживание системы.

1.2 Сценарии применения нескольких источников данных

Бизнес-сценарии нескольких распространенных масштабных проектов таковы:

  • финансовая система:финансовая система通常要处理大量из事务数据и报表数据。将事务处理и报表生成分配到不同из数据库МожетОптимизация производительностии简化系统设计。
  • Платформа электронной коммерции:Платформа электронной коммерции обычно включает в себя информацию пользователя、Данные заказа、Данные о продукте и другие типы данных。为这些不同类型из数据использовать不同из数据库实例Может实现数据иИзоляция бизнес-логики
  • ERP-система:планирование ресурсов предприятия(ERP)Системы обычно содержат несколько модулей.,Такие как финансы, человеческие ресурсы и управление цепочками поставок. Использование отдельной базы данных для каждого модуля упрощает разработку и обслуживание.

Все следующие примеры загружены на Github. Вы можете загрузить проект локально и запустить его.

Пример Github (если вы еще не знакомы с Gradle),Рекомендуется прочитать мои предыдущие статьи.):gradle-spring-boot-demo


Практическая демонстрация

В этой главе будет подробно описано, как реализовать несколько источников данных в проекте Spring Boot. Мы шаг за шагом продемонстрируем, как настроить два экземпляра базы данных H2 в качестве источников данных.

2.1 Создание класса сущности

первый,Мы создаем два класса сущностей,Один для основного источника данных,один для вторичного источника данных。Мы здесь сUserСущность в качестве примера。Убедитесь, что ваши классы сущностей находятся в правильном пакете.。

Язык кода:java
копировать
// Объект источника основных данных
@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;
}

2.2 Настройка источника данных

Следующий,нам нужно быть внутриapplication.ymlНастройте два источника данных в:

Язык кода:yaml
копировать
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: один в качестве основного источника данных, а другой — в качестве вторичного источника данных.

2.3 Реализация класса конфигурации источника данных

Для достижения нескольких источников данных,Нам нужно создать два класса конфигурации,PrimaryDataSourceConfigиSecondaryDataSourceConfig,и определено в немDataSourceEntityManagerFactoryиTransactionManagerизbeans。

Язык кода:java
копировать
@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);
    }

}

💡 Добрые советы: пожалуйста, обрати внимание,В этом классе конфигурации,Мы определили отдельно для двух источников данныхDataSourceEntityManagerFactoryиTransactionManager@Primary注解用于指定主数据源相关изbeans。

2.4 Настройка класса репозитория

Нам нужно создать два класса репозитория, каждый из которых работает с объектом источника данных. Здесь мы можем использовать Spring Data JPAизJpaRepositoryинтерфейс。

Язык кода:java
копировать
// Основной источник данных Репозиторий
@Repository
public interface UserRepository extends JpaRepository<User, Long> {}

// Вторичный источник данных Репозиторий
@Repository
public interface OrderRepository extends JpaRepository<Order, Long> {}

2.5 Эксплуатация и проверка

Запустите приложение Spring Boot, и вы увидите в консоли, что оба источника данных успешно настроены.

А операции с данными можно выполнять через соответствующий класс Repository. Пишем Контроллер для взаимодействия с базой данных. Из-за ограничений места я опустил другие коды. Если вам нужен полный пример, вы можете скачать его прямо из репозитория GitHub:

Язык кода:java
копировать
@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 = "指定изуправление транзакциямиустройство")указать。


Управление транзакциями и согласованность данных

3.1 Управление транзакциями

существовать Несколько источников данныхсередина,управление транзакциями是至关重要из,Это гарантирует, что наша система поддерживает целостность и согласованность данных при выполнении нескольких операций. Давайте продемонстрируем случай, используя код,我们существоватьUserOrderServiceсередина故意Писатьвстреча Сообщить об ошибкекод:

Язык кода:java
копировать
    @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таблица отправлена:

3.2 Используйте Seata для глобального управления транзакциями

💡В этом разделе рассматривается простое управление глобальными транзакциями. Для облегчения демонстрации и тестирования не используется никакое другое промежуточное программное обеспечение, кроме самого Seata.

3.2.1 Установка seata-сервера

Чтобы использовать Seata, вам необходимо сначала Seata-сервер,Путь загрузки следующий:https://github.com/seata/seata/releases Я использую здесь оконную систему, просто скачайте zip-архив:

После загрузки дважды щелкните, чтобы запустить:

В консоли видно, что сеата запускается нормально:

3.2.2 Настройка IDEA
  1. Представьте зависимость от Seata. Я использую gradle. Вам нужно только ввести эту строку:
Язык кода:java
копировать
	    implementation 'io.seata:seata-spring-boot-starter:1.7.1' // Пожалуйста, проверьте номер последней версии
  1. Добавлена ​​конфигурация application.yml.
Язык кода:yaml
копировать
	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
  1. Добавить файл file.conf
Язык кода:java
копировать
	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"
	  }
	}
	
  1. Добавлен файл реестра.conf.
Язык кода:java
копировать
	registry {
	  type = "file"
	
	  file {
	    name = "file.conf"
	  }
	}
	
	config {
	  type = "file"
	  file {
	    name = "file.conf"
	  }
	}
	
  1. UserOrderServiceДобавьте следующие методы:
Язык кода:java
копировать
	    @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);
	    }
  1. Добавьте новый метод контроллера:
Язык кода:java
копировать
	    @PostMapping("/createMixGlobalTransaction")
	    public void createMixByGlobalTransaction(@Valid @RequestBody UserOrderDTO userOrderDTO) {
	        userOrderService.createUserAndOrderByGlobalTransaction(userOrderDTO);
	    }
  1. контроллер запросов

Результаты выполнения следующие:

Глобальная транзакция вступает в силу, и транзакция откатывается.


Подвести итог

На этом глава заканчивается. В этой статье подробно рассматриваются преимущества и распространенные применения нескольких источников данных, а также показано, как использовать и интегрировать несколько источников данных в Spring Boot. В то же время мы также обсудили проблемы транзакций, существующие в нескольких источниках данных, и решили эту проблему с помощью глобального управления транзакциями Seata.

Ссылки

  1. Как SpringBoot интегрирует несколько источников данных, просто прочитайте эту статью. - Тенсент Облако
  2. Spring Boot Интегрировать Несколько источников данные, это называется элегантность - наггетсы
  3. Springboot интегрирует межбазовые операции mybatis и настраивает несколько источников данных DataSource - CSDN
  4. SpringBoot интегрирует MyBatisНесколько источников данных - Тенсент Облако
  5. Узнайте, как интегрировать Springboot за 5 минут источников данных - Подумай об этом
boy illustration
Неразрушающее увеличение изображений одним щелчком мыши, чтобы сделать их более четкими артефактами искусственного интеллекта, включая руководства по установке и использованию.
boy illustration
Копикодер: этот инструмент отлично работает с Cursor, Bolt и V0! Предоставьте более качественные подсказки для разработки интерфейса (создание навигационного веб-сайта с использованием искусственного интеллекта).
boy illustration
Новый бесплатный RooCline превосходит Cline v3.1? ! Быстрее, умнее и лучше вилка Cline! (Независимое программирование AI, порог 0)
boy illustration
Разработав более 10 проектов с помощью Cursor, я собрал 10 примеров и 60 подсказок.
boy illustration
Я потратил 72 часа на изучение курсорных агентов, и вот неоспоримые факты, которыми я должен поделиться!
boy illustration
Идеальная интеграция Cursor и DeepSeek API
boy illustration
DeepSeek V3 снижает затраты на обучение больших моделей
boy illustration
Артефакт, увеличивающий количество очков: на основе улучшения характеристик препятствия малым целям Yolov8 (SEAM, MultiSEAM).
boy illustration
DeepSeek V3 раскручивался уже три дня. Сегодня я попробовал самопровозглашенную модель «ChatGPT».
boy illustration
Open Devin — инженер-программист искусственного интеллекта с открытым исходным кодом, который меньше программирует и больше создает.
boy illustration
Эксклюзивное оригинальное улучшение YOLOv8: собственная разработка SPPF | SPPF сочетается с воспринимаемой большой сверткой ядра UniRepLK, а свертка с большим ядром + без расширения улучшает восприимчивое поле
boy illustration
Популярное и подробное объяснение DeepSeek-V3: от его появления до преимуществ и сравнения с GPT-4o.
boy illustration
9 основных словесных инструкций по доработке академических работ с помощью ChatGPT, эффективных и практичных, которые стоит собрать
boy illustration
Вызовите deepseek в vscode для реализации программирования с помощью искусственного интеллекта.
boy illustration
Познакомьтесь с принципами сверточных нейронных сетей (CNN) в одной статье (суперподробно)
boy illustration
50,3 тыс. звезд! Immich: автономное решение для резервного копирования фотографий и видео, которое экономит деньги и избавляет от беспокойства.
boy illustration
Cloud Native|Практика: установка Dashbaord для K8s, графика неплохая
boy illustration
Краткий обзор статьи — использование синтетических данных при обучении больших моделей и оптимизации производительности
boy illustration
MiniPerplx: новая поисковая система искусственного интеллекта с открытым исходным кодом, спонсируемая xAI и Vercel.
boy illustration
Конструкция сервиса Synology Drive сочетает проникновение в интрасеть и синхронизацию папок заметок Obsidian в облаке.
boy illustration
Центр конфигурации————Накос
boy illustration
Начинаем с нуля при разработке в облаке Copilot: начать разработку с минимальным использованием кода стало проще
boy illustration
[Серия Docker] Docker создает мультиплатформенные образы: практика архитектуры Arm64
boy illustration
Обновление новых возможностей coze | Я использовал coze для создания апплета помощника по исправлению домашних заданий по математике
boy illustration
Советы по развертыванию Nginx: практическое создание статических веб-сайтов на облачных серверах
boy illustration
Feiniu fnos использует Docker для развертывания личного блокнота Notepad
boy illustration
Сверточная нейронная сеть VGG реализует классификацию изображений Cifar10 — практический опыт Pytorch
boy illustration
Начало работы с EdgeonePages — новым недорогим решением для хостинга веб-сайтов
boy illustration
[Зона легкого облачного игрового сервера] Управление игровыми архивами
boy illustration
Развертывание SpringCloud-проекта на базе Docker и Docker-Compose