springboot+mybatis多数据源切换

易兴      2022-02-17     735

关键词:

由于公司业务划分了多个数据库,开发一个项目会同时调用多个库,经过学习我们采用了注解+aop的方式实现的

1.首先定义一个注解类

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface TargetDataSource {
	String value();//此处接收的是数据源的名称
}

2.然后建一个配置类,这个在项目启动时会加载数据源,一开始采用了HikariCP,查资料说是最快性能最好的,然后又发现了阿里的druid,这个功能比较全面,而且性能也还可以,最主要他还有监控功能,具体实现看如下代码

 

package com.example.demo.datasource;

import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.support.http.StatViewServlet;
import com.alibaba.druid.support.http.WebStatFilter;
import com.example.demo.datasource.DynamicDataSource;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import lombok.extern.slf4j.Slf4j;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.transaction.PlatformTransactionManager;
import org.w3c.dom.NodeList;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;

import javax.servlet.annotation.WebInitParam;
import javax.servlet.annotation.WebServlet;
import javax.sql.DataSource;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.io.File;
import com.alibaba.druid.support.http.StatViewServlet;
/**
 * Author:   wangchao
 * Version:
 * Date:     2017/9/11
 * Description:数据源配置
 * Modification  History:
 * Date         	Author        		Version        	Description
 * --------------------------------------------------------------
 * Why & What is modified:
 */

@Configuration
@EnableScheduling
public class DataSourceConfig {

	/*@Autowired
	private DBProperties properties;*/
	@Value("${datasource.filePath}")
	private String filePath;//数据源配置

	@Bean(name = "dataSource")
	public DataSource dataSource() {
		//按照目标数据源名称和目标数据源对象的映射存放在Map中
		Map<Object, Object> targetDataSources = new HashMap<>();
		//查找xml数据连接字符串
		targetDataSources=getdataMap(filePath);
		//动态获取DBProperties类申明的属性
		/*Field[] fields=properties.getClass().getDeclaredFields();
		for(int i=0;i<fields.length;i++)
		{
			targetDataSources.put(fields[i].getName(), getFieldValueByName(fields[i].getName(),properties));
		}*/
		//采用是想AbstractRoutingDataSource的对象包装多数据源
		DynamicDataSource dataSource = new DynamicDataSource();
		dataSource.setTargetDataSources(targetDataSources);
		//设置默认的数据源,当拿不到数据源时,使用此配置
		//dataSource.setDefaultTargetDataSource(properties.getUzaiTravel());
		return dataSource;
	}

	@Bean
	public PlatformTransactionManager txManager() {
		return new DataSourceTransactionManager(dataSource());
	}

	/**
	*获取数据源集合
	*/

	private Map<Object, Object> getdataMap(String fiePath)
	{

		try {
			Map<Object, Object> targetDataSources = new HashMap<>();
			File xmlFile = new File(fiePath);

			DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();

			DocumentBuilder builder = builderFactory.newDocumentBuilder();

			Document doc = builder.parse(xmlFile);

			doc.getDocumentElement().normalize();

			System.out.println("Root element: " + doc.getDocumentElement().getNodeName());

			NodeList nList = doc.getElementsByTagName("db");
			for(int i = 0 ; i<nList.getLength();i++) {

				Node node = nList.item(i);
				Element ele = (Element)node;

				/*HikariConfig config = new HikariConfig();
				config.setDriverClassName(ele.getElementsByTagName("driver-class").item(0).getTextContent());
				config.setJdbcUrl(ele.getElementsByTagName("jdbc-url").item(0).getTextContent());
				config.setUsername(ele.getElementsByTagName("username").item(0).getTextContent());
				config.setPassword(ele.getElementsByTagName("password").item(0).getTextContent());
				//config.addDataSourceProperty("password", ele.getElementsByTagName("password").item(0).getTextContent());
				HikariDataSource dataSource = new HikariDataSource(config);*/


				DruidDataSource dataSource = new DruidDataSource();
				dataSource.setDriverClassName(ele.getElementsByTagName("driver-class").item(0).getTextContent());
				dataSource.setUsername(ele.getElementsByTagName("username").item(0).getTextContent());
				dataSource.setPassword(ele.getElementsByTagName("password").item(0).getTextContent());
				dataSource.setUrl(ele.getElementsByTagName("jdbc-url").item(0).getTextContent());
				dataSource.setInitialSize(5);
				dataSource.setMinIdle(1);
				dataSource.setMaxActive(10);// 启用监控统计功能
				dataSource.setFilters("stat");//设置是否显示sql语句
				targetDataSources.put(ele.getElementsByTagName("databasename").item(0).getTextContent(), dataSource);
			}
			return targetDataSources;
		}
		catch (Exception ex)
		{
			return null;
		}

	}
    //访问的ip
	@Value("${druid.IP}")
	private String IP;
    //登录名
	@Value("${druid.druidLgoinName}")
	private String druidLgoinName;
    //密码
	@Value("${druid.druidLgoinPassword}")
	private String druidLgoinPassword;

