springboot多数据源配置实例

这里我们使用两个数据库, 但是数据库的表结构一致(至少多数据源涉及到的表需要结构一致), 以避免不能共用一套代码

DataSourceType

public enum DataSourceType {

//NONE用来返回默认

NONE(“”),

DB1(“db1”),

DB2(“db2”),

;

//省略部分代码

}

复制代码

此文件是数据源的枚举, 这里的枚举我们只配置里一个参数, 可以依据业务扩展. 本枚举的值代表数据源的名称, 是和配置文件里的配置对应上的

另外说明一下, NONE是用来代表默认数据源的, 是为了方便编程添加的, 不是必须的

DataSourceUtil

public class DataSourceUtil {

private static final ThreadLocal localDataSource = new ThreadLocal<>();

private DataSourceUtil(){

}

public static DataSourceType get() {

return localDataSource.get();

}

public static void set(DataSourceType type){

localDataSource.set(type);

}

public static void remove() {

localDataSource.remove();

}

}

复制代码

数据源切换工具类, 这里的核心是ThreadLocal<DataSourceType>变量, ThreadLocal以前有过文档分析, 主要是用来维护线程内部变量的, 其中:

  • get()方法用来获取数据源

  • set()方法用来设置数据源

  • remove()用来清除数据源

MybatisConfig*

@Configuration

@MapperScan(“jin.panpan.database.dao”)

