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

Spring Boot 多数据源配置

一、为什么需要多数据源?
多数据源在以下场景中非常有用:

数据库分库分表:业务数据分布在不同的数据库实例中

读写分离:主库处理写操作,从库处理读操作

多租户系统:每个租户使用独立的数据源

异构数据库:同时使用 MySQL、PostgreSQL、Oracle 等不同数据库

数据迁移和同步:需要同时访问新旧两个数据库系统

二、基础依赖配置
首先在 pom.xml 中添加必要的依赖:

<dependencies><!-- Spring Boot Starter --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- 数据访问相关 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-jpa</artifactId></dependency><!-- 多数据源动态路由 --><dependency><groupId>com.baomidou</groupId><artifactId>dynamic-datasource-spring-boot-starter</artifactId><version>3.5.0</version></dependency><!-- 数据库驱动 --><dependency><groupId>org.postgresql</groupId><artifactId>postgresql</artifactId><scope>runtime</scope></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.33</version></dependency>
</dependencies>

三、方案一:基于 dynamic-datasource 的配置(推荐)
3.1 配置文件设置

spring:datasource:dynamic:primary: master    # 设置默认数据源strict: true       # 严格匹配模式datasource:master:url: jdbc:postgresql://localhost:5432/main_dbusername: adminpassword: passworddriver-class-name: org.postgresql.Driverslave:url: jdbc:mysql://localhost:3306/log_dbusername: log_userpassword: log_passworddriver-class-name: com.mysql.cj.jdbc.Driverreport:url: jdbc:postgresql://localhost:5432/report_dbusername: report_userpassword: report_passworddriver-class-name: org.postgresql.Driver# 连接池配置hikari:max-pool-size: 20min-idle: 5connection-timeout: 30000idle-timeout: 600000

3.2 使用注解切换数据源

@Service
public class BusinessService {// 使用默认数据源(master)public void createUser(User user) {userRepository.save(user);}// 使用从数据源@DS("slave")public List<Log> getLogs(String userId) {return logRepository.findByUserId(userId);}// 使用报表数据源@DS("report")public Report generateReport(Date startDate, Date endDate) {return reportRepository.generateReport(startDate, endDate);}
}

3.3 事务管理

@Service
public class TransactionalService {// 单个数据源事务@Transactional@DS("master")public void updateUser(User user) {userRepository.save(user);}// 多数据源事务(需要分布式事务支持)@DS("master")@Transactionalpublic void crossDatabaseOperation(User user, Log log) {userRepository.save(user);switchToSlave();logRepository.save(log);}@DS("slave")public void switchToSlave() {// 方法级别切换数据源}
}

四、方案二:手动配置多数据源

@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(basePackages = "com.example.repository.primary",entityManagerFactoryRef = "primaryEntityManagerFactory",transactionManagerRef = "primaryTransactionManager"
)
public class PrimaryDataSourceConfig {@Bean@Primary@ConfigurationProperties("spring.datasource.primary")public DataSource primaryDataSource() {return DataSourceBuilder.create().build();}@Bean@Primarypublic LocalContainerEntityManagerFactoryBean primaryEntityManagerFactory(EntityManagerFactoryBuilder builder) {return builder.dataSource(primaryDataSource()).packages("com.example.entity.primary").persistenceUnit("primary").properties(jpaProperties()).build();}@Bean@Primarypublic PlatformTransactionManager primaryTransactionManager(@Qualifier("primaryEntityManagerFactory") EntityManagerFactory entityManagerFactory) {return new JpaTransactionManager(entityManagerFactory);}private Map<String, Object> jpaProperties() {Map<String, Object> props = new HashMap<>();props.put("hibernate.hbm2ddl.auto", "update");props.put("hibernate.dialect", "org.hibernate.dialect.PostgreSQLDialect");return props;}
}

4.2 从数据源配置

@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(basePackages = "com.example.repository.secondary",entityManagerFactoryRef = "secondaryEntityManagerFactory",transactionManagerRef = "secondaryTransactionManager"
)
public class SecondaryDataSourceConfig {@Bean@ConfigurationProperties("spring.datasource.secondary")public DataSource secondaryDataSource() {return DataSourceBuilder.create().build();}@Beanpublic LocalContainerEntityManagerFactoryBean secondaryEntityManagerFactory(EntityManagerFactoryBuilder builder) {return builder.dataSource(secondaryDataSource()).packages("com.example.entity.secondary").persistenceUnit("secondary").properties(jpaProperties()).build();}@Beanpublic PlatformTransactionManager secondaryTransactionManager(@Qualifier("secondaryEntityManagerFactory") EntityManagerFactory entityManagerFactory) {return new JpaTransactionManager(entityManagerFactory);}
}

五、方案三:动态数据源路由
5.1 数据源路由配置

