Spring Boot 配置多数据源并手动配置事务

news2025/1/15 15:48:32

Spring Boot 配置多数据源并手动配置事务

  • 一、为什么多数据源需要手动配置?
  • 二、配置多数据源
    • 1. 数据源配置类 (DataSourceConfig)
    • 2. 主数据库 MyBatis 配置类 (PrimaryDbMyBatisConfig)
    • 3. 从数据库 MyBatis 配置类 (SecondaryDbMyBatisConfig)
    • 4. application.yml 配置
    • 5. 使用 @Transactional 指定事务管理器
  • 三、问题说明:will not be managed by Spring
  • 四、总结


在现代企业级应用中,往往会涉及到多个数据库的使用。例如,一个应用可能需要同时操作不同的业务数据库,或分离读写操作。

Spring Boot 提供了对多数据源的支持,但在某些情况下,自动配置可能无法满足复杂的需求。特别是在事务管理方面,当需要在多个数据源间进行操作时,默认的事务管理方式可能不适用,或者无法满足更高的灵活性需求。

因此,手动配置多个数据源并支持事务管理是一个常见的解决方案。

一、为什么多数据源需要手动配置?

  1. 多数据源隔离:

    在微服务架构或者不同模块之间可能会使用不同的数据库,每个数据库有独立的数据源和事务管理。如果使用自动配置的方式,Spring Boot会自动配置一个数据源和一个事务管理器,这可能会导致不同数据源的事务冲突或共享问题。因此,需要手动配置多个数据源和多个事务管理器

  2. 跨数据源的事务管理:

    Spring 默认的事务管理仅适用于单一数据源,如果在一个方法中涉及到多个数据源,Spring并不会自动处理这些数据源之间的事务关系。这时,我们需要显式地配置事务管理器,以便手动控制事务的提交与回滚。

  3. 灵活的事务控制:

    有时我们可能需要在不同的数据源之间手动控制事务,比如先操作主数据源,再操作从数据源,或者在同一个方法中对多个数据源进行操作。这时,手动配置事务管理器可以为我们提供更高的灵活性和控制能力。

二、配置多数据源

1. 数据源配置类 (DataSourceConfig)

这个配置类主要负责配置 primary_db 和 secondary_db 两个数据源,并为它们设置基本的连接属性。

@Configuration
@EnableTransactionManagement
public class DataSourceConfig {

    /**
     * 配置主数据库(primary_db)数据源
     * 
     * 这个方法使用 `@ConfigurationProperties` 注解,从 `application.yml` 配置文件中读取 
     * `spring.datasource.primary_db` 下的配置,自动配置数据源的连接信息(如 URL、用户名、密码等)。
     * 
     * @return 配置的主数据库数据源对象
     */
    @Bean(name = "primaryDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.primary_db")
    public DataSource primaryDataSource() {
        return DataSourceBuilder.create().build(); // 创建并返回数据源对象
    }

    /**
     * 配置从数据库(secondary_db)数据源
     * 
     * 该方法使用 `@ConfigurationProperties` 注解,从 `application.yml` 配置文件中读取 
     * `spring.datasource.secondary_db` 下的配置,自动配置数据源的连接信息。
     * 
     * @return 配置的从数据库数据源对象
     */
    @Bean(name = "secondaryDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.secondary_db")
    public DataSource secondaryDataSource() {
        return DataSourceBuilder.create().build(); // 创建并返回数据源对象
    }

}

  • primaryDataSource()secondaryDataSource() 方法用于创建和配置两个数据源。通过 @ConfigurationProperties 注解,Spring 会自动读取配置文件中的数据源信息,并将其与 DataSource 配置绑定。DataSourceBuilder.create().build() 是 Spring Boot 提供的便捷方式来创建数据源实例。

  • @EnableTransactionManagement 启用了事务管理功能,允许你在需要的地方使用 @Transactional 注解来声明事务。

2. 主数据库 MyBatis 配置类 (PrimaryDbMyBatisConfig)

这个配置类负责配置与主数据库(primary_db)相关的 MyBatis 组件。它包含了 MyBatis 的 SqlSessionFactory 和事务管理器配置。

@Configuration
@EnableTransactionManagement
@MapperScan(basePackages = "com.cx.mail.mapper.primary_db", sqlSessionFactoryRef = "primaryDbSqlSessionFactory")
public class PrimaryDbMyBatisConfig {

    @Autowired
    @Qualifier("primaryDataSource")
    private DataSource primaryDataSource;

