摘要:springboot1.x到springboot2.0配置变化有一点变化,网上关于springboot2.0配置多数据源的资料也比较少,为了让大家配置多数据源从springboot1.x升级到springboot2.0少踩写坑,博主在此介绍用springboot2.0来进行动态数据源切换。(在博客的最后会给大家提供源码的下载地址)

一、引入依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <groupId>com.heshouyou</groupId>
<artifactId>dynamic-datasource</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging> <name>dynamic-datasource</name>
<description>dynamic-datasource project for Spring Boot</description> <parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.0.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent> <dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency> <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency> <dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency> <dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency> <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency> <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency> <dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
</dependencies> <build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

二、编辑application.yml

server.port=8080
#数据源配置(默认)
#useSSL=false MySQL在高版本需要指明是否进行SSL连接(不加第一次连接数据库会有警告信息)
spring.datasource.driver=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/test?useSSL=false
spring.datasource.username=root
spring.datasource.password=123456
#ds1,ds2 其他两个数据源
slave.datasource.names=ds1,ds2
#ds1
slave.datasource.ds1.driver=com.mysql.jdbc.Driver
slave.datasource.ds1.url=jdbc:mysql://localhost:3306/test1?useSSL=false
slave.datasource.ds1.username=root
slave.datasource.ds1.password=123456
#ds2
slave.datasource.ds2.driver=com.mysql.jdbc.Driver
slave.datasource.ds2.url=jdbc:mysql://localhost:3306/test2?useSSL=false
slave.datasource.ds2.username=root
slave.datasource.ds2.password=123456
#mapper.xml文件
mybatis.mapper-locations=classpath:mapper/*.xml
#实体类包
mybatis.type-aliases-package=datasource.springboot_datasource.entity

三、DataSource工具类

动态切换类

package com.chaoqi.springboot_datasource.config;

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

/**
* @Author caiChaoqi
* @Date 2018-06-23
* @Description 动态数据源
* AbstractRoutingDataSource(每执行一次数据库,动态获取DataSource)
*/
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DynamicDataSourceContextHolder.getDataSourceType();
}
}
动态数据源上下文管理
package com.chaoqi.springboot_datasource.config;

import org.apache.log4j.Logger;

import java.util.ArrayList;
import java.util.List; /**
* @Author caiChaoqi
* @Date 2018-06-23
* @Description 动态数据源上下文管理
*/
public class DynamicDataSourceContextHolder { private Logger logger = Logger.getLogger(DynamicDataSourceContextHolder.class);
//存放当前线程使用的数据源类型信息
private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();
//存放数据源id
public static List<String> dataSourceIds = new ArrayList<String>(); //设置数据源
public static void setDataSourceType(String dataSourceType) {
contextHolder.set(dataSourceType);
} //获取数据源
public static String getDataSourceType() {
return contextHolder.get();
} //清除数据源
public static void clearDataSourceType() {
contextHolder.remove();
} //判断当前数据源是否存在
public static boolean isContainsDataSource(String dataSourceId) {
return dataSourceIds.contains(dataSourceId);
}
}
初始化数据源和提供了执行动态切换数据源的工具类
package com.chaoqi.springboot_datasource.config;

