Spring Boot 多数据源配置
第一种方式: AbstractRoutingDataSource
1.1. 手动切换数据源

application.properties
# Order
# 如果用Druid作为数据源,应该用url属性,而不是jdbc-url
spring.datasource.order.jdbc-url=jdbc:mysql://localhost:3306/order?useUnicode=true&characterEncoding=utf8&allowMultiQueries=true&useSSL=false
spring.datasource.order.username=root
spring.datasource.order.password=123456
spring.datasource.order.driver-class-name=com.mysql.cj.jdbc.Driver
# Stock
spring.datasource.stock.jdbc-url=jdbc:mysql://localhost:3306/stock?useUnicode=true&characterEncoding=utf8&allowMultiQueries=true&useSSL=false
spring.datasource.stock.username=root
spring.datasource.stock.password=123456
spring.datasource.stock.driver-class-name=com.mysql.cj.jdbc.Driver
# Account
spring.datasource.account.jdbc-url=jdbc:mysql://localhost:3306/account?useUnicode=true&characterEncoding=utf8&allowMultiQueries=true&useSSL=false
spring.datasource.account.username=root
spring.datasource.account.password=123456
spring.datasource.account.driver-class-name=com.mysql.cj.jdbc.Driver
配置数据源
DataSourceConfig.java
package com.cjs.example.config;
import com.alibaba.druid.pool.DruidDataSource;
import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
import com.zaxxer.hikari.HikariDataSource;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
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;
import java.util.HashMap;
import java.util.Map;
@Configuration
public class DataSourceConfig {
@Bean("orderDataSource")
@ConfigurationProperties(prefix = "spring.datasource.order")
public DataSource orderDataSource() {
// return new HikariDataSource();
// return new DruidDataSource();
return DataSourceBuilder.create().build();
}
@Bean("accountDataSource")
@ConfigurationProperties(prefix = "spring.datasource.account")
public DataSource accountDataSource() {
// return new HikariDataSource();
// return new DruidDataSource();
return DataSourceBuilder.create().build();
}
@Bean("stockDataSource")
@ConfigurationProperties(prefix = "spring.datasource.stock")
public DataSource stockDataSource() {
// return new HikariDataSource();
// return new DruidDataSource();
return DataSourceBuilder.create().build();
}
@Primary
@Bean("dynamicDataSource")
public DataSource dynamicDataSource(@Qualifier("orderDataSource") DataSource orderDataSource,
@Qualifier("accountDataSource") DataSource accountDataSource,
@Qualifier("stockDataSource") DataSource stockDataSource) {
Map<Object, Object> dataSourceMap = new HashMap<>(3);
dataSourceMap.put(DataSourceKey.ORDER.name(), orderDataSource);
dataSourceMap.put(DataSourceKey.STOCK.name(), stockDataSource);
dataSourceMap.put(DataSourceKey.ACCOUNT.name(), accountDataSource);
DynamicRoutingDataSource dynamicRoutingDataSource = new DynamicRoutingDataSource();
dynamicRoutingDataSource.setDefaultTargetDataSource(orderDataSource);
dynamicRoutingDataSource.setTargetDataSources(dataSourceMap);
return dynamicRoutingDataSource;
}
/* https://baomidou.com/pages/3b5af0/ */
@Bean
public MybatisSqlSessionFactoryBean sqlSessionFactoryBean(@Qualifier("dynamicDataSource") DataSource dataSource) {
MybatisSqlSessionFactoryBean sqlSessionFactoryBean = new MybatisSqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSource);
// sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mappers/*.xml"));
return sqlSessionFactoryBean;
}
}
由于是MyBatsi-Plus,所以配的是MybatisSqlSessionFactoryBean,如果是MyBatis,则应该是SqlSessionFactoryBean
DataSourceKey.java
package com.cjs.example.config;
public enum DataSourceKey {
/**
* Order data source key.
*/
ORDER,
/**
* Stock data source key.
*/
STOCK,
/**
* Account data source key.
*/
ACCOUNT
}
DynamicDataSourceContextHolder.java
package com.cjs.example.config;
public class DynamicDataSourceContextHolder {
private static final ThreadLocal<String> CONTEXT_HOLDER = ThreadLocal.withInitial(DataSourceKey.ORDER::name);
public static void setDataSourceKey(DataSourceKey key) {
CONTEXT_HOLDER.set(key.name());
}
public static String getDataSourceKey() {
return CONTEXT_HOLDER.get();
}
public static void clearDataSourceKey() {
CONTEXT_HOLDER.remove();
}
}
DynamicRoutingDataSource.java
package com.cjs.example.config;
import lombok.extern.slf4j.Slf4j;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
@Slf4j
public class DynamicRoutingDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
log.info("当前数据源 [{}]", DynamicDataSourceContextHolder.getDataSourceKey());
return DynamicDataSourceContextHolder.getDataSourceKey();
}
}
好了,配置完以后,在操作数据库之前,先设置用哪个数据源即可,就像下面这样:
DynamicDataSourceContextHolder.setDataSourceKey(DataSourceKey.ACCOUNT);
举个例子:
package com.cjs.example;
import com.cjs.example.account.entity.Account;
import com.cjs.example.account.service.IAccountService;
import com.cjs.example.config.DataSourceKey;
import com.cjs.example.config.DynamicDataSourceContextHolder;
import com.cjs.example.order.entity.Order;
import com.cjs.example.order.service.IOrderService;
import com.cjs.example.stock.entity.Stock;
import com.cjs.example.stock.service.IStockService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.math.BigDecimal;
@SpringBootTest
public class Demo1122ApplicationTests {
@Autowired
private IOrderService orderService;
@Autowired
private IAccountService accountService;
@Autowired
private IStockService stockService;
@Test
public void doBusiness() {
DynamicDataSourceContextHolder.setDataSourceKey(DataSourceKey.ORDER);
Order order = new Order();
order.setOrderNo("123");
order.setUserId("1");
order.setCommodityCode("abc");
order.setCount(1);
order.setAmount(new BigDecimal("9.9"));
orderService.save(order);
DynamicDataSourceContextHolder.setDataSourceKey(DataSourceKey.STOCK);
Stock stock = new Stock();
stock.setId(1);
stock.setCommodityCode("abc");
stock.setName("huawei");
stock.setCount(1);
stockService.updateById(stock);
DynamicDataSourceContextHolder.setDataSourceKey(DataSourceKey.ACCOUNT);
Account account = new Account();
account.setId(1);
account.setUserId("1");
account.setAmount(new BigDecimal(100));
accountService.updateById(account);
}
}
这样写看起来确实有些麻烦,通常可能不会像这样在一个方法里操作多个数据库,就比如说假设这是一个管理后台,为了图省事把所有业务都写在这一个项目里,这个时候就需要配置多个数据源,各个数据库的业务互相没有关联,只是写在同一个项目中而已,这样的话如果每次都手动设置数据源太麻烦,可以定义一个AOP切面来自动切换数据源。
1.2. 自动切换数据源
https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#aop-ataspectj
給刚才的代码升个级,利用AOP来拦截目标方法自动切换数据源
1、添加@EnableAspectJAutoProxy注解
package com.cjs.example;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@EnableAspectJAutoProxy
@MapperScan("com.cjs.example.*.mapper")
@SpringBootApplication
public class Demo1122Application {
public static void main(String[] args) {
SpringApplication.run(Demo1122Application.class, args);
}
}
2、定义切面、切点、通知
package com.cjs.example.aop;
import com.cjs.example.config.DataSourceKey;
import com.cjs.example.config.DynamicDataSourceContextHolder;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class DataSourceAdvice {
// @Pointcut("within(com.cjs.example.order..*)")
@Pointcut("execution(* com.cjs.example.order..*.*(..))")
public void orderPointcut() {}
// @Pointcut("within(com.cjs.example.account..*)")
@Pointcut("execution(* com.cjs.example.account..*.*(..))")
public void accountPointcut() {}
// @Pointcut("within(com.cjs.example.stock..*)")
@Pointcut("execution(* com.cjs.example.stock..*.*(..))")
public void stockPointcut() {}
@Around("orderPointcut()")
public Object order(ProceedingJoinPoint pjp) throws Throwable {
DynamicDataSourceContextHolder.setDataSourceKey(DataSourceKey.ORDER);
Object retVal = pjp.proceed();
DynamicDataSourceContextHolder.clearDataSourceKey();
return retVal;
}
@Around("accountPointcut()")
public Object account(ProceedingJoinPoint pjp) throws Throwable {
DynamicDataSourceContextHolder.setDataSourceKey(DataSourceKey.ACCOUNT);
Object retVal = pjp.proceed();
DynamicDataSourceContextHolder.clearDataSourceKey();
return retVal;
}
@Around("stockPointcut()")
public Object stock(ProceedingJoinPoint pjp) throws Throwable {
DynamicDataSourceContextHolder.setDataSourceKey(DataSourceKey.STOCK);
Object retVal = pjp.proceed();
DynamicDataSourceContextHolder.clearDataSourceKey();
return retVal;
}
}
现在就不用每次调用service方法前手动设置数据源了

