springboot-mybatis多数据源的两种整合方法[通俗易懂]

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

大家好,又见面了,我是你们的朋友全栈君。

#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")
	// 表示这个数据源是默认数据源
	@Primary
	public 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层都需要两个分开,目录如图:
springboot-mybatis多数据源的两种整合方法[通俗易懂]
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赋值给resolvedDataSources
    private 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 {

	@Override
	protected 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

微信公众号关注: 965净化者

在这里插入图片描述

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/152627.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • JS数组合并(5种)

    JS数组合并(5种)前言项目过程中,经常会遇到JS数组合并的情况,时常为这个纠结。这里整理一下。简单而实用的for最容易想到的莫过于for了。会变更原数组,当然也可以写成生成新数组的形式。letarr=[1,2]letarr2=[3,4]for(letiinarr2){arr.push(arr2[i])}console.log(arr)//[1,2,3,4]arr.concat(arr2)会生成新的数组。letarr=[1,2]let

    2022年6月30日
    23
  • python基础知识点(精心整理)_python编程基础知识

    python基础知识点(精心整理)_python编程基础知识在Python里,标识符有字母、数字、下划线组成。在Python中,所有标识符可以包括英文、数字以及下划线(_),但不能以数字开头。Python中的标识符是区分大小写的。以下划线开头的标识符是有特殊意义的。以单下划线开头_foo的代表不能直接访问的类属性,需通过类提供的接口进行访问,不能用fromxxximport*而导入;以双下划线开头的__foo代表类的私有成员;以双下划线开头和结尾的foo代表Python里特殊方法专用的标识,如init()代表类的构造函

    2022年10月8日
    2
  • fat文件系统中,文件的物理结构_磁盘的文件系统结构

    fat文件系统中,文件的物理结构_磁盘的文件系统结构在这个系类的开篇还是先说一下文件系统是什么吧。首先来介绍一下对u盘的格式化这个操作,格式化不是仅仅删除了所有文件,还为接下的来文件存储约定了一种存放格式,这种约定的文件存放格式就叫做文件系统。再用最通俗的说法简要介绍一下,磁盘如sd卡只能存放0和1这两种二进制状态序列,数字文件本质上也是一串0和1的序列。那么磁盘存储文件怎么存放呢?你说这个简单,把一个个的文件紧挨着排列在磁盘中不就可以了吗。那么,…

    2025年6月19日
    3
  • nginx中location的使用

    nginx中location的使用

    2021年8月3日
    67
  • imfill简介_im ill

    imfill简介_im illBW2=imfill(BW)这种格式将一张二值图像显示在屏幕上,允许用户使用鼠标在图像上点几个点,这几个点围成的区域即要填充的区域。要以这种交互方式操作,BW必须是一个二维的图像。用户可以通过按Backspace键或者Delete键来取消之前选择的区域;通过shift+鼠标左键单击或者鼠标右键单击或双击可以确定选择区域。[BW2,locations]=imfill(BW)

    2022年9月13日
    5
  • 不同浏览器中手动启用Flash Player「建议收藏」

    FlashPlayer用户在浏览网页有时会提示:“AdobeFlashPlayer已被屏蔽”等类似界面,当出现该情况时,那么我们如何手动启用FlashPlayer?我们将这个问题分为以下四种类型进行逐一介绍:谷歌Chrome浏览器1点击chrome浏览器右上角图标2在跳出的对话框中单击设置3在页面底部单击高级4在展开的页面中单击内容设置5单击Flash6打开Flash下方的先询问(推荐)开关7“刷新”视频页面。…

    2022年4月10日
    120

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号