import org.apache.log4j.Logger;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotationMetadata; import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map; /**
* @Author caiChaoqi
* @Date 2018-06-23
* @Description 注册动态数据源
* 初始化数据源和提供了执行动态切换数据源的工具类
* EnvironmentAware(获取配置文件配置的属性值)
*/ public class DynamicDataSourceRegister implements ImportBeanDefinitionRegistrar, EnvironmentAware { private Logger logger = Logger.getLogger(DynamicDataSourceRegister.class); //指定默认数据源(springboot2.0默认数据源是hikari如何想使用其他数据源可以自己配置)
private static final String DATASOURCE_TYPE_DEFAULT = "com.zaxxer.hikari.HikariDataSource";
//默认数据源
private DataSource defaultDataSource;
//用户自定义数据源
private Map<String, DataSource> slaveDataSources = new HashMap<>(); @Override
public void setEnvironment(Environment environment) {
initDefaultDataSource(environment);
initslaveDataSources(environment);
} private void initDefaultDataSource(Environment env) {
// 读取主数据源
Map<String, Object> dsMap = new HashMap<>();
dsMap.put("driver", env.getProperty("spring.datasource.driver"));
dsMap.put("url", env.getProperty("spring.datasource.url"));
dsMap.put("username", env.getProperty("spring.datasource.username"));
dsMap.put("password", env.getProperty("spring.datasource.password"));
defaultDataSource = buildDataSource(dsMap);
} private void initslaveDataSources(Environment env) {
// 读取配置文件获取更多数据源
String dsPrefixs = env.getProperty("slave.datasource.names");
for (String dsPrefix : dsPrefixs.split(",")) {
// 多个数据源
Map<String, Object> dsMap = new HashMap<>();
dsMap.put("driver", env.getProperty("slave.datasource." + dsPrefix + ".driver"));
dsMap.put("url", env.getProperty("slave.datasource." + dsPrefix + ".url"));
dsMap.put("username", env.getProperty("slave.datasource." + dsPrefix + ".username"));
dsMap.put("password", env.getProperty("slave.datasource." + dsPrefix + ".password"));
DataSource ds = buildDataSource(dsMap);
slaveDataSources.put(dsPrefix, ds);
}
} @Override
public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {
Map<Object, Object> targetDataSources = new HashMap<Object, Object>();
//添加默认数据源
targetDataSources.put("dataSource", this.defaultDataSource);
DynamicDataSourceContextHolder.dataSourceIds.add("dataSource");
//添加其他数据源
targetDataSources.putAll(slaveDataSources);
for (String key : slaveDataSources.keySet()) {
DynamicDataSourceContextHolder.dataSourceIds.add(key);
} //创建DynamicDataSource
GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
beanDefinition.setBeanClass(DynamicDataSource.class);
beanDefinition.setSynthetic(true);
MutablePropertyValues mpv = beanDefinition.getPropertyValues();
mpv.addPropertyValue("defaultTargetDataSource", defaultDataSource);
mpv.addPropertyValue("targetDataSources", targetDataSources);
//注册 - BeanDefinitionRegistry
beanDefinitionRegistry.registerBeanDefinition("dataSource", beanDefinition); logger.info("Dynamic DataSource Registry");
} public DataSource buildDataSource(Map<String, Object> dataSourceMap) {
try {
Object type = dataSourceMap.get("type");
if (type == null) {
type = DATASOURCE_TYPE_DEFAULT;// 默认DataSource
}
Class<? extends DataSource> dataSourceType;
dataSourceType = (Class<? extends DataSource>) Class.forName((String) type);
String driverClassName = dataSourceMap.get("driver").toString();
String url = dataSourceMap.get("url").toString();
String username = dataSourceMap.get("username").toString();
String password = dataSourceMap.get("password").toString();
// 自定义DataSource配置
DataSourceBuilder factory = DataSourceBuilder.create().driverClassName(driverClassName).url(url)
.username(username).password(password).type(dataSourceType);
return factory.build();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return null;
}
}

自定义注解

package com.chaoqi.springboot_datasource.config;

import java.lang.annotation.*;

/**
* @Author caiChaoqi
* @Date 2018-06-23
* @Description 作用于类、接口或者方法上
*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TargetDataSource { String name();
}
动态数据源通知
package com.chaoqi.springboot_datasource.config;

import org.apache.log4j.Logger;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component; /**
* @Author caiChaoqi
* @Description 动态数据源通知
* @Date 2018-06-23
*/
@Aspect
@Order(-1)//保证在@Transactional之前执行
@Component
public class DynamicDattaSourceAspect { private Logger logger = Logger.getLogger(DynamicDattaSourceAspect.class); //改变数据源
@Before("@annotation(targetDataSource)")
public void changeDataSource(JoinPoint joinPoint, TargetDataSource targetDataSource) {
String dbid = targetDataSource.name(); if (!DynamicDataSourceContextHolder.isContainsDataSource(dbid)) {
//joinPoint.getSignature() :获取连接点的方法签名对象
logger.error("数据源 " + dbid + " 不存在使用默认的数据源 -> " + joinPoint.getSignature());
} else {
logger.debug("使用数据源:" + dbid);
DynamicDataSourceContextHolder.setDataSourceType(dbid);
}
} @After("@annotation(targetDataSource)")
public void clearDataSource(JoinPoint joinPoint, TargetDataSource targetDataSource) {
logger.debug("清除数据源 " + targetDataSource.name() + " !");
DynamicDataSourceContextHolder.clearDataSourceType();
}
}

核心代码以全部展示,源码下载地址:https://github.com/caicahoqi/ChaoqiIsPrivateLibrary 如果在项目搭建中遇到问题可以在评论区留言,博主看到第一时间会给予回复,谢谢

springboot2.0动态多数据源切换的更多相关文章

  1. springboot2.0+mybatis多数据源集成

    最近在学springboot,把学的记录下来.主要有springboot2.0+mybatis多数据源集成,logback日志集成,springboot单元测试. 一.代码结构如下 二.pom.xml ...

  2. SpringBoot与动态多数据源切换

      本文简单的介绍一下基于SpringBoot框架动态多数据源切换的实现,采用主从配置的方式,配置master.slave两个数据库. 一.配置主从数据库 spring: datasource: ty ...

  3. springboot(2.0以上) --数据源切换时报错

    在进行数据源切换时spring.datasource.type类型根据源码所给的默认值修改后依然报错 先看源码:标色部分 ,  就是springboot所给的数据源 ,  正常来说只要在配置文件中修改 ...

  4. springboot动态多数据源切换

    application-test.properties #datasource -- mysql multiple.datasource.master.url=jdbc:mysql://localho ...

  5. springboot学习入门简易版九---springboot2.0整合多数据源mybatis mysql8+(22)

    一个项目中配置多个数据源(链接不同库jdbc),无限大,具体多少根据内存大小 项目中多数据源如何划分:分包名(业务)或注解方式.分包名方式类似多个不同的jar,同业务需求放一个包中. 分包方式配置多数 ...

  6. SpringBoot2.0之八 多数据源配置

     在开发的过程中我们可能都会遇到对接公司其他系统等需求,对于外部的系统可以采用接口对接的方式,对于一个公司开发的两个系统,并且知道相关数据库结构的情况下,就可以考虑使用多数据源来解决这个问题.Spri ...

  7. SpringBoot2.0 配置多数据源

    一.简述 配置多数据源意思就是在一个项目中使用多个数据库,在项目使用中可以不用手动切换数据库来实现不同数据库的数据获取和更新. 源码地址: https://github.com/hanguilin/b ...

  8. SpringBoot2 + Druid + Mybatis 多数据源动态配置

    在大数据高并发的应用场景下,为了更快的响应用户请求,读写分离是比较常见的应对方案.读写分离会使用多数据源的使用.下面记录如何搭建SpringBoot2 + Druid + Mybatis  多数据源配 ...

  9. Spring(AbstractRoutingDataSource)实现动态数据源切换--转载

    原始出处:http://linhongyu.blog.51cto.com/6373370/1615895 一.前言 近期一项目A需实现数据同步到另一项目B数据库中,在不改变B项目的情况下,只好选择项目 ...

随机推荐

  1. 同时使用n和nvm踩到的坑

    平时在公司的时候,一直只使用一个node版本.这两天想多了解下node的新版本,使用tj大神的 n 包管理工具来安装node包. 安装过程中一直难以容忍过慢的包下载速度,于是根据日志的描述直接把包下载 ...

  2. SpringCloud使用Feign实现服务间通信

    SpringCloud的服务间通信主要有两种办法,一种是使用Spring自带的RestTemplate,另一种是使用Feign,这里主要介绍后者的通信方式. 整个实例一共用到了四个项目,一个Eurek ...

  3. VSTO:使用C#开发Excel、Word【15】

    使用文档属性DocumentProperties集合和DocumentProperty对象位于Microsoft Office 11.0 Object Library(office.dll)中,该对象 ...

  4. 解决VS2010使用mscomm控件无法接收数据的问题【转】

    之前有用过VC6的mscomm控件.所以这次也想继续用此控件实现此功能,结果没想到刚一上手还真的绕了不少弯子.主要是因为VC2010下对mscomm控件的添加,以及对控件成员变量的添加有点小繁琐,特此 ...

  5. day 11 函数参数

    形参与实参 形参:就是形式参数,在函数定义时,写在括号里面指定的参数就是形式参数 实参:在调用函数时传入的参数就是实参 在调用函数时就会自动的把形参与实参绑定起来,然后调用结束之后,解除绑定关系 位置 ...

  6. [LeetCode&Python] Problem 674. Longest Continuous Increasing Subsequence

    Given an unsorted array of integers, find the length of longest continuousincreasing subsequence (su ...

  7. mysql与mysqli的区别

    博客搬家了,欢迎大家关注,https://bobjin.com mysqli连接是永久连接,而MySQL是非永久连接. mysql连接:每当第二次使用的时候,都会重新打开一个新的进程. mysqli连 ...

  8. arduino 522样本中文注释

    #accesscontrol访问控制#change uid更改UID#dump info转储信息#firmware_check固件#Fix Bricked UID修复变砖的UID#Mifare Cla ...

  9. Appium介绍及工作原理

    一.Appium介绍 Appium是一个开源.跨平台的测试框架,可以用来测试原生及混合的移动端应用.Appium支持IOS.Android及FirefoxOS平台.Appium使用WebDriver的 ...

  10. day04-Python的流程控制