	@Bean
	public ServletRegistrationBean DruidStatViewServle() {
		//org.springframework.boot.context.embedded.ServletRegistrationBean提供类的进行注册.
		ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(new StatViewServlet(), "/druid/*");
		//添加初始化参数:initParams

		//白名单:
		servletRegistrationBean.addInitParameter("allow",IP);
		//IP黑名单 (存在共同时,deny优先于allow) : 如果满足deny的话提示:Sorry, you are not permitted to view this page.
		// servletRegistrationBean.addInitParameter("deny", "192.168.1.73");
		//登录查看信息的账号密码.
		servletRegistrationBean.addInitParameter("loginUsername",druidLgoinName);
		servletRegistrationBean.addInitParameter("loginPassword",druidLgoinPassword);
		//是否能够重置数据.
		servletRegistrationBean.addInitParameter("resetEnable","false");
		return servletRegistrationBean;
	}

	/**

	 * 注册一个:filterRegistrationBean

	 * @return

    */
	@Bean
	public FilterRegistrationBean druidStatFilter2(){
		FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(new WebStatFilter());
		//添加过滤规则.
		filterRegistrationBean.addUrlPatterns("/*");
		//添加不需要忽略的格式信息.
		filterRegistrationBean.addInitParameter("exclusions","*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*");
		return filterRegistrationBean;
	}

}

3.动态数据源,从之前已加载的数据源中选取,DynamicDataSource和DynamicDataSourceHolder配合使用

public class DynamicDataSource extends AbstractRoutingDataSource{
	//数据源路由,此方用于产生要选取的数据源逻辑名称
	@Override
	protected Object determineCurrentLookupKey() {
		//从共享线程中获取数据源名称
		return DynamicDataSourceHolder.getDataSource();
	}
}
public class DynamicDataSourceHolder {
	/**
	 * 本地线程共享对象
	 */
	private static final ThreadLocal<String> THREAD_LOCAL = new ThreadLocal<>();

	public static void putDataSource(String name) {
		THREAD_LOCAL.set(name);
	}

	public static String getDataSource() {
		return THREAD_LOCAL.get();
	}

	public static void removeDataSource() {
		THREAD_LOCAL.remove();
	}
}

5.就是使用aop,在dao层切换数据源

@Component
@Aspect
public class DataSourceAspect {
	//切换放在mapper接口的方法上,所以这里要配置AOP切面的切入点
	@Pointcut("execution( * com.example.demo.dao.*.*(..))")
	public void dataSourcePointCut() {
	}

	@Before("dataSourcePointCut()")
	public void before(JoinPoint joinPoint) {
		Object target = joinPoint.getTarget();
		String method = joinPoint.getSignature().getName();
		Class<?>[] clazz = target.getClass().getInterfaces();
		Class<?>[] parameterTypes = ((MethodSignature) joinPoint.getSignature()).getMethod().getParameterTypes();
		try {
			Method m = clazz[0].getMethod(method, parameterTypes);
			//如果方法上存在切换数据源的注解,则根据注解内容进行数据源切换
			if (m != null && m.isAnnotationPresent(TargetDataSource.class)) {
				TargetDataSource data = m.getAnnotation(TargetDataSource.class);
				String dataSourceName = data.value();
				DynamicDataSourceHolder.putDataSource(dataSourceName);

			} else {

			}
		} catch (Exception e) {

		}
	}

	//执行完切面后,将线程共享中的数据源名称清空
	@After("dataSourcePointCut()")
	public void after(JoinPoint joinPoint){
		DynamicDataSourceHolder.removeDataSource();
	}
}

  

 数据连接都配置在xml里面

 xml路径在配置文件里面配置,这样适用读写分离和多个不同的数据源,而且多个项目可以共用这一个配置

 

 最后引用注解,需要注意的是注解的数据库名称和xml里面databasename节点是一一对应的,可以随便自定义,比如读写是一个数据库名字,这时候就可以定义成pringtest_r表示读库

 

 

 至此多数据源就配置完成,至于阿里的druid下次再分享,代码都贴出来,如果大家感觉还有哪些不足的地方,欢迎指正。

  

springboot+mybatis数据源动态切换与加载

...ataSourceUtil类保存projectId与dataSourceId的对应关系  springboot启动时的配置类配置默认datasource  可以看到,已经实现了数据源的动态切换 查看详情

springboot+mybatis+druid配置多数据源(mysql+postgre)

springboot+mybatis+Druid配置多数据源(mysql+postgre)引入pom依赖设置application多数据源config配置db1config配置(主数据库配置)db2config配置(其他数据库)事务处理mapper层springboot+mybatis+Druid配置多数据源(mysql+postgre)参考资料:第八章springboot+mybatis+... 查看详情

springboot-整合多数据源配置&abstractroutingdatasource详解,分析多数据源切换原理(代码片段)