public class MybatisConfig {

//默认数据源

@Primary

@Bean(“db1”)

@ConfigurationProperties(prefix = “spring.datasource.db1”)

public DataSource dataSource1(){

return DataSourceBuilder.create().build();

}

@Bean(“db2”)

@ConfigurationProperties(prefix = “spring.datasource.db2”)

public DataSource dataSource2(){

return DataSourceBuilder.create().build();

}

//动态数据源选择

@Bean

public DynamicDataSource dynamicDataSource(@Qualifier(“db1”) DataSource db1,

@Qualifier(“db2”) DataSource db2){

Map<Object, Object> map = new HashMap<>();

//此处的key 要和DynamicDataSource类的determineCurrentLookupKey方法返回值一致

map.put(DataSourceType.DB1.getDatabase(), db1);

map.put(DataSourceType.DB2.getDatabase(), db2);

DynamicDataSource dynamicDataSource = new DynamicDataSource();

dynamicDataSource.setTargetDataSources(map);

//设置默认数据库, 选择的数据库Bean注入时要带@Primary注释

dynamicDataSource.setDefaultTargetDataSource(db1);

return dynamicDataSource;

}

//会话工厂配置

@Bean

public SqlSessionFactory sqlSessionFactory(DynamicDataSource dynamicDataSource) throws Exception {

SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();

factoryBean.setDataSource(dynamicDataSource);

Resource[] resources = new PathMatchingResourcePatternResolver()

.getResources(“classpath:mappers/*xml”);

factoryBean.setMapperLocations(resources);

return factoryBean.getObject();

}

//事务管理配置

@Bean

public PlatformTransactionManager transactionManager(DynamicDataSource dynamicDataSource){

return new DataSourceTransactionManager(dynamicDataSource);

}

}

复制代码

本类主要用来配置和注入mybatis相关配置bean, 主要有下面几步:

  • 自动扫描mapper接口配置

@MapperScan(“jin.panpan.database.dao”)

一般常规配置, 如果不配置多数据源, 一般是在入口类上注解

  • 数据源注入

@ConfigurationProperties 导入配置文件里的配置

@Bean 指定数据源名称

@Primary 标记默认数据源

  • 数据源动态选择Bean注入

加载多个数据源, 如果需要的话, 指定默认数据源

需要注意数据源容器Map的key要和DataSourceType里的字段值一致, 否则无法切换

  • 会话工厂Bean配置

单数据源时, 一般无需手动配置; 多数据源时, 需要手动指定会话工厂, 但是配置是模式化的

  • 事务管理Bean配置

事务管理的配置也是模式化的, 一般无特殊处理之处

DynamicDataSource*

public class DynamicDataSource extends AbstractRoutingDataSource {

private static final Logger logger = LoggerFactory.getLogger(DynamicDataSource.class);

@Override

protected Object determineCurrentLookupKey() {

String database = DataSourceUtil.get() == null ? null : DataSourceUtil.get().getDatabase();

logger.info(“DynamicDataSource, 动态数据源返回={}”, database);

return database;

}

}

复制代码

此类其实是动态路由数据源的核心类, 扩展AbstractRoutingDataSource类, 重写determineCurrentLookupKey方法, determineCurrentLookupKey方法的返回值就是动态数据源, 返回值需要和配置文件及MybatisConfig里的一一对应.

DataSource和DataSourceAspect*

@Retention(RetentionPolicy.RUNTIME)

@Target(ElementType.METHOD)

@Documented

public @interface DataSource {

DataSourceType value() default DataSourceType.NONE;

}

复制代码

DataSource是一个方法注解, 用来作为数据源切换切入点, 此处可以使用类注解或者直接正则织入想监控的点

DataSourceAspect是注解的实现, 是核心实现

@Aspect

@Component

public class DataSourceAspect {

private static final Logger logger = LoggerFactory.getLogger(DataSourceAspect.class);

@Pointcut(“@annotation(jin.panpan.database.dynamic.datasource.annotate.DataSource)”)

public void pointCut() {

}

@SneakyThrows

@Around(“pointCut()”)

public Object around(ProceedingJoinPoint pjp) {

//数据源

DataSourceType dataSourceType = null;

//方法对象

MethodSignature signature = (MethodSignature) pjp.getSignature();

Method method = signature.getMethod();

//注释

DataSource dynamicDataSource = method.getAnnotation(DataSource.class);

String path = null;

//注释中指定数据源, 则使用数据库中的数据源

if(dynamicDataSource.value()!=null && dynamicDataSource.value() != DataSourceType.NONE){

dataSourceType = dynamicDataSource.value();

}

//否则从请求路径中获取

if(dataSourceType == null){

HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();

path = request.getRequestURL().toString();

//TODO 待优化 需截取域名进行检查

if(path.contains(DataSourceType.DB1.getDatabase())){

dataSourceType = DataSourceType.DB1;

}

if(path.contains(DataSourceType.DB2.getDatabase())){

dataSourceType = DataSourceType.DB2;

}

}

logger.info(“DataSource, 方法={}, 数据源={}, 路径={}, 注释自带数据源={}”,

method.getName(), dataSourceType==null ? null : dataSourceType.getDatabase(),

path, dynamicDataSource.value());

if(dataSourceType == null){

throw new Exception(“非法数据源”);

}

//指定数据库

DataSourceUtil.set(dataSourceType);

Object result = pjp.proceed();

//清除数据库

logger.info(“DataSource, 清除数据源, 方法={}, 数据源={}, 路径={}, 注释自带数据源={}”,

method.getName(), dataSourceType.getDatabase(),

path, dynamicDataSource.value());

DataSourceUtil.remove();

return result;

}

}

复制代码

重要的步骤已经加了注解, 完成的工作是:

  • 获取数据源 从注解/从路径

  • 指定数据源

  • 调用结束后清除数据源

核心步骤是通过DataSourceUtil切换数据源

此方法是实现切换逻辑的核心方法, 具体的切换逻辑, 应该由业务逻辑决定, 这里只是示范

逻辑验证


这里说明一下测试数据:

两个数据库: db1 db2

db1中有一条数据 id=1

db2中没有数据

核心代码也有日志, 可以依据日志观察数据源

为了区分路径, 在hosts里加了如下配置, 以区分不同环境路径:

127.0.0.1       db1.db.com

127.0.0.1       db2.db.com

127.0.0.1       db.com

复制代码

不指定数据源, 也不启用数据源注解

@GetMapping(“queryById/{id}”)

public Result selectById(@PathVariable(“id”) Long id){

BasTableEntity entity;

try {

entity = basTableService.queryById(id);

}catch (Exception e){

logger.error(“查询异常, 错误={}”, e.getMessage(), e);

return Result.fail(null, “查询异常”);

}

return Result.success(entity);

}

复制代码

按预期使用默认数据源db1,可以查询到一条数据

image.png

启用注解, 不指定数据源

@DataSource

@GetMapping(“queryByIdj/{id}”)

public Result selectByIdj(@PathVariable(“id”) Long id){

BasTableEntity entity;

try {

entity = basTableService.queryById(id);

}catch (Exception e){

logger.error(“查询异常, 错误={}”, e.getMessage(), e);

return Result.fail(null, “查询异常”);

}

return Result.success(entity);

}

复制代码

按预期将启用地址匹配数据源

image.png

image.png

启用注解, 指定数据源

@DataSource(DataSourceType.DB2)

@GetMapping(“queryByIdp/{id}”)

public Result selectByIdp(@PathVariable(“id”) Long id){

BasTableEntity entity;

try {

entity = basTableService.queryById(id);

}catch (Exception e){

logger.error(“查询异常, 错误={}”, e.getMessage(), e);

return Result.fail(null, “查询异常”);

}

return Result.success(entity);

}

复制代码

按预期将使用指定的数据源2

image.png

测试在线程间切换时, 导致的数据源丢失

@SneakyThrows

@DataSource(DataSourceType.DB2)

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

总结

一般像这样的大企业都有好几轮面试,所以自己一定要花点时间去收集整理一下公司的背景,公司的企业文化,俗话说「知己知彼百战不殆」,不要盲目的去面试,还有很多人关心怎么去跟HR谈薪资。

这边给大家一个建议,如果你的理想薪资是30K,你完全可以跟HR谈33~35K,而不是一下子就把自己的底牌暴露了出来,不过肯定不能说的这么直接,比如原来你的公司是25K,你可以跟HR讲原来的薪资是多少,你们这边能给到我的是多少?你说我这边希望可以有一个20%涨薪。

最后再说几句关于招聘平台的,总之,简历投递给公司之前,请确认下这家公司到底咋样,先去百度了解下,别被坑了,每个平台都有一些居心不良的广告党等着你上钩,千万别上当!!!

Java架构学习资料,学习技术内容包含有:Spring,Dubbo,MyBatis, RPC, 源码分析,高并发、高性能、分布式,性能优化,微服务 高级架构开发等等。

还有Java核心知识点+全套架构师学习资料和视频+一线大厂面试宝典+面试简历模板可以领取+阿里美团网易腾讯小米爱奇艺快手哔哩哔哩面试题+Spring源码合集+Java架构实战电子书。
在这里插入图片描述

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
1a72faed303032d36.jpg" alt=“img” style=“zoom: 33%;” />

总结

一般像这样的大企业都有好几轮面试,所以自己一定要花点时间去收集整理一下公司的背景,公司的企业文化,俗话说「知己知彼百战不殆」,不要盲目的去面试,还有很多人关心怎么去跟HR谈薪资。

这边给大家一个建议,如果你的理想薪资是30K,你完全可以跟HR谈33~35K,而不是一下子就把自己的底牌暴露了出来,不过肯定不能说的这么直接,比如原来你的公司是25K,你可以跟HR讲原来的薪资是多少,你们这边能给到我的是多少?你说我这边希望可以有一个20%涨薪。

最后再说几句关于招聘平台的,总之,简历投递给公司之前,请确认下这家公司到底咋样,先去百度了解下,别被坑了,每个平台都有一些居心不良的广告党等着你上钩,千万别上当!!!

Java架构学习资料,学习技术内容包含有:Spring,Dubbo,MyBatis, RPC, 源码分析,高并发、高性能、分布式,性能优化,微服务 高级架构开发等等。

还有Java核心知识点+全套架构师学习资料和视频+一线大厂面试宝典+面试简历模板可以领取+阿里美团网易腾讯小米爱奇艺快手哔哩哔哩面试题+Spring源码合集+Java架构实战电子书。
[外链图片转存中…(img-80AF4E9D-1713406355805)]

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值