当前位置: 首页 > news >正文

01.【面试题】在SpringBoot中如何实现多数据源配置

文章目录

    • 1. 什么是多数据源
      • 1.1 基本概念
      • 1.2 传统单数据源 vs 多数据源
        • 单数据源架构
        • 多数据源架构
    • 2. 为什么需要多数据源
      • 2.1 业务场景需求
      • 2.2 技术优势
    • 3. 多数据源的实现方式
      • 3.1 静态多数据源
      • 3.2 动态多数据源
    • 4. 环境准备
      • 4.1 创建SpringBoot项目
        • pom.xml依赖配置
      • 4.2 准备测试数据库
        • 创建两个测试数据库
    • 5. 方法一:使用@Primary和@Qualifier注解
      • 5.1 配置文件
        • application.yml
      • 5.2 数据源配置类
        • DataSourceConfig.java
      • 5.3 MyBatis配置类
        • PrimaryMyBatisConfig.java
        • SecondaryMyBatisConfig.java
      • 5.4 实体类
        • User.java
        • Order.java
      • 5.5 Mapper接口
        • UserMapper.java
        • OrderMapper.java
      • 5.6 MyBatis XML映射文件
        • UserMapper.xml
        • OrderMapper.xml
      • 5.7 Service层
        • UserService.java
        • OrderService.java
      • 5.8 Controller层
        • UserController.java
        • OrderController.java
    • 6. 方法二:使用@ConfigurationProperties
      • 6.1 更简洁的配置方式
        • DataSourceProperties.java
        • application.yml(配置属性方式)
        • EnhancedDataSourceConfig.java
    • 7. 方法三:动态数据源切换
      • 7.1 动态数据源核心类
        • DynamicDataSource.java
        • DataSourceContextHolder.java
        • DataSource.java(注解)
      • 7.2 AOP切面处理数据源切换
        • DataSourceAspect.java
      • 7.3 动态数据源配置
        • DynamicDataSourceConfig.java
      • 7.4 使用动态数据源的Service
        • DynamicUserService.java
        • DynamicOrderService.java
      • 7.5 动态数据源控制器
        • DynamicController.java
    • 8. 方法四:使用MyBatis-Plus多数据源
      • 8.1 添加MyBatis-Plus依赖
        • pom.xml(添加MyBatis-Plus)
      • 8.2 MyBatis-Plus配置
        • application.yml(MyBatis-Plus多数据源配置)
      • 8.3 MyBatis-Plus实体类
        • User.java(MyBatis-Plus版本)
        • Order.java(MyBatis-Plus版本)
      • 8.4 MyBatis-Plus Mapper接口
        • UserPlusMapper.java
        • OrderPlusMapper.java
      • 8.5 MyBatis-Plus Service层
        • UserPlusService.java
        • OrderPlusService.java
      • 8.6 MyBatis-Plus控制器
        • UserPlusController.java
    • 9. 事务管理
      • 9.1 单数据源事务
        • 基本事务使用
      • 9.2 多数据源事务
        • ChainedTransactionManager(链式事务管理器)
        • 多数据源事务服务
      • 9.3 分布式事务
        • JTA配置(Atomikos)
        • application.yml(JTA配置)
        • JTA数据源配置
    • 10. 常见问题和解决方案
      • 10.1 循环依赖问题
        • 问题描述
        • 解决方案
      • 10.2 事务不生效问题
        • 问题1:类内部调用
        • 解决方案
        • 问题2:异常类型不匹配
        • 解决方案
      • 10.3 数据源配置错误
        • 问题:配置文件格式错误
        • 解决方案
      • 10.4 MyBatis映射问题
        • 问题:SQL映射找不到
        • 解决方案
      • 10.5 连接池配置问题
        • 监控和调优连接池
    • 11. 最佳实践
      • 11.1 配置规范
        • 1. 数据源命名规范
        • 2. 配置文件组织
      • 11.2 包结构规范
      • 11.3 性能优化
        • 1. 连接池优化
        • 2. 读写分离优化
      • 11.4 监控和维护
        • 1. 数据源监控
        • 2. 健康检查
      • 11.5 安全最佳实践
        • 1. 密码加密
        • 2. 连接参数安全
    • 12. 总结
      • 12.1 多数据源实现方式对比
      • 12.2 选择建议
        • 1. 简单业务场景
        • 2. 复杂业务场景
        • 3. 快速开发
        • 4. 企业级应用
      • 12.3 重要注意事项
      • 12.4 学习路径建议