简介主要介绍两种整合方式,分别是springboot+mybatis使用分包方式整合,和springboot+druid+mybatisplus使用注解方式整合。一、表结构在本地新建两个数据库,名称分别为db1和db2,新建一张user表,表结构如下... 查看详情

springboot:mybatis配置多数据源

第一个数据源:importorg.springframework.boot.context.properties.ConfigurationProperties;importorg.springframework.stereotype.Component;@Component@ConfigurationProperties(prefix="spring.datasource")publicclas 查看详情

springboot集成mybatis动态多数据源后,mybatisplus的ipage失效的问题解决方案

背景之前做数据抽取的时候,搭了一个mybatis动态数据源切换的架子。方便他们写抽取的代码。今天同事问我,架子里面的mybatisplus的IPage失效了是什么问题。想了一下,应该是写动态数据源的时候,我自定义的mybatis的配置覆盖了... 查看详情

springboot入门之基于druid配置mybatis多数据源

...处理,这篇了解下使用基于基于Druid配置Mybatis多数据源。SpringBoot默认配置数据库连接信息时只需设置url等属性信息就可以了,SpringBoot就会基于约定根据配置信息实例化对象,但是一般大型的项目都是有多个子系统或者多个数据... 查看详情

springboot+mybatis+pagehelper配置多数据源

 前言:本文为springboot结合mybatis配置多数据源,在项目当中很多情况是使用主从数据源来读写分离,还有就是操作多库,本文介绍如何一个项目同时使用2个数据源。也希望大家带着思考去学习!博主是最近才学的配置写成博... 查看详情

springboot+mybatis链接多数据库

多数据库:https://blog.csdn.net/weixin_44076260/article/details/111912460多数据库:https://cloud.tencent.com/developer/article/1676947https://springboot.io/t/topic/2782https://m.imooc.com/article/ 查看详情

springboot2.x:mybatis多数据源配置

前言MyBatis多数据源配置,最近在项目建设中,需要在原有系统上扩展一个新的业务模块,特意将数据库分库,以便减少复杂度。本文直接以简单的代码示例,如何对MyBatis多数据源配置。准备创建数据库db_testSETNAMESutf8mb4;SETFOREIGN_... 查看详情

第九章springboot+mybatis+多数据源(aop实现)

在第八章springboot+mybatis+多数据源代码的基础上,做两点修改1、ShopDaopackagecom.xxx.firstboot.dao;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.stereotype.Repository;importcom.xxx. 查看详情

springboot+mybatis+druid+pagehelper实现多数据源并分页

前言本篇文章主要讲述的是SpringBoot整合Mybatis、Druid和PageHelper并实现多数据源和分页。其中SpringBoot整合Mybatis这块,在之前的的一篇文章中已经讲述了,这里就不过多说明了。重点是讲述在多数据源下的如何配置使用Druid和PageHelpe... 查看详情

springboot2+druid+mybatis多数据源配置(代码片段)

...案。读写分离会使用多数据源的使用。下面记录如何搭建SpringBoot2+Druid+Mybatis 多数据源配置以及在使用过程遇到的问题。一、先从pom.xml入手(使用springboot2的版本)<parent><groupId>org.springframework.boot&l 查看详情

第九章springboot+mybatis+多数据源(aop实现)(转载)

本编博客转发自:http://www.cnblogs.com/java-zhao/p/5415896.html 1、ShopDaopackagecom.xxx.firstboot.dao;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.stereotype.Rep 查看详情

第八章springboot+mybatis+多数据源

在实际开发中,我们一个项目可能会用到多个数据库,通常一个数据库对应一个数据源。代码结构:简要原理:1)DatabaseType列出所有的数据源的key---key2)DatabaseContextHolder是一个线程安全的DatabaseType容器,并提供了向其中设置和... 查看详情

springboot+mybatis链接多数据库

...据库:https://cloud.tencent.com/developer/article/1676947https://springboot.io/t/topic/2782https://m.imooc.com/article/details?article_id=310840https://blog.csdn.net/u013490585/article/details/108497108 查看详情

springboot-mybatis多数据源以及踩坑之旅

首先,springboot项目结构如下springboot配置文件内容如下 动态数据源的配置类如下(必须保证能被ComponentScan扫描到):1packagecom.letzgo.config;23importcom.alibaba.druid.pool.DruidDataSource;4importorg.apache.ibatis.session.SqlSessionFacto 查看详情

springboot实现多数据源整合mybatis版(代码片段)

前言本篇博客只讲如何从零到壹地再SpringBoot项目中实现多数据源配置,不谈源码(后续上SpringBoot自动配置等源码)。本篇博客内容基于SpringBootVersion2.5.1。背景一个项目中需要连接多个数据源,我们需要在业务层... 查看详情

springboot实现多数据源整合mybatis版(代码片段)

前言本篇博客只讲如何从零到壹地再SpringBoot项目中实现多数据源配置,不谈源码(后续上SpringBoot自动配置等源码)。本篇博客内容基于SpringBootVersion2.5.1。背景一个项目中需要连接多个数据源,我们需要在业务层... 查看详情