    /**
     * 配置主数据库的 MyBatis SqlSessionFactory
     * 
     * 创建并配置 MyBatis 的 `SqlSessionFactory`,它是 MyBatis 用来与数据库交互的核心组件。
     * 在这个配置中,我们指定了使用的 `primaryDataSource` 数据源。
     * 
     * @return 配置好的 SqlSessionFactory 对象
     * @throws Exception 可能抛出异常,例如创建 `SqlSessionFactory` 时出错
     */
    @Bean
    public SqlSessionFactory primaryDbSqlSessionFactory() throws Exception {
        MybatisSqlSessionFactoryBean factory = new MybatisSqlSessionFactoryBean();
        factory.setDataSource(primaryDataSource); // 设置使用的主数据库数据源

        // 配置全局设置,主要是 MetaObjectHandler,用于自动填充字段
        GlobalConfig globalConfig = GlobalConfigUtils.defaults();
        globalConfig.setMetaObjectHandler(new EntityAutoFillHandler()); // 自动填充创建时间和更新时间
        factory.setGlobalConfig(globalConfig);

        // 配置 MyBatis Plus 插件
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); // 添加分页插件
        factory.setPlugins(interceptor);

        // 配置 MyBatis 基本设置
        MybatisConfiguration configuration = new MybatisConfiguration();
        configuration.setMapUnderscoreToCamelCase(true); // 将数据库字段下划线命名转换为 Java 类的驼峰命名法
        configuration.setLogImpl(StdOutImpl.class); // 配置 SQL 打印
        factory.setConfiguration(configuration);

        // 配置 MyBatis Mapper XML 文件的位置
        factory.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:mapper/primary_db/*.xml"));

        return factory.getObject(); // 返回创建的 SqlSessionFactory
    }

    /**
     * 配置主数据库的事务管理器
     * 
     * 该方法创建一个 `DataSourceTransactionManager`,它是 Spring 提供的事务管理器,用来处理主数据库的事务。
     * 
     * @param dataSource 主数据库的数据源
     * @return 配置好的事务管理器
     */
    @Primary
    @Bean(name = "primaryDbTransactionManager")
    public DataSourceTransactionManager primaryDbTransactionManager(@Qualifier("primaryDataSource") DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource); // 返回主数据库的事务管理器
    }
}

  • primaryDbSqlSessionFactory() 方法配置了主数据库的 SqlSessionFactory,并指定了与该数据库交互时的 MyBatis 配置。

  • primaryDbTransactionManager() 方法创建了主数据库的事务管理器(DataSourceTransactionManager),使得我们可以通过 @Transactional 注解来管理与主数据库相关的事务。

3. 从数据库 MyBatis 配置类 (SecondaryDbMyBatisConfig)

这个配置类与主数据库配置类似,但是它专门配置与从数据库(secondary_db)相关的 MyBatis 组件。

@Configuration
@EnableTransactionManagement
@MapperScan(basePackages = "com.cx.mail.mapper.secondary_db", sqlSessionFactoryRef = "secondaryDbSqlSessionFactory")
public class SecondaryDbMyBatisConfig {

    @Autowired
    @Qualifier("secondaryDataSource")
    private DataSource secondaryDataSource;

    /**
     * 配置从数据库的 MyBatis SqlSessionFactory
     * 
     * 创建并配置从数据库的 `SqlSessionFactory`,它将用于从数据库的 MyBatis 操作。
     * 
     * @return 配置好的 SqlSessionFactory 对象
     * @throws Exception 可能抛出异常,例如创建 `SqlSessionFactory` 时出错
     */
    @Bean(name = "secondaryDbSqlSessionFactory")
    public SqlSessionFactory secondaryDbSqlSessionFactory() throws Exception {
        MybatisSqlSessionFactoryBean factory = new MybatisSqlSessionFactoryBean();
        factory.setDataSource(secondaryDataSource); // 设置使用的从数据库数据源

        // 配置全局设置,主要是 MetaObjectHandler,用于自动填充字段
        GlobalConfig globalConfig = GlobalConfigUtils.defaults();
        globalConfig.setMetaObjectHandler(new EntityAutoFillHandler());
        factory.setGlobalConfig(globalConfig);

        // 配置 MyBatis Plus 插件
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); // 添加分页插件
        factory.setPlugins(interceptor);