public class DynamicDataSource extends AbstractRoutingDataSource {private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>();public static void setDataSourceKey(String dataSourceKey) {CONTEXT_HOLDER.set(dataSourceKey);}public static String getDataSourceKey() {return CONTEXT_HOLDER.get();}public static void clearDataSourceKey() {CONTEXT_HOLDER.remove();}@Overrideprotected Object determineCurrentLookupKey() {return getDataSourceKey();}
}@Configuration
public class DynamicDataSourceConfig {@Bean@ConfigurationProperties("spring.datasource.master")public DataSource masterDataSource() {return DataSourceBuilder.create().build();}@Bean@ConfigurationProperties("spring.datasource.slave")public DataSource slaveDataSource() {return DataSourceBuilder.create().build();}@Beanpublic DataSource dynamicDataSource() {Map<Object, Object> targetDataSources = new HashMap<>();targetDataSources.put("master", masterDataSource());targetDataSources.put("slave", slaveDataSource());DynamicDataSource dynamicDataSource = new DynamicDataSource();dynamicDataSource.setTargetDataSources(targetDataSources);dynamicDataSource.setDefaultTargetDataSource(masterDataSource());return dynamicDataSource;}
}

5.2 切面编程实现自动切换

@Aspect
@Component
public class DataSourceAspect {@Before("@annotation(targetDataSource)")public void switchDataSource(JoinPoint point, TargetDataSource targetDataSource) {if (!DynamicDataSourceContextHolder.containDataSourceKey(targetDataSource.value())) {throw new RuntimeException("数据源 " + targetDataSource.value() + " 不存在");}DynamicDataSourceContextHolder.setDataSourceKey(targetDataSource.value());}@After("@annotation(targetDataSource)")public void restoreDataSource(JoinPoint point, TargetDataSource targetDataSource) {DynamicDataSourceContextHolder.clearDataSourceKey();}
}@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TargetDataSource {String value() default "master";
}

文章转载自:

http://jJ4f5xLt.cspwj.cn
http://NZhKAKYN.cspwj.cn
http://lAudgEcr.cspwj.cn
http://GkYIYH1Z.cspwj.cn
http://PxZXSqCJ.cspwj.cn
http://xevvMe3e.cspwj.cn
http://o0CiaeNZ.cspwj.cn
http://yZdTgkxu.cspwj.cn
http://53UcDTrT.cspwj.cn
http://OEBGsl7E.cspwj.cn
http://lZnzhnkf.cspwj.cn
http://K8s3LgDU.cspwj.cn
http://42V3r5sl.cspwj.cn
http://1HURiiJF.cspwj.cn
http://FDPeeg0P.cspwj.cn
http://iN3g2wbV.cspwj.cn
http://n4cfhMsU.cspwj.cn
http://gus95WlT.cspwj.cn
http://rtDNCQlV.cspwj.cn
http://Sq1QrZuR.cspwj.cn
http://1LuaauyQ.cspwj.cn
http://sh5Tj7HS.cspwj.cn
http://Na5kOuyh.cspwj.cn
http://mR43iBP4.cspwj.cn
http://bcIq36X7.cspwj.cn
http://lbQGDBdM.cspwj.cn
http://s62dQdjp.cspwj.cn
http://F1BYpwGa.cspwj.cn
http://phP343UI.cspwj.cn
http://5RqcJTP6.cspwj.cn
http://www.dtcms.com/a/373277.html

相关文章:

  • 信奥赛csp初赛高频考点真题分类解析之:基本运算
  • langchain 输出解析器 Output Parser
  • [数据结构] 栈 · Stack
  • 大语言模型的链式思维推理:从理论到实践
  • C语言快速排序
  • 软件可靠性失效严重程度分类与深度解析
  • 如何让dify分类器更加精准的分类?
  • C# Web API 前端传入参数时间为Utc
  • Python爬虫实战:研究3D plotting模块,构建房地产二手房数据采集和分析系统
  • sglang pytorch NCCL hang分析
  • langchain 缓存 Caching
  • Spark生态全景图:图计算与边缘计算的创新实践
  • 最长上升/下降子序列的长度(动态规划)
  • 自动驾驶中的传感器技术38——Lidar(13)
  • 计算机组成原理:计算机的分类
  • Spark SQL解析查询parquet格式Hive表获取分区字段和查询条件
  • 辨析——汇编 shell C语言
  • 免费的SSL和付费SSL 证书差异
  • 全新 Navicat On-Prem Server 3 正式上线,数据库云管理能力全面跃升
  • 华大 MCU 串口 PWM 控制方案完整笔记
  • 档案管理软件
  • Qoder 使用说明书,公测期免费体验
  • 实现自己的AI视频监控系统-第四章-基于langchain的AI大模型与智能体应用2
  • 消息队列-初识kafka
  • linux 100个问答81~101 主要是k8s相关
  • 【C++设计模式】第三篇:观察者模式(别名:发布-订阅模式、模型-视图模式、源-监听器模式)
  • OpenCV C++ 二值图像处理:阈值化技术全解析
  • OpenCV C++ 形态学分析:从基础操作到高级应用
  • 区块链里的 “信标” 是啥?
  • ROS与SDF/URDF的关系及其设计差异(为什么ROS不能直接调用sdf模型进行控制)