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
springboot 多数据源mybatis的两种整合方法相关推荐
- Destoon数据库配置文件在哪_Mybatis 系列 2:Mybatis 的两种配置文件
Mybatis 的配置文件,有两种: 一.Mybatis 全局配置文件(主配置文件): 起名:不固定,但一般起名要见名知意 --> mybatis-config.xml 路径:classpath ...
- springboot 多数据源配置的几种方式
springboot多数据源配置的三种方式 application.yml配置 1.@Ds("配置数据源名称") 引入依赖 <dependency> <group ...
- SpringBoot文件访问映射的两种实现方式
SpringBoot文件访问映射的两种实现方式 业务需求:通过SpringBoot访问服务器(磁盘内)的所有文件,用于正常项目中上传图片(文件)的访问. 图片路径:E://images/upload/ ...
- 通过ID查询一个用户的两种开发方法
通过ID查询一个用户的两种开发方法 数据库建表sql语句如下:https://github.com/beyondyanyu/Sayingyy/blob/master/JDBC2-数据库sql建表语句 ...
- 链表反转的两种实现方法,后一种击败了100%的用户!
作者 | 王磊 来源 | Java中文社群(ID:javacn666) 转载请联系授权(微信ID:GG_Stone) 链表反转是一道很基础但又非常热门的算法面试题,它也在<剑指Offer> ...
- 数据递归查询的两种实现方法
在业务代码当中,经常需要递归查询有等级结构的数据. 现在是两种实现方法. 第一种在oracle当中,使用 start with connect by prior 递归查询 附递归查询用法 https: ...
- 快速排序的两种实现方法(c语言版本)
经过调研发现,对任意无序整数数组,快速排序有两种实现方法,这里简单阐述下思路: 思路一:随意选择一个基准元,一般选择数组的起始元或末尾元,Weiss这本书上特意搞了个算法来选择基准元,--,总之就是基 ...
- R语言生存分析COX回归分析实战:两种治疗方法发生肾功能损害的情况
R语言生存分析COX回归分析实战:两种治疗方法发生肾功能损害的情况 目录
- mysql workbench kernelbase.dll_电脑出现kernelbase.dll错误的两种解决方法
KernelBase.dll是Windows操作系统的重要文件,它为各种应用程序提供服务.如果电脑提示kernelbase.dll错误,这该怎么处理?大家可以用电脑自带的防火墙或者是第三方软件来进行故 ...
最新文章
- 下拉列表插件bootstrap-select使用实例
- 牛逼了!一周内咸鱼疯转 3.6w 次,最终被所有大厂封杀!
- 成功解决pywintypes.com_error: (-2147221005, '无效的类字符串', None, None)
- 元学习Meta Learning/Learning to learn
- window当mac用,VirtualBox虚拟机安装os系统
- mysql sql语句 datediff_SQL语句中DateDiff函数说明
- Ubuntu Mysql安装配置
- - 麦田守望者 -读后感
- linux转录组kegg注释,KEGG pathway 注释整理
- 【算法竞赛学习笔记】平衡树专题——替罪羊树
- [编程题]山寨金闪闪 (面试题)
- MATLAB ~的用法
- Hexo + yilia 主题实现文章目录
- 服务端判断客户端是移动端浏览器还是PC端浏览器
- 本周最新文献速递20211205
- c语言设计计算器的意义,C语言结课设计之计算器功能
- AVI音视频封装格式学习(三)——AVI 数据结构解析
- db2 replace函数的用法_48R软件数据的基本处理之删除重复数据(duplicated()、unique()、distinct()函数)...
- 获取svg和svg内容的方法
- 从零开始的2D游戏开发 —— 像素方块生成器
热门文章
- java opencsv_用opencsv文件读写CSV文件
- python中定制类_python定制类__str__(实例详解)
- 数据结构——最小生成树之prime算法(与最短路径之迪杰斯特拉算法很像)
- 7-6 区间覆盖 (10 分)(思路+详解)Come 宝!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
- [SpringSecurity]web权限方案_用户认证_查询数据库完成认证
- Java中各种整形类型可以表示的范围
- [Java网络编程基础]InetAddress的使用
- C++ class实现顺序栈(完整代码)
- C++vector容器-预留空间
- 2018 蓝桥杯省赛 A 组模拟赛(一)数列求值+推导