        // 配置 MyBatis 基本设置
        MybatisConfiguration configuration = new MybatisConfiguration();
        configuration.setMapUnderscoreToCamelCase(true); // 设置驼峰命名法
        configuration.setLogImpl(StdOutImpl.class); // 配置 SQL 打印
        factory.setConfiguration(configuration);

        // 设置 Mapper XML 文件的位置
        factory.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:mapper/secondary_db/*.xml"));

        return factory.getObject(); // 返回创建的 SqlSessionFactory
    }

    /**
     * 配置从数据库的事务管理器
     * 
     * 该方法创建一个 `DataSourceTransactionManager`,它是 Spring 提供的事务管理器,用来处理从数据库的事务。
     * 
     * @param dataSource 从数据库的数据源
     * @return 配置好的事务管理器
     */
    @Bean(name = "secondaryDbTransactionManager")
    public DataSourceTransactionManager secondaryDbTransactionManager(@Qualifier("secondaryDataSource") DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource); // 返回从数据库的事务管理器
    }
}

  • secondaryDbSqlSessionFactory()secondaryDbTransactionManager() 方法与主数据库的配置类类似,但它们专门用于配置和管理从数据库的 MyBatis 和事务。

4. application.yml 配置

# 本地库
spring:
  # 数据源配置 mysql
  datasource:
    primary_db:
      url: jdbc:mysql://localhost:3306/primary_db
      username: root
      password: root
      driver-class-name: com.mysql.cj.jdbc.Driver
    secondary_db:
      url: jdbc:mysql://localhost:3306/secondary_db
      username: root
      password: root
      driver-class-name: com.mysql.cj.jdbc.Driver

  • 配置了 primary_dbsecondary_db 两个数据源的连接信息(如 URL、用户名、密码等),这些配置将被数据源配置类读取。

5. 使用 @Transactional 指定事务管理器

在多个数据源配置好之后,需要手动管理事务,确保每个数据源的操作在不同的事务上下文中进行。

使用 @Transactional 指定事务管理器,应该加在需要使用该事务管理器进行事务管理的 服务层方法或类 上。通过 @Transactional 注解来指定使用哪个事务管理器。这样,Spring 会使用指定的事务管理器来管理该方法的事务。

@Service
@Transactional(transactionManager = "primaryDbTransactionManager")  // 显式指定事务管理器
public class AdminServiceImpl implements AdminService {

    @Autowired
    private FakeDomainMapper fakeDomainMapper;

    @Autowired
    private FakeUserService fakeUserService;

    /**
     * 新增域名
     */
    @Override
    public void addFakeDomain(String domain, String domainDescription) {

        String loginUsername = FakeSecurityUtil.getUsername();
        if (!fakeUserService.isFakeUserAdmin(loginUsername)) {
            throw new FakeServiceException("FORBIDDEN");
        }

        FakeDomainEntity fakeDomainEntity = new FakeDomainEntity();
        fakeDomainEntity.setFakeDomain(domain);
        if (domainDescription != null) {
            fakeDomainEntity.setFakeDescription(domainDescription);
        }
        fakeDomainEntity.setFakeCreated(new Date());
        fakeDomainEntity.setFakeModified(new Date());
        // 设置默认的虚拟配额
        String fakeDefaultSettings = "default_user_quota:2048;";
        fakeDomainEntity.setFakeSettings(fakeDefaultSettings);

        fakeDomainMapper.insertFakeDomain(fakeDomainEntity);
    }
}

  • @Transactional(transactionManager = "primaryDbTransactionManager") 使得 addFakeDomain 方法使用 primaryDbTransactionManager 来管理事务,这样该方法的事务操作会应用到 primaryDataSource 数据源上。

三、问题说明:will not be managed by Spring

在使用 @Transactional 注解时,如果有多个事务管理器(例如多数据源配置),需要特别注意 指定事务管理器

否则,Spring 无法自动决定使用哪个事务管理器来管理当前方法的事务,可能导致事务 无法被 Spring 管理,从而出现错误或者无法保证事务的一致性。

如果没有指定正确的事务管理器,Spring 可能会抛出类似如下的错误:

在这里插入图片描述

这个问题通常出现在以下两种情况下:

  1. 多个数据源
    应用程序配置了多个数据源,每个数据源有自己的事务管理器。Spring 可能不知道应该使用哪个事务管理器来管理当前方法的事务。

  2. 未指定事务管理器
    在有多个事务管理器的环境下,@Transactional 默认使用主数据源的事务管理器。如果你没有显式指定事务管理器,可能会导致事务无法被正确管理,或者使用错误的事务管理器。