1. 什么是多数据源

1.1 基本概念

多数据源(Multiple DataSources)是指在一个SpringBoot应用中同时连接和使用多个数据库的技术。这些数据库可以是:

  • 不同类型的数据库:MySQL、PostgreSQL、Oracle等
  • 相同类型的不同实例:主库、从库、不同业务的数据库
  • 不同环境的数据库:开发、测试、生产环境

1.2 传统单数据源 vs 多数据源

单数据源架构
SpringBoot应用 → 单个DataSource → 单个数据库
多数据源架构
SpringBoot应用 → DataSource1 → 数据库1(用户数据)→ DataSource2 → 数据库2(订单数据)→ DataSource3 → 数据库3(日志数据)

2. 为什么需要多数据源

2.1 业务场景需求

  1. 读写分离:主库写入,从库读取,提高性能
  2. 业务隔离:不同业务模块使用独立数据库
  3. 数据迁移:新老系统数据库并存
  4. 分布式架构:微服务架构中的数据分离

2.2 技术优势

  • 性能优化:分散数据库压力
  • 数据安全:重要数据隔离存储
  • 扩展性好:易于水平扩展
  • 故障隔离:一个数据库故障不影响其他业务

3. 多数据源的实现方式

3.1 静态多数据源

  • 编译时确定:在代码中明确指定使用哪个数据源
  • 配置简单:通过注解或配置类实现
  • 性能较好:没有动态切换的开销

3.2 动态多数据源

  • 运行时决定:根据业务逻辑动态选择数据源
  • 灵活性高:可以根据条件动态切换
  • 实现复杂:需要自定义路由逻辑

4. 环境准备

4.1 创建SpringBoot项目

pom.xml依赖配置
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.7.8</version><relativePath/></parent><groupId>com.example</groupId><artifactId>multi-datasource-demo</artifactId><version>1.0.0</version><name>multi-datasource-demo</name><description>SpringBoot多数据源配置示例</description><properties><java.version>1.8</java.version></properties><dependencies><!-- SpringBoot Web Starter --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- SpringBoot JDBC Starter --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jdbc</artifactId></dependency><!-- MyBatis Starter --><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>2.3.0</version></dependency><!-- MySQL驱动 --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><scope>runtime</scope></dependency><!-- 连接池 --><dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>1.2.16</version></dependency><!-- Lombok --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><!-- Test --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build>
</project>

4.2 准备测试数据库

创建两个测试数据库
-- 创建用户数据库
CREATE DATABASE user_db CHARACTER SET utf8mb4;-- 创建订单数据库
CREATE DATABASE order_db CHARACTER SET utf8mb4;-- 用户表
USE user_db;
CREATE TABLE user (id BIGINT PRIMARY KEY AUTO_INCREMENT,username VARCHAR(50) NOT NULL,email VARCHAR(100),age INT,create_time DATETIME DEFAULT CURRENT_TIMESTAMP
);INSERT INTO user (username, email, age) VALUES 
('张三', 'zhangsan@example.com', 25),
('李四', 'lisi@example.com', 30),
('王五', 'wangwu@example.com', 28);-- 订单表
USE order_db;
CREATE TABLE orders (id BIGINT PRIMARY KEY AUTO_INCREMENT,user_id BIGINT NOT NULL,product_name VARCHAR(100) NOT NULL,price DECIMAL(10,2),quantity INT,create_time DATETIME DEFAULT CURRENT_TIMESTAMP
);INSERT INTO orders (user_id, product_name, price, quantity) VALUES 
(1, 'iPhone 14', 8999.00, 1),
(2, 'MacBook Pro', 15999.00, 1),
(1, 'AirPods', 1999.00, 2);

5. 方法一:使用@Primary和@Qualifier注解

5.1 配置文件

