springboot 多数据源mybatis的两种整合方法

转载自  springboot-mybatis多数据源的两种整合方法

简介:
随着并发量的不断增加,显然单个数据库已经承受不了高并发带来的压力。一个项目使用多个数据库(无论是主从复制- - 读写分离还是分布式数据库结构)的重要性变得越来越明显。传统项目中(个人对传统项目的理解就是所有的业务模块都在一个tomcat中完成,多个相同的tomcat集群也可认为是传统项目)整合多数据源有两种方法:分包和AOP。

版本:
springboot:1.5.9.RELEASE 
mariadb:5.7

一、分包方式实现

1、在application.properties中配置两个数据库:

## test1 database
spring.datasource.test1.url=jdbc:mysql://localhost:3307/multipledatasource1?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&useSSL=false
spring.datasource.test1.username=root
spring.datasource.test1.password=root
spring.datasource.test1.driver-class-name=com.mysql.cj.jdbc.Driver
## test2 database
spring.datasource.test2.url=jdbc:mysql://localhost:3307/multipledatasource2?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&useSSL=false
spring.datasource.test2.username=root
spring.datasource.test2.password=root
spring.datasource.test2.driver-class-name=com.mysql.cj.jdbc.Driver


2、建立连个数据源的配置文件

springbooot中的参数可以参考上一篇博客(不定期更新中):https://blog.csdn.net/tuesdayma/article/details/81029539

第一个配置文件:

//表示这个类为一个配置类
@Configuration
// 配置mybatis的接口类放的地方
@MapperScan(basePackages = "com.mzd.multipledatasources.mapper.test01", sqlSessionFactoryRef = "test1SqlSessionFactory")
public class DataSourceConfig1 {// 将这个对象放入Spring容器中@Bean(name = "test1DataSource")// 表示这个数据源是默认数据源@Primary// 读取application.properties中的配置参数映射成为一个对象// prefix表示参数的前缀@ConfigurationProperties(prefix = "spring.datasource.test1")public DataSource getDateSource1() {return DataSourceBuilder.create().build();}@Bean(name = "test1SqlSessionFactory")// 表示这个数据源是默认数据源@Primary// @Qualifier表示查找Spring容器中名字为test1DataSource的对象public SqlSessionFactory test1SqlSessionFactory(@Qualifier("test1DataSource") DataSource datasource)throws Exception {SqlSessionFactoryBean bean = new SqlSessionFactoryBean();bean.setDataSource(datasource);bean.setMapperLocations(// 设置mybatis的xml所在位置new PathMatchingResourcePatternResolver().getResources("classpath*:mapping/test01/*.xml"));return bean.getObject();}@Bean("test1SqlSessionTemplate")// 表示这个数据源是默认数据源@Primarypublic SqlSessionTemplate test1sqlsessiontemplate(@Qualifier("test1SqlSessionFactory") SqlSessionFactory sessionfactory) {return new SqlSessionTemplate(sessionfactory);}
}

第二个配置文件:

@Configuration
@MapperScan(basePackages = "com.mzd.multipledatasources.mapper.test02", sqlSessionFactoryRef = "test2SqlSessionFactory")
public class DataSourceConfig2 {@Bean(name = "test2DataSource")@ConfigurationProperties(prefix = "spring.datasource.test2")public DataSource getDateSource2() {return DataSourceBuilder.create().build();}@Bean(name = "test2SqlSessionFactory")public SqlSessionFactory test2SqlSessionFactory(@Qualifier("test2DataSource") DataSource datasource)throws Exception {SqlSessionFactoryBean bean = new SqlSessionFactoryBean();bean.setDataSource(datasource);bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:mapping/test02/*.xml"));return bean.getObject();}@Bean("test2SqlSessionTemplate")public SqlSessionTemplate test2sqlsessiontemplate(@Qualifier("test2SqlSessionFactory") SqlSessionFactory sessionfactory) {return new SqlSessionTemplate(sessionfactory);}
}

注意:

1、@Primary这个注解必须要加,因为不加的话spring将分不清楚那个为主数据源(默认数据源)

2、mapper的接口、xml形式以及dao层都需要两个分开,目录如图: 
 
3、bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(“XXXX”));mapper的xml形式文件位置必须要配置,不然将报错:no statement (这种错误也可能是mapper的xml中,namespace与项目的路径不一致导致的,具体看情况吧,注意一下就行,问题不大的)

4、在service层中根据不同的业务注入不同的dao层。

5、如果是主从复制- -读写分离:比如test01中负责增删改,test02中负责查询。但是需要注意的是负责增删改的数据库必须是主库(master)

6、如果是分布式结构的话,不同模块操作各自的数据库就好,test01包下全是test01业务,test02全是test02业务,但是如果test01中掺杂着test02的编辑操作,这时候将会产生事务问题:即test01中的事务是没法控制test02的事务的,这个问题在之后的博客中会解决。

 

二、AOP实现

简介: 用这种方式实现多数据源的前提必须要清楚两个知识点:AOP原理和AbstractRoutingDataSource抽象类。

1、AOP:这个东西。。。不切当的说就是相当于拦截器,只要满足要求的都会被拦截过来,然后进行一些列的操作。具体需要自己去体会。。。

2、AbstractRoutingDataSource:这个类是实现多数据源的关键,他的作用就是动态切换数据源,实质:有多少个数据源就存多少个数据源在targetDataSources(是AbstractRoutingDataSource的一个map类型的属性,其中value为每个数据源,key表示每个数据源的名字)这个属性中,然后根据determineCurrentLookupKey()这个方法获取当前数据源在map中的key值,然后determineTargetDataSource()方法中动态获取当前数据源,如果当前数据源不存并且默认数据源也不存在就抛出异常。

public abstract class AbstractRoutingDataSource extends AbstractDataSource implements InitializingBean {//多数据源map集合private Map<Object, Object> targetDataSources;//默认数据源private Object defaultTargetDataSource;//其实就是targetDataSources,后面的afterPropertiesSet()方法会将targetDataSources赋值给resolvedDataSourcesprivate Map<Object, DataSource> resolvedDataSources;private DataSource resolvedDefaultDataSource;public void setTargetDataSources(Map<Object, Object> targetDataSources) {this.targetDataSources = targetDataSources;}protected DataSource determineTargetDataSource() {Assert.notNull(this.resolvedDataSources, "DataSource router not initialized");Object lookupKey = this.determineCurrentLookupKey();DataSource dataSource = (DataSource)this.resolvedDataSources.get(lookupKey);if (dataSource == null && (this.lenientFallback || lookupKey == null)) {dataSource = this.resolvedDefaultDataSource;}if (dataSource == null) {throw new IllegalStateException("Cannot determine target DataSource for lookup key [" + lookupKey + "]");} else {return dataSource;}}protected abstract Object determineCurrentLookupKey();
}


具体实现:

1、定义一个动态数据源:继承AbstractRoutingDataSource 抽象类,并重写determineCurrentLookupKey()方法

public class DynamicDataSource extends AbstractRoutingDataSource {@Overrideprotected Object determineCurrentLookupKey() {DataSourceType.DataBaseType dataBaseType = DataSourceType.getDataBaseType();return dataBaseType;}}

2、创建一个切换数据源类型的类: ThreadLocal这个知识点可以参考我的博客:https://blog.csdn.net/tuesdayma/article/details/74841657 就是为了线程的安全性,每个线程之间不会相互影响。

public class DataSourceType {public enum DataBaseType {TEST01, TEST02}// 使用ThreadLocal保证线程安全private static final ThreadLocal<DataBaseType> TYPE = new ThreadLocal<DataBaseType>();// 往当前线程里设置数据源类型public static void setDataBaseType(DataBaseType dataBaseType) {if (dataBaseType == null) {throw new NullPointerException();}System.err.println("[将当前数据源改为]:" + dataBaseType);TYPE.set(dataBaseType);}// 获取数据源类型public static DataBaseType getDataBaseType() {DataBaseType dataBaseType = TYPE.get() == null ? DataBaseType.TEST01 : TYPE.get();System.err.println("[获取当前数据源的类型为]:" + dataBaseType);return dataBaseType;}// 清空数据类型public static void clearDataBaseType() {TYPE.remove();}}


3、定义多个数据源:怎么定义就不多说了,和方法一是一样的,主要是将定义好的多个数据源放在动态数据源中。

@Configuration
@MapperScan(basePackages = "com.mzd.multipledatasources.mapper", sqlSessionFactoryRef = "SqlSessionFactory")
public class DataSourceConfig {@Primary@Bean(name = "test1DataSource")@ConfigurationProperties(prefix = "spring.datasource.test1")public DataSource getDateSource1() {return DataSourceBuilder.create().build();}@Bean(name = "test2DataSource")@ConfigurationProperties(prefix = "spring.datasource.test2")public DataSource getDateSource2() {return DataSourceBuilder.create().build();}@Bean(name = "dynamicDataSource")public DynamicDataSource DataSource(@Qualifier("test1DataSource") DataSource test1DataSource,@Qualifier("test2DataSource") DataSource test2DataSource) {Map<Object, Object> targetDataSource = new HashMap<>();targetDataSource.put(DataSourceType.DataBaseType.TEST01, test1DataSource);targetDataSource.put(DataSourceType.DataBaseType.TEST02, test2DataSource);DynamicDataSource dataSource = new DynamicDataSource();dataSource.setTargetDataSources(targetDataSource);dataSource.setDefaultTargetDataSource(test1DataSource);return dataSource;}@Bean(name = "SqlSessionFactory")public SqlSessionFactory test1SqlSessionFactory(@Qualifier("dynamicDataSource") DataSource dynamicDataSource)throws Exception {SqlSessionFactoryBean bean = new SqlSessionFactoryBean();bean.setDataSource(dynamicDataSource);bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:mapping/*.xml"));return bean.getObject();}
}


4、定义AOP:就是不同业务切换不同数据库的入口。如果觉得execution太长不愿意写,就可以定义一个注解来实现。可参考于我的博客:https://blog.csdn.net/tuesdayma/article/details/79704238

@Aspect
@Component
public class DataSourceAop {@Before("execution(* com.mzd.multipledatasources.service..*.test01*(..))")public void setDataSource2test01() {System.err.println("test01业务");DataSourceType.setDataBaseType(DataBaseType.TEST01);}@Before("execution(* com.mzd.multipledatasources.service..*.test02*(..))")public void setDataSource2test02() {System.err.println("test02业务");DataSourceType.setDataBaseType(DataBaseType.TEST02);}
}


整体目录如图: 


源代码:https://github.com/mzd123/springboot-multipledatasources/tree/master
 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/322802.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

P1373-小a和uim之大逃离【dp】

正题 题目大意 n∗mn*mn∗m的矩阵有不同的权值&#xff0c;每次只可以往下走或往右走。要求走奇数次&#xff0c;要求偶数次经过的点和奇数次经过的点同余KKK 解题思路 设fi,j,k,0/1f_{i,j,k,0/1}fi,j,k,0/1​表示在第iii行jjj列&#xff0c;两个数只差为kkk&#xff0c;是奇数…

项目实战+感慨

已经好久没写博客了&#xff0c;自从接触了项目以后&#xff0c;发现很难再挤出时间来写博客&#xff0c;大部分时间都是在关注项目的进展&#xff0c;以及查阅相关的资料。我想这也是很多程序员入职以后就很少写博客的原因。 前言 最近一段时间我接触了很多前端的东西&#x…

解决mybatis generator无法覆盖XML

转载自 解决mybatis generator无法覆盖XML 今天发现mybatis generator maven plugin在重复生成的时候xml文件只会merge&#xff0c;不会覆盖。 明明在pom.xml中配置了如下&#xff1a; <configuration><configurationFile>src/main/resources/mybatis/generato…

laravel构造器的CURD

这里主要记一下关于用laravel写相关接口的一些知识点&#xff0c;方便以后查阅&#xff0c;基本都是Controller里面的一些操作 查询数据&#xff1a; // 查询数据$query DB::table(think_data)->where([ [status,>,500]])->get();插入数据&#xff1a; // 新增单条数…

亿些模板【图论】

文章目录前言图论模板最短路-Floyd最短路-SPFA最短路-Dij堆优化最小生成树-Kruskal最小生成树-Prim堆优化最大匹配-匈牙利算法tarjan求割点tarjan缩点LCA-树上倍增LCA-tarjan并查集优化网络流-最大流dinic网络流-最小费用最大流负环虚树2-SATKruskal重构树静态仙人掌前言 因为…

中间件中渲染Razor视图

前言 上一篇文章《ASP.NET Core 奇技淫巧&#xff08;1&#xff09;&#xff1a;中间件实现服务端静态化缓存》中介绍了中间件的使用方法、以及使用中间件实现服务端静态化缓存的功能。本系列文章取名“奇技淫巧”不是没道理的&#xff0c;因为这写技巧都是我最近在做的公司实际…

解决idea启动项目报错:Unable to open debugger port(127.0.0.1:60157):java.net.SocketExceptionsocket closed

转载自 解决idea启动项目报错:Unable to open debugger port(127.0.0.1:60157):java.net.SocketException"socket closed 1.问题描述: 工作当中免不了要重启服务,debug模式下偶尔启动项目,却启动失败报错: Unable to open debugger port (127.0.0.1:60157): java.net.S…

python3如何连接mysql数据库

原本连接数据库用的是mysql_python&#xff0c;但发现无论怎么安装都装不上去&#xff0c;pip和手动安装都失败了 后来才知道用pymysql才是3版本以后最好适用的 命令 pip3 install PyMySQL连接数据库 import pymysql conn pymysql.connect(localhost, root, 123456)

亿些模板【数据结构】

文章目录前言数据结构模板并查集带权并查集堆hash表RMQ线段树树状数组树状数组区间修改版分块主席树有旋TreapSplay替罪羊树文艺平衡树-Splay点分治树链剖分可持久化并查集LCT左偏树前言 因为老是懒得打模板的时候老是扣不到自己的标(因为之前的都打得太丑了)&#xff0c;所以…

下一个计划 : .NET/.NET Core应用性能管理

前言 最近几个月一直在研究开源的APM和监控方案&#xff0c;并对比使用了Zipkin,CAT,Sky-walking,PinPoint(仅对比,未实际部署),Elastic APM,TICK Stack,Prometheus等开源产品&#xff0c;其中不乏功能强大的监控和追踪系统&#xff0c;但它们都对.NET/.NET Core没有支持或支持…

详解proxy_pass、upstream与resolver

转载自 详解proxy_pass、upstream与resolver 应用场景 这里列举几个应用场景&#xff0c;下文会针对这几个场景并结合代码进行分析。 &#xff08;1&#xff09;proxy_pass upstream upstream foo.example.com {server 127.0.0.1:8001;}server {listen 80;server_na…

如何用python将srt文件转化成数组形式

原srt文件 0 00:00:00,150 --> 00:00:11,430 Fighting this pandemic needs political commitment and commitment at the highest level possible and the Presidents commitment.1 00:00:11,431 --> 00:00:16,020 you have what it is in it and the would it have se…

架构演化:云原生时代开启之系列一演化篇

信息技术从出现伊始到渐成主流&#xff0c;其趋势经历了软件、开源和云三个阶段&#xff1a; 软件改变世界。纵观人类社会漫长的发展历程&#xff0c;农耕时代、工业时代与信息时代可谓是三个明显分水岭&#xff0c;每个时代人类涉及的领域范畴均喷井式增长。作为信息时代最重要…

mysql出现unblock with 'mysqladmin flush-hosts'

转载自 mysql出现unblock with mysqladmin flush-hosts 朋友发来消息&#xff0c;说一个系统应用登录的时候提示连接超时&#xff0c;让帮忙处理一下。 问他应用和数据库是否都正常&#xff0c;回复说数据库好像没有问题&#xff0c;但是应用日志报无法连接数据库。 数据库版…

laravel部署到服务器显示Permission denied

解决这个问题可以先cd到项目根目录中 依次输入以下两个命令 find storage -type d -exec chmod 777 {} \;find storage -type f -exec chmod 777 {} \;不报错就证明成功了

亿些模板【数论数学】

文章目录前言数论数学模板GCDexgcd快速幂线性推逆元线性推逆元(非连续)逆元求组合数矩阵乘法线性筛素数-埃氏筛线性筛素数-线性筛线性筛欧拉-埃氏筛线性求欧拉龟速乘FFTNTT分治FFT多项式求逆扩展中国剩余定理Lucas定理高斯消元BSGS拉格朗日插值二次剩余线性基杜教筛前言 因为老…

基于OIDC(OpenID Connect)的SSO

在[认证授权]系列博客中&#xff0c;分别对OAuth2和OIDC在理论概念方面进行了解释说明&#xff0c;其间虽然我有写过一个完整的示例&#xff08;https://github.com/linianhui/oidc.example&#xff09;&#xff0c;但是却没有在实践方面做出过解释。在这里新开一个系列博客&am…

Consul集群搭建

转载自 Consul集群搭建 概述 作为服务发现的几种产品&#xff0c;比较可以查看这里。Consul官方也提供了几种产品之间的比较&#xff0c;点击查看。 服务发现产品 Consul有很多组件&#xff0c;但总体来说&#xff0c;它是一个发现和配置服务工具&#xff0c;特性&#xff…

js遍历对象的key和value

如果想要得到数组的键值对&#xff0c;可以用以下方法 object {"name":"kejin","age":"18"}for(var index in object){console.log(index);console.log(object[index]); }

亿些模板【字符串+其他】

文章目录前言其他快读快输卡常DataMakerDataMakerDataMaker字符串模板KMP字符串hashTrie最小表示法ManacherAC自动机SASAM广义SAMPAM其他模板凸包模拟退火前言 因为老是懒得打模板的时候老是扣不到自己的标(因为之前的都打得太丑了)&#xff0c;所以导致我十分的不爽。便打算开…