2023年元旦,我妈又开始了对我的念叨
妈:你到底想多少岁结婚
(资料图片仅供参考)
我:60
妈:60,你想找个多大的
我:找个55的啊,她55我60,结婚都有退休金,不用上班不用生孩子,不用买车买房,成天就是玩儿
我:而且一结婚就是白头偕老,多好
我妈直接一大嘴巴子呼我脸上
需求背景最近接到一个需求,需要从两个数据源获取数据,然后进行汇总展示
一个数据源是MySQL,另一个数据源是SQL Server
楼主是一点都不慌的,因为我写过好几篇关于数据源的文章
spring集成mybatis实现mysql读写分离
原理解密 → Spring AOP 实现动态数据源(读写分离),底层原理是什么
Spring 下,关于动态数据源的事务问题的探讨
我会慌?
但还是有点小拒绝,为什么了?
自己实现的话,要写的东西还是很多,要写AOP,还要实现AbstractRoutingDataSource,还要用到ThreadLocal,...
如果考虑更远一些,事务、数据源之间的嵌套等等,要如何保证正确?
但好在这次需求只是查询,然后汇总,问题就简单很多了,但还是觉得有点小繁琐
当然,如上只是楼主的臆想
有小伙伴可能会问道:能不能合到一个数据源?
楼主只能说:别问了,再问就不礼貌了
既然改变不了,那就盘它
难道就没有现成的多数据源工具?
因为用到了Mybatis-Plus,楼主试着Google了一下
直接一发入魂,眼前一黑,不对,是眼前一亮!
感觉就是它了!
MyBatis-Plus 多数据源关于苞米豆(baomidou),我们最熟悉的肯定是MyBatis-Plus
但旗下还有很多其他优秀的组件
多数据源就是其中一个,今天我们就来会会它
数据源准备用docker准备一个MySQL和SQL Server,图省事,两个数据库服务器放到同个docker下了
有小伙伴会觉得放一起不合适,有单点问题!
楼主只是为了演示,纠结那么细,当心敲你狗头
MySQL版本:8.0.27
建库:datasource_mysql,建表:tbl_user,并插入初始化数据
CREATE DATABASE datasource_mysql;USE datasource_mysql;CREATE TABLE tbl_user ( id INT UNSIGNED NOT NULL AUTO_INCREMENT, user_name VARCHAR(50), PRIMARY KEY(id));INSERT INTO tbl_user(user_name) VALUES("张三"),("李四");View Code
SQL Server版本:Microsoft SQL Server 2017 ...,是真长,跟楼主一样长!
建库:datasource_mssql,建表:tbl_order,并插入初始化数据
CREATE DATABASE datasource_mssql;USE datasource_mssql;CREATE TABLE tbl_order( id BIGINT PRIMARY KEY IDENTITY(1,1), order_no NVARCHAR(50), created_at DATETIME NOT NULL DEFAULT(GETDATE()), updated_at DATETIME NOT NULL DEFAULT(GETDATE()));INSERT INTO tbl_order(order_no) VALUES("123456"),("654321");View Codedynamic-datasource 使用
基于spring-boot 2.2.10.RELEASE、mybatis-plus 3.1.1搭建
dynamic-datasource-spring-boot-starter也是3.1.1
依赖很简单,pom.xml
View Code4.0.0 com.lee mybatis-plus-dynamic-datasource 1.0-SNAPSHOT org.springframework.boot spring-boot-starter-parent 2.2.10.RELEASE 1.8 8 8 UTF-8 UTF-8 3.1.1 6.2.1.jre8 org.springframework.boot spring-boot-starter-web org.projectlombok lombok com.baomidou mybatis-plus-boot-starter ${mybatis-plus-boot-starter.version} com.baomidou dynamic-datasource-spring-boot-starter ${mybatis-plus-boot-starter.version} mysql mysql-connector-java com.microsoft.sqlserver mssql-jdbc ${mssql-jdbc.version}
配置也很简单,application.yml
server: port: 8081spring: application: name: dynamic-datasource datasource: dynamic: datasource: mssql_db: driver-class-name: com.microsoft.sqlserver.jdbc.SQLServerDriver url: jdbc:sqlserver://10.5.108.225:1433;DatabaseName=datasource_mssql;IntegratedSecurity=false;ApplicationIntent=ReadOnly;MultiSubnetFailover=True username: sa password: Root#123456 mysql_db: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://10.5.108.225:3306/datasource_mysql?useSSL=false&useUnicode=true&characterEncoding=utf-8&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=Asia/Shanghai username: root password: 123456 primary: mssql_db strict: falsemybatis-plus: mapper-locations: classpath:mappers/*.xml configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImplView Code
然后在对应的类或者方法上加上注解DS("数据源名称")即可,例如
我们来看下效果
是不是很神奇?
完整代码:mybatis-plus-dynamic-datasource
原理探究@DS用于指定数据源,可以注解在方法上或类上,同时存在则采用就近原则 方法上注解 优先于 类上注解
这可不是我瞎说,官方文档就是这么写的
难道一个@DS就有如此强大的功能?你们不信,我也不信,它背后肯定有人!
那么我们就来揪一揪背后的它
怎么揪了,这又是个难题,我们先打个断点,看一下调用栈
点一下,瞬间高潮了,不是,是瞬间清醒了
红线框住的,分 2 点:1:determineDatasource,2:DynamicDataSourceContextHolder.push
我们先看determineDatasource
1、获取Method对象
2、该方法上是否有 DS 注解,有则取方法的 DS 注解,没有则取方法对应的类上的 DS 注解;这个看明白了没?
3、获取注解的值,也就是@DS("mysql_db")中的mysql_db
4、如果数据源名不为空并且数据原名以动态前缀(#)开头,则你们自己去跟dsProcessor.determineDatasource
否则则直接返回数据源名
针对案例的话,这里肯定是返回类上的数据源名(方法上没有指定数据源,也没有以动态前缀开头)
我们再来看看DynamicDataSourceContextHolder.push
很简单,但LOOKUP_KEY_HOLDER很有意思
是一个栈,而非楼主在spring集成mybatis实现mysql读写分离采用的
至于为什么,人家注释已经写的很清楚了,试问楼主的实现能满足一级一级数据源切换的调用场景吗?
但不管怎么说,LOOKUP_KEY_HOLDER的类型还是ThreadLocal
接下来该分析什么?
我们回顾下:原理解密 → Spring AOP 实现动态数据源(读写分离),底层原理是什么
直接跳到总结
框住的 3 条,上面的 2 条在上面已经分析过了把,是不是?你回答是就完事了
注意,楼主的DynamicDataSource是自实现的类,继承了spring-jdbc的AbstractRoutingDataSource
那我们就找AbstractRoutingDataSource的实现类呗
发现它就一个实现类,并且是在spring-jdbc下,而不是在com.baomidou下
莫非苞米豆有自己的AbstractRoutingDataSource? 我们来看看AbstractDataSource的实现类有哪些
看到了没,那么我们接下来就分析它
内容很简单,最重要的determineDataSource还是个抽象方法,那没办法了,看它有哪些子类实现
DynamicRoutingDataSource的determineDataSource方法如下
DynamicDataSourceContextHolder有没有感觉到熟悉?
想想它的ThreadLocal
出栈,获取到当前的数据源名;接下来该分析谁了?
那肯定是getDataSource方法
1、如果数据源为空,那么直接返回默认数据源,对应配置文件中的
2、分组数据源,我们的示例代码那么简单,应该没涉及到这个,先不管
3、所有数据源,是一个LinkHashMap,key 是数据源名,value 是数据源
可想而知,我们示例的数据源获取就是从该 map 获取的
4、是否启用严格模式,默认不启动。严格模式下未匹配到数据源直接报错,,非严格模式下则使用默认数据源primary所设置的数据源
5、对应 4,未开启严格模式,未匹配到数据源则使用primary所设置的数据源
那现在又该分析谁?肯定是dataSourceMap的值是怎么put进去的
我们看哪些地方用到了dataSourceMap
发现就一个地方进行了put
那这个addDataSource方法又在哪被调用了?
DynamicRoutingDataSource实现了InitializingBean,所以在启动过程中,它的afterPropertiesSet方法会被调用,至于为什么,大家自行去查阅
接下来该分析什么?那肯定是Map
我们跟进loadDataSources(),发现有两个类都有该方法
那么我们应该跟谁?有两种方法
1、凭感觉,我们的配置文件是yml
2、打断点,重新启动项目,一目了然
YmlDynamicDataSourceProvider的loadDataSources方法如下
(这里留个疑问:dataSourcePropertiesMap存放的是什么,值是如何 put 进去的?)
继续往下跟createDataSourceMap方法
1、配置文件中的数据源属性,断点下就很清楚了
2、根据数据源属性创建数据源,然后放进dataSourceMap中
创建数据源的过程就不跟了,感兴趣的自行去研究
至此,不知道大家清楚了没? 我反正是晕了
总结1、万变不离其宗,多数据源的原理是不变的
原理解密 → Spring AOP 实现动态数据源(读写分离),底层原理是什么
2、苞米豆的多数据源的自动配置类
com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceAutoConfiguration
这个配置类很重要,很多重要的对象都是在这里注入到Spring容器中的
关于自动配置,大家可参考:springboot2.0.3源码篇 - 自动配置的实现,发现也不是那么复杂
3、遇到问题,不要立马一头扎进去,自己实现,多查查,看是否有现成的第三方实现
自己实现,很容易踩别人踩过的坑,容易浪费时间;另外局限性太大,不易拓展,毕竟一人之力有限
-
天天资讯:苞米豆的多数据源 → dynamic-datasource-spring-boot-starter,挺香的!开心一刻2023年元旦,我妈又开始了对我的念叨妈:你到底想多少岁结婚我:60妈:60,你想找个多大的我:找个55的啊,她55我60,结婚都有退休金
-
世界头条:琼中今年将打造千亩有机茶示范基地原标题:琼中今年将打造千亩有机茶示范基地(记者邓钰通讯员王丽娟胡淇玮)琼中黎族苗族自治县今年将打造1000亩有机茶
-
每日热点:京东白条贷款逾期6个月延迟还款会不会上征信网贷逾期一般会上征信,有些借贷机构在用户逾期后一天后就会上报给征信机构,而有些借贷机构则是会在几天后上报给征信机构,因为有些借贷机构可
-
热资讯!l201异形鱼_l2011、三星L201隶属于三星知名的蓝调系列相机,三星L201不仅配备了千万像素级别的CCD元件及防抖功能,还配有手动调焦
-
每日速看!今年“五一”昆明高星酒店预订量同比增长20倍飞猪旅行4月19日发布了《2023年“五一”出游风向标》。距离“五一”假期越来越近,截至4月19日,国内机票、酒店、景区门票、跟团游的预订量均
X 关闭
X 关闭