application.yml
# SpringBoot多数据源配置
spring:# 主数据源配置(用户数据库)datasource:primary:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/user_db?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghaiusername: rootpassword: 123456type: com.alibaba.druid.pool.DruidDataSource# Druid连接池配置druid:initial-size: 5min-idle: 5max-active: 20max-wait: 60000time-between-eviction-runs-millis: 60000min-evictable-idle-time-millis: 300000validation-query: SELECT 1test-while-idle: truetest-on-borrow: falsetest-on-return: false# 从数据源配置(订单数据库)secondary:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/order_db?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghaiusername: rootpassword: 123456type: com.alibaba.druid.pool.DruidDataSourcedruid:initial-size: 5min-idle: 5max-active: 20max-wait: 60000time-between-eviction-runs-millis: 60000min-evictable-idle-time-millis: 300000validation-query: SELECT 1test-while-idle: truetest-on-borrow: falsetest-on-return: false# MyBatis配置
mybatis:mapper-locations: classpath:mapper/**/*.xmltype-aliases-package: com.example.entityconfiguration:map-underscore-to-camel-case: truelog-impl: org.apache.ibatis.logging.stdout.StdOutImpl# 日志配置
logging:level:com.example.mapper: debugorg.springframework.jdbc: debug

5.2 数据源配置类

DataSourceConfig.java
package com.example.config;import com.alibaba.druid.pool.DruidDataSource;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;import javax.sql.DataSource;/*** 多数据源配置类* 使用@Primary和@Qualifier注解方式实现多数据源*/
@Configuration
public class DataSourceConfig {/*** 创建主数据源(用户数据库)* @Primary 注解表示这是主要的数据源,当有多个同类型Bean时优先使用此Bean* @ConfigurationProperties 自动绑定配置文件中的属性*/@Primary@Bean(name = "primaryDataSource")@ConfigurationProperties(prefix = "spring.datasource.primary")public DataSource primaryDataSource() {return new DruidDataSource();}/*** 创建从数据源(订单数据库)*/@Bean(name = "secondaryDataSource")@ConfigurationProperties(prefix = "spring.datasource.secondary")public DataSource secondaryDataSource() {return new DruidDataSource();}/*** 主数据源的JdbcTemplate*/@Primary@Bean(name = "primaryJdbcTemplate")public JdbcTemplate primaryJdbcTemplate(@Qualifier("primaryDataSource") DataSource dataSource) {return new JdbcTemplate(dataSource);}/*** 从数据源的JdbcTemplate*/@Bean(name = "secondaryJdbcTemplate")public JdbcTemplate secondaryJdbcTemplate(@Qualifier("secondaryDataSource") DataSource dataSource) {return new JdbcTemplate(dataSource);}/*** 主数据源的事务管理器*/@Primary@Bean(name = "primaryTransactionManager")public DataSourceTransactionManager primaryTransactionManager(@Qualifier("primaryDataSource") DataSource dataSource) {return new DataSourceTransactionManager(dataSource);}/*** 从数据源的事务管理器*/@Bean(name = "secondaryTransactionManager")public DataSourceTransactionManager secondaryTransactionManager(@Qualifier("secondaryDataSource") DataSource dataSource) {return new DataSourceTransactionManager(dataSource);}
}

5.3 MyBatis配置类