四、总结

通过上述配置,系统能够实现同时操作两个不同的数据源,每个数据源有独立的事务管理、MyBatis 配置等,确保了在进行跨库操作时能够稳定运行。每个模块的配置都进行了明确的注释,方便维护和修改。

  • 在有 多个数据源多个事务管理器 的情况下,必须显式指定 transactionManager,否则事务将不会被 Spring 管理,可能导致错误。

  • 使用 @Transactional(transactionManager = "primaryDbTransactionManager") 来明确指定事务管理器。

  • 默认情况下,如果没有显式指定事务管理器,Spring 只会使用主数据源的事务管理器,其他数据源的事务管理器将无法生效。

  • 通过正确的事务管理器配置,可以确保每个方法或服务层类的事务控制是正确的,从而避免由于事务管理不当导致的数据库操作异常或数据不一致问题。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2258812.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

微积分复习笔记 Calculus Volume 2 - 3.6 Numerical Integration

3.6 Numerical Integration - Calculus Volume 2 | OpenStax

axios请求之参数拼接

URL 查询参数传递数据 优点: 简洁性: URL 查询参数的方式比较简洁,适合传递少量的数据。缓存友好: 查询参数可以被浏览器缓存,适合 GET 请求,但对于 POST 请求,浏览器通常不会缓存。 缺点: 数据大小限制: U…

使用硬约束的物理信息神经网络(PINNs,Physics-Informed Neural Networks)求解一维泊松方程