工程结构

第二种方式:dynamic-datasource-spring-boot-starter
功能很强大,支持 数据源分组 ,适用于多种场景 纯粹多库 读写分离 一主多从 混合模式
https://github.com/baomidou/dynamic-datasource-spring-boot-starter
1、引入dynamic-datasource-spring-boot-starter
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>3.5.2</version>
</dependency>
2、配置数据源
spring:
datasource:
dynamic:
primary: master #设置默认的数据源或者数据源组,默认值即为master
strict: false #严格匹配数据源,默认false. true未匹配到指定数据源时抛异常,false使用默认数据源
datasource:
master:
url: jdbc:mysql://xx.xx.xx.xx:3306/dynamic
username: root
password: 123456
driver-class-name: com.mysql.jdbc.Driver # 3.2.0开始支持SPI可省略此配置
slave_1:
url: jdbc:mysql://xx.xx.xx.xx:3307/dynamic
username: root
password: 123456
driver-class-name: com.mysql.jdbc.Driver
slave_2:
url: ENC(xxxxx) # 内置加密,使用请查看详细文档
username: ENC(xxxxx)
password: ENC(xxxxx)
driver-class-name: com.mysql.jdbc.Driver
#......省略
#以上会配置一个默认库master,一个组slave下有两个子库slave_1,slave_2
主从配置,读写分离
# 多主多从 纯粹多库(记得设置primary) 混合配置
spring: spring: spring:
datasource: datasource: datasource:
dynamic: dynamic: dynamic:
datasource: datasource: datasource:
master_1: mysql: master:
master_2: oracle: slave_1:
slave_1: sqlserver: slave_2:
slave_2: postgresql: oracle_1:
slave_3: h2: oracle_2:
改造一下前面的例子
spring.datasource.dynamic.primary=order
# Order
spring.datasource.dynamic.datasource.order.url=jdbc:mysql://localhost:3306/order?useUnicode=true&characterEncoding=utf8&allowMultiQueries=true&useSSL=false
spring.datasource.dynamic.datasource.order.username=root
spring.datasource.dynamic.datasource.order.password=123456
spring.datasource.dynamic.datasource.order.driver-class-name=com.mysql.cj.jdbc.Driver
# Stock
spring.datasource.dynamic.datasource.stock.url=jdbc:mysql://localhost:3306/stock?useUnicode=true&characterEncoding=utf8&allowMultiQueries=true&useSSL=false
spring.datasource.dynamic.datasource.stock.username=root
spring.datasource.dynamic.datasource.stock.password=123456
spring.datasource.dynamic.datasource.stock.driver-class-name=com.mysql.cj.jdbc.Driver
# Account
spring.datasource.dynamic.datasource.account.url=jdbc:mysql://localhost:3306/account?useUnicode=true&characterEncoding=utf8&allowMultiQueries=true&useSSL=false
spring.datasource.dynamic.datasource.account.username=root
spring.datasource.dynamic.datasource.account.password=123456
spring.datasource.dynamic.datasource.account.driver-class-name=com.mysql.cj.jdbc.Driver
3、使用 @DS 切换数据源
@DS 可以注解在方法上或类上,同时存在就近原则 方法上注解 优先于 类上注解
| 注解 | 结果 |
| 没有@DS | 默认数据源 |
| @DS("dsName") | dsName可以为组名也可以为具体某个库的名称 |
package com.cjs.example.stock.service.impl;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.cjs.example.stock.entity.Stock;
import com.cjs.example.stock.mapper.StockMapper;
import com.cjs.example.stock.service.IStockService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
@DS("stock")
@Service
public class StockServiceImpl extends ServiceImpl<StockMapper, Stock> implements IStockService {
}
package com.cjs.example.account.service.impl;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.cjs.example.account.entity.Account;
import com.cjs.example.account.mapper.AccountMapper;
import com.cjs.example.account.service.IAccountService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
@DS("account")
@Service
public class AccountServiceImpl extends ServiceImpl<AccountMapper, Account> implements IAccountService {
}
Spring Boot 多数据源配置的更多相关文章
- Spring Boot多数据源配置(二)MongoDB
在Spring Boot多数据源配置(一)durid.mysql.jpa 整合中已经讲过了Spring Boot如何配置mysql多数据源.本篇文章讲一下Spring Boot如何配置mongoDB多 ...
- spring boot多数据源配置(mysql,redis,mongodb)实战
使用Spring Boot Starter提升效率 虽然不同的starter实现起来各有差异,但是他们基本上都会使用到两个相同的内容:ConfigurationProperties和AutoConfi ...
- spring boot(12)-数据源配置原理
本篇讲的不仅是数据源配置,这也是spring boot实现自动配置的一部分.要理解数据源的配置原理,首先要理解第十篇tomcat连接池的配置 数据源配置源码 这里截取org.springframewo ...
- 三、Spring Boot 多数据源配置
下面一个Java类是已经写好的根据配置文件动态创建多dataSource的代码,其原理也很简单,就是读取配置文件,根据配置文件中配置的数据源数量,动态创建dataSource并注册到Spring中. ...
- Spring Boot Druid数据源配置
package com.hgvip.config; import com.alibaba.druid.pool.DruidDataSource; import com.alibaba.druid.su ...
- 21. Spring Boot Druid 数据源配置解析
1.数据源配置属性类源码 package org.springframework.boot.autoconfigure.jdbc; @ConfigurationProperties( prefix = ...
- spring boot 多数据源配置与使用
在介绍使用JdbcTemplate和Spring-data-jpa时,都使用了单数据源.在单数据源的情况下,Spring Boot的配置非常简单,只需要在application.properties文 ...
- Spring Boot多数据源配置与使用
在单数据源的情况下,Spring Boot的配置非常简单,只需要在application.properties文件中配置连接参数即可.但是往往随着业务量发展,我们通常会进行数据库拆分或是引入其他数据库 ...
- Spring Boot (14) 数据源配置原理
数据源配置源码 这里截取org.springframework.boot.autoconfigure.jdbc.DataSourceConfiguration的部分源码,主要介绍Tomcat和Hika ...
- Spring Boot多数据源配置(一)durid、mysql、jpa整合
目前在做一个统计项目.需要多数据源整合,其中包括mysql和mongo.本节先讲mysql.durid.jpa与spring-boot的整合. 引入Durid包 <dependency> ...
随机推荐
- 餐饮行业SRM案例:方正璞华"采云链"携手和府捞面
方正璞华在与和府捞面的项目中,帮助整合采购系统,系统化管理供应商,优化采购流程和数据分析,建立储备知识库. 项目背景 企业介绍 ◇ 和府捞面成立于2012年,是国内中式面食头部品牌.自2013年首 ...
- Windows SMTP服务器的中继列表
今天在一台Windows 2019的SMTP服务器上想增加一个匿名中继的客户端地址.登录到服务器后发现原来的设置丢失了,而且原来的允许列表也不见了.界面变成了这样. https://www.cnblo ...
- day02-代码实现01
多用户即时通讯系统02 4.编码实现01 4.1功能实现-用户登录 4.1.1功能说明 因为还没有学习数据库,我们人为规定 用户名/id = 100,密码为 123456 就可以登录,其他用户不能登录 ...
- 3.云原生之Docker容器三大核心概念介绍
转载自:https://www.bilibili.com/read/cv15181760/?from=readlist docker search --no-trunc=false [镜像名称] #搜 ...
- 使用growpart扩容CentOS虚拟机磁盘
说明 一块磁盘,比如有130G,但是只有100G拿来用了,安装俩分区,一个是swap,另一个是根分区/,根分区采用的是LVM逻辑卷管理. 现在要做的是把剩余的这空闲30G分配给根分区使用 这个并不是新 ...
- 设置HTTP请求自动跳转HTTPS
第一种方式,分两种情况: 第一种情况:修改Nginx安装目录/conf/nginx.conf文件 server { listen 80; server_name localhost; #将localh ...
- WPF 的内部世界(Binding)
目录 一.控件与布局 二.Binding基础 前言 "一桥飞架南北, 天堑变通途" 写于1956年,1957年武汉长江大桥建成, 称之为:一桥飞架南北,大堑变通途.它形象地描述武汉 ...
- 《Go 精进之路》 读书笔记 (第一次更新)
<Go 精进之路> 读书笔记.简要记录自己打五角星的部分,方便复习巩固.目前看到p120 Go 语言遵从的设计哲学为组合 垂直组合:类型嵌入,快速让一个类型复用其他类型已经实现的能力,实现 ...
- IC入门课第五课作业:完善 Microblog 前端(1、显示发布者的名字;2、增加新UI、3、关注其他学员的 canister)
上周完成了 IC 入门课程第五课的作业 现将答案贴出,欢迎同学们参考,禁止抄袭作业哦 课程作业 (完善 microblog 前端) 1. 显示消息的发布者名字 a. 给 Message 增加 auth ...
- 企业MES系统与ERP信息集成要素有哪些?
关于要讲明企业MES系统与ERP信息集成要素有哪些,得先弄清楚他们之间的关系:从工厂的管理来说,ERP在上MES在下,ERP统领企业全局包括MES,为管理层服务,重心在于企业决策,ERP对企业宏观管理 ...