PrimaryMyBatisConfig.java
package com.example.config;import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;import javax.sql.DataSource;/*** 主数据源MyBatis配置* 扫描用户相关的Mapper接口*/
@Configuration
@MapperScan(basePackages = "com.example.mapper.user", sqlSessionTemplateRef = "primarySqlSessionTemplate")
public class PrimaryMyBatisConfig {/*** 主数据源的SqlSessionFactory*/@Primary@Bean(name = "primarySqlSessionFactory")public SqlSessionFactory primarySqlSessionFactory(@Qualifier("primaryDataSource") DataSource dataSource) throws Exception {SqlSessionFactoryBean bean = new SqlSessionFactoryBean();bean.setDataSource(dataSource);// 设置MyBatis配置文件位置bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/user/*.xml"));// 设置实体类别名包路径bean.setTypeAliasesPackage("com.example.entity");return bean.getObject();}/*** 主数据源的SqlSessionTemplate*/@Primary@Bean(name = "primarySqlSessionTemplate")public SqlSessionTemplate primarySqlSessionTemplate(@Qualifier("primarySqlSessionFactory") SqlSessionFactory sqlSessionFactory) {return new SqlSessionTemplate(sqlSessionFactory);}
}
SecondaryMyBatisConfig.java
package com.example.config;import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;import javax.sql.DataSource;/*** 从数据源MyBatis配置* 扫描订单相关的Mapper接口*/
@Configuration
@MapperScan(basePackages = "com.example.mapper.order", sqlSessionTemplateRef = "secondarySqlSessionTemplate")
public class SecondaryMyBatisConfig {/*** 从数据源的SqlSessionFactory*/@Bean(name = "secondarySqlSessionFactory")public SqlSessionFactory secondarySqlSessionFactory(@Qualifier("secondaryDataSource") DataSource dataSource) throws Exception {SqlSessionFactoryBean bean = new SqlSessionFactoryBean();bean.setDataSource(dataSource);// 设置MyBatis配置文件位置bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/order/*.xml"));// 设置实体类别名包路径bean.setTypeAliasesPackage("com.example.entity");return bean.getObject();}/*** 从数据源的SqlSessionTemplate*/@Bean(name = "secondarySqlSessionTemplate")public SqlSessionTemplate secondarySqlSessionTemplate(@Qualifier("secondarySqlSessionFactory") SqlSessionFactory sqlSessionFactory) {return new SqlSessionTemplate(sqlSessionFactory);}
}

5.4 实体类

User.java
package com.example.entity;import lombok.Data;
import java.time.LocalDateTime;/*** 用户实体类*/
@Data
public class User {private Long id;private String username;private String email;private Integer age;private LocalDateTime createTime;
}
Order.java
package com.example.entity;import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDateTime;/*** 订单实体类*/
@Data
public class Order {private Long id;private Long userId;private String productName;private BigDecimal price;private Integer quantity;private LocalDateTime createTime;
}

5.5 Mapper接口

UserMapper.java
package com.example.mapper.user;import com.example.entity.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;import java.util.List;/*** 用户Mapper接口* 此接口会使用主数据源(用户数据库)*/
@Mapper
public interface UserMapper {/*** 查询所有用户*/List<User> findAll();/*** 根据ID查询用户*/User findById(@Param("id") Long id);/*** 根据用户名查询用户*/User findByUsername(@Param("username") String username);/*** 插入用户*/int insert(User user);/*** 更新用户*/int update(User user);/*** 删除用户*/int deleteById(@Param("id") Long id);
}
OrderMapper.java
package com.example.mapper.order;import com.example.entity.Order;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;import java.util.List;/*** 订单Mapper接口* 此接口会使用从数据源(订单数据库)*/
@Mapper
http://www.dtcms.com/a/324287.html

相关文章:

  • (Python)爬虫进阶(Python爬虫教程)(CSS选择器)
  • 2025年全国青少年信息素养大赛Scratch编程践挑战赛-小低组-初赛-模拟题
  • Linux系统编程Day11 -- 进程状态的优先级和特性
  • 移动端音频处理实践:59MB变声应用的技术实现分析
  • 机器学习-增加样本、精确率与召回率
  • LeetCode 869.重新排序得到 2 的幂:哈希表+排序(一次初始化)
  • Android 16 KB页面大小适配的权威技术方案总结
  • Android快速视频解码抽帧FFmpegMediaMetadataRetriever,Kotlin(2)
  • Android 开发问题:Invalid id; ID definitions must be of the form @+id/ name
  • 010601抓包工具及证书安装-基础入门-网络安全
  • 浅谈 A2A SDK 核心组件
  • 电脑本地摄像头做成rtsp流调用测试windows系统中
  • 【Docker实战】将Django应用容器化的完整指南
  • Pytorch深度学习框架实战教程-番外篇10-PyTorch中的nn.Linear详解
  • Linux-静态配置ip地址
  • 怎么将视频转换成字幕python作为工具
  • 计算机视觉(CV)——pytorch张量基本使用
  • 深入解析Java中的String、StringBuilder与StringBuffer:特性、区别与最佳实践
  • Gin 框架中的模板引擎使用指南
  • LeetCode 每日一题 2025/8/4-2025/8/10
  • mpv core_thread pipeline
  • c语言常见错误
  • MySQL 处理重复数据详细说明
  • ADK(Agent Development Kit)【2】调用流程详解
  • 智慧交通-道路积雪识别分割数据集labelme格式1985张2类别
  • python Flask简单图书管理 API
  • 【Linux知识】Linux grep 命令全面使用指南
  • 祝融号无线电工作频段
  • C++入门自学Day8-- 初识Vector
  • leetcode2379:得到K个黑块的最少涂色次数(定长滑动窗口)