背景 一维泊松方程定义为: u ′ ′ ( x ) f ( x ) u(x) f(x) u′′(x)f(x),边界条件为 x 0 x0 x0 和 x 1 x1 x1 处, u ( x ) 0 u(x) 0 u(x)0,其中, f ( x ) − π 2 sin ⁡ ( π x ) f(x) -\pi^2 \sin(\pi …

基于单片机的多功能函数信号发生器的设计(论文+源码)

系统总体设计思路 在本次课题为信号发生器,其系统整体架构如图2.1所示,在硬件结构上,其主要包括核心控制器AT89C51单片机,液晶显示器LCD1602,数模转换器DAC0832,运算放大器L358以及按键、LED指示灯等器件,…

Docker 安装 sentinel

Docker 安装系列 1、拉取 [rootTseng ~]# docker pull bladex/sentinel-dashboard Using default tag: latest latest: Pulling from bladex/sentinel-dashboard 4abcf2066143: Pull complete 1ec1e81da383: Pull complete 56bccb36a894: Pull complete 7cc80011dc6f: Pull…

解读数据资产管理实践白皮书(5.0版)深入学习掌握数据资产管理知识体系。

本文介绍了数据资产管理的重要性及其概述,详细阐述了数据资产管理的活动职能包括数据模型管理、数据标准管理、数据质量管理等,并强调了数据安全管理的重要性。文章还讨论了数据资产管理的保障措施和实践步骤,以及发展趋势和总结展望。 重点内…

【大前端vue:组件】vue鼠标滑动:平滑滚动效果 向左、向右

【大前端vue&#xff1a;组件】vue鼠标滑动&#xff1a;平滑滚动效果 向左、向右 <template><div class"tab-container"><div class"tab-wrapper"><h2 class"main-title">鼠标滑动&#xff1a;平滑滚动效果 向左、向右…

python脚本:向kafka数据库中插入测试数据

# coding:utf-8 import datetime import json import random import timefrom kafka import KafkaProducer生产者demo向branch-event主题中循环写入10条json数据注意事项&#xff1a;要写入json数据需加上value_serializer参数&#xff0c;如下代码producer KafkaProducer(val…

selenium自动爬虫工具

一、介绍selenium爬虫工具 selenium 是一个自动化测试工具&#xff0c;可以用来进行 web 自动化测试、爬虫 selenium 本质是通过驱动浏览器&#xff0c;完全模拟浏览器的操作&#xff0c;比如跳转、输入、点击、下拉等&#xff0c;来拿到网页渲染之后的结果&#xff0c;可支持…

【数据结构——栈与队列】环形队列的基本运算(头歌实践教学平台习题)【合集】

目录&#x1f60b; 任务描述 相关知识 测试说明 我的通关代码: 测试结果&#xff1a; 任务描述 本关任务&#xff1a;编写一个程序实现环形队列的基本运算。 相关知识 为了完成本关任务&#xff0c;你需要掌握&#xff1a; 初始化队列、销毁队列、判断队列是否为空、进队列…

笔记本电脑升级硬盘存储、Windows10系统安装及后续步骤(以联想ThinkPad X1 Carbon Gen10为例)

文章目录 1.前言2.材料准备3.Win10系统安装盘制作3.1 系统下载3.2 系统启动U盘刻录 4.拆机更换硬盘5.开机启动项修改6.系统安装&#xff08;以Win10为例&#xff09;7.系统安装后可能需要的步骤7.1 缺少WIFI等网络驱动7.2 系统激活7.3 办公软件安装 8.旧硬盘变废为宝参考文献 1…

关于idea-Java-servlet-Tomcat-Web开发中出现404NOT FOUND问题的解决

在做web项目时&#xff0c;第一次使用servlet开发链接前端和后端的操作&#xff0c;果不其然&#xff0c;遇到了诸多问题&#xff0c;而遇到最多的就是运行项目打开页面时出现404NOT FOUND的情况。因为这个问题我也是鼓捣了好久&#xff0c;上网查了许多资料才最终解决&#xf…

国产物联网平台(IotSharp+IoTGateway+Influxdb)快速上手

环境说明&#xff1a; Visual Studio 2022 CommunityIotSharp代码&#xff1a;https://github.com/IoTSharp/IoTSharp.gitIoTGateway版本&#xff1a;v2.1.1Node版本&#xff1a;v20.18.1Influxdb版本&#xff1a;v2.7.11 安装Node Node.js官网 官网下载并安装&#xff0c;…

网页502 Bad Gateway nginx1.20.1报错与解决方法

目录 网页报错的原理 查到的502 Bad Gateway报错的原因 出现的问题和尝试解决 问题 解决 网页报错的原理 网页显示502 Bad Gateway 报错原理是用户访问服务器时&#xff0c;nginx代理服务器接收用户信息&#xff0c;但无法反馈给服务器&#xff0c;而出现的报错。 查到…

2025系统架构师(一考就过):选择题基础知识二

考点14&#xff1a;知识产权和标准化 真题1&#xff1a;甲软件公司受乙企业委托安排公司软件设计师开发了信息系统管理软件&#xff0c;由于在委托开发合同中未对软件著作权归属作出明确的约定&#xff0c;所以该信息系统管理软件的著作权由(甲) 享有。 真题2&#xff1a;根据…

算法日记48 day 图论(拓扑排序,dijkstra)

今天继续图论章节&#xff0c;主要是拓扑排序和dijkstra算法。 还是举例说明。 题目&#xff1a;软件构建 117. 软件构建 (kamacoder.com) 题目描述 某个大型软件项目的构建系统拥有 N 个文件&#xff0c;文件编号从 0 到 N - 1&#xff0c;在这些文件中&#xff0c;某些文件…

复原IP地址 什么是运算符重载? 如何在 C++ 中进行运算符重载?运算符重载在面向对象编程中的好处是什么?getline方法

getline方法 getline 是一个强大的函数&#xff0c;主要用于从输入流中按行读取数据或基于自定义分隔符提取字符串。它是 C 标准库的一部分&#xff0c;定义在头文件 <string> 中。 语法 std::getline(istream& input, std::string& str);input&#xff1a;输…

【蓝桥杯备战】Day 1

1.基础题目 LCR 018.验证回文串 给定一个字符串 s &#xff0c;验证 s 是否是 回文串 &#xff0c;只考虑字母和数字字符&#xff0c;可以忽略字母的大小写。 本题中&#xff0c;将空字符串定义为有效的 回文串 。 示例 1: 输入: s "A man, a plan, a canal: Panama…

牛客网刷题 | BC126 小乐乐查找数字

&#x1f601;博客主页&#x1f601;&#xff1a;&#x1f680;从0至1-CSDN博客&#x1f680; &#x1f911;博客内容&#x1f911;&#xff1a;&#x1f36d;C语言、C、数据结构、嵌入式、Linux&#x1f36d; &#x1f60e;本文内容&#x1f923;&#xff1a;&#x1f36d;BC1…

单元测试-FATAL ERROR in native method: processing of -javaagent failed

文章目录 前言单元测试-FATAL ERROR in native method: processing of -javaagent failed1. 报错信息2. 解决方案 前言 如果您觉得有用的话&#xff0c;记得给博主点个赞&#xff0c;评论&#xff0c;收藏一键三连啊&#xff0c;写作不易啊^ _ ^。   而且听说点赞的人每天的运…