SpringBoot动态数据源
1、原理图

2、创建枚举类
/**
* 存数据源key值
*/
public enum DataSourceKey {
master,salve,migration
}
3、创建自定义注解类
/**
* 自定义注解
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DBSource
{ String value() default "master"; }
4、切换数据源类
/**
* @author yehui
* 根据线程动态切换数据源
*/
@Configuration
public class DynamicDataSourceContextHolder {
private static final Logger log = LoggerFactory.getLogger(DynamicDataSourceContextHolder.class); /**
* 设置默认数据源
*/
public static String DEFAULT_DS = "master";
/**
*用于轮训计数
*/
private static int counter = 0;
/*
* 当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,
* 所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。
*/
private static final ThreadLocal<String> contextHolder = ThreadLocal.withInitial(() -> DataSourceKey.master.name()); /**
*用于在切换数据源时保证不会被其他线程修改
*/
public static Lock lock = new ReentrantLock(); /**
* 设置数据源
*/
public static void setDB(String dbType){
log.info("切换到{" + dbType + "}数据源");
contextHolder.set(dbType);
} /**
* 得到数据源
*
*/
public static String getDB(){
return contextHolder.get();
} /**
* 使用主数据源
*/
public static void useMasterDataSource() {
contextHolder.set(DataSourceKey.master.name());
}
/**
* 移除数据源
*/
public static void removeDB(){
contextHolder.remove();
}
/**
* The constant slaveDataSourceKeys.
*/
public static List<Object> slaveDataSourceKeys = new ArrayList<>();
/**
* 当使用只读数据源时通过轮循方式选择要使用的数据源
*/
public static String getSlaveDB(){
lock.lock();
try {
int datasourceKeyIndex = counter % slaveDataSourceKeys.size();
counter++;
return String.valueOf(slaveDataSourceKeys.get(datasourceKeyIndex));
} catch (Exception e) {
log.error(e.getMessage(), e);
e.printStackTrace();
return "master";
} finally {
lock.unlock();
}
}
}
5、获取数据源类
/**
* @author yehui
* 多数据源的选择
*/
public class DynamicDataSource extends AbstractRoutingDataSource {
private static final Logger log = LoggerFactory.getLogger(DynamicDataSource.class); @Override
protected Object determineCurrentLookupKey() {
log.info("Current DataSource is " + DynamicDataSourceContextHolder.getDB());
return DynamicDataSourceContextHolder.getDB();
}
}
6、Aop类
/**
* @author yehui
* 自定义注解 + AOP的方式实现数据源动态切换。
*/
@Aspect
@Component
public class DynamicDataSourceAspect {
private static final Logger log = LoggerFactory.getLogger(DynamicDataSourceAspect.class); @Before("@annotation(DBSource)")
public void beforeSwitchDB(JoinPoint joinPoint,DBSource DBSource){
//获取目标类的方法
Class<?> aClass = joinPoint.getTarget().getClass();
//获得访问的方法名
String methodName = joinPoint.getSignature().getName();
//得到方法的参数类型
Class[] parameterTypes = ((MethodSignature) joinPoint.getSignature()).getParameterTypes();
String dataSource = DynamicDataSourceContextHolder.DEFAULT_DS;
try {
Method method = aClass.getMethod(methodName, parameterTypes);
if(method.isAnnotationPresent(DBSource.class)){
DBSource db = method.getAnnotation(DBSource.class);
//指定数据源
dataSource = db.value();
}else{
//轮训设置数据源
dataSource = DynamicDataSourceContextHolder.getSlaveDB();
}
} catch (NoSuchMethodException e) {
log.error(e.getMessage(), e);
}
//设置数据源
DynamicDataSourceContextHolder.setDB(dataSource);
} @After("@annotation(DBSource)")
public void afterSwitchDB(DBSource DBSource){
DynamicDataSourceContextHolder.removeDB();
}
}
6、application.properties文件
spring.druid.datasource.slave.password=root
spring.druid.datasource.slave.username=root
spring.druid.datasource.slave.jdbc-url=jdbc:mysql://localhost:3306/study
spring.druid.datasource.slave.driver-class-name=com.mysql.jdbc.Driver spring.druid.datasource.master.password=root
spring.druid.datasource.master.username=root
spring.druid.datasource.master.jdbc-url=jdbc:mysql://localhost:3306/study01
spring.druid.datasource.master.driver-class-name=com.mysql.jdbc.Driver spring.druid.datasource.migration.password=root
spring.druid.datasource.migration.username=root
#2.0版本多数据源必须是使用jdbc-url 不能使用url,否则报错 jdbcUrl is required with driverClassName
spring.druid.datasource.migration.jdbc-url=jdbc:mysql://localhost:3306/study02
spring.druid.datasource.migration.driver-class-name=com.mysql.jdbc.Driver
7、数据源配置类
/**
* @author yehui
* 数据源配置类
*/
@Configuration
public class DataSourceConfig { /**
* 主数据
*
* @return data source
*/
@Bean("master")
@Primary
@ConfigurationProperties(prefix = "spring.druid.datasource.master")
public DataSource master() {
return DataSourceBuilder.create().build();
} /**
* 从数据库
*
* @return data source
*/
@Bean("slave")
@ConfigurationProperties(prefix ="spring.druid.datasource.slave")
public DataSource slave() {
return DataSourceBuilder.create().build();
}
/**
* 从数据库
*
* @return data source
*/
@Bean("migration")
@ConfigurationProperties(prefix ="spring.druid.datasource.migration")
public DataSource migration() {
return DataSourceBuilder.create().build();
} /**
* 配置动态数据源
*
* @return
*/
@Bean("dynamicDataSource")
public DataSource dynamicDataSource() {
DynamicDataSource dynamicRoutingDataSource = new DynamicDataSource();
Map<Object, Object> dataSourceMap = new HashMap<>(4);
dataSourceMap.put(DataSourceKey.master.name(), master());
dataSourceMap.put(DataSourceKey.salve.name(), slave());
dataSourceMap.put(DataSourceKey.master.name(), slave()); //设置默认的数据源
dynamicRoutingDataSource.setDefaultTargetDataSource(master());
// 多个slave数据源在此添加,自定义key,用于轮询
dataSourceMap.put(DataSourceKey.salve.name() + "1", slave());
//设置目标数据源
dynamicRoutingDataSource.setTargetDataSources(dataSourceMap);
//将数据源的key放在集合中判断是否正常
DynamicDataSourceContextHolder.slaveDataSourceKeys.addAll(dataSourceMap.keySet()); //实现负载均衡算法 将 Slave 数据源的 key 放在集合中,用于轮循
DynamicDataSourceContextHolder.slaveDataSourceKeys.addAll(dataSourceMap.keySet());
DynamicDataSourceContextHolder.slaveDataSourceKeys.remove(DataSourceKey.migration.name());
return dynamicRoutingDataSource;
} /**
* 设置工厂类
*/
@Bean
public SqlSessionFactoryBean sqlSessionFactoryBean() {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dynamicDataSource()); //此处设置为了解决找不到mapper文件的问题
try {
sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/*Mapper.xml"));
} catch (IOException e) {
e.printStackTrace();
}
return sqlSessionFactoryBean;
} /**
* 事物管理器
*/
@Bean("transactionManager")
public DataSourceTransactionManager transactionManager() {
return new DataSourceTransactionManager(dynamicDataSource());
}
}
8、启动类
/**
* springboot入口类,此类需要在所有用到的package上层 exclude =
* {DataSourceAutoConfiguration.class}
* 禁用springboot默认加载的application.properties单数据源配置
*/
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
public class StartApp {
public static void main(String[] args) {
SpringApplication.run(StartApp.class);
}
}
9、测试
mapper接口
@Mapper
public interface UserDataSourceMapper {
public List<TbUser> findUser();
}
mapper文件
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.yehui.mapper.UserDataSourceMapper">
<select id="findUser" resultType="com.yehui.entity.TbUser">
select * from tb_user
</select>
</mapper>
service类
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserDataSourceMapper userMapper; @Override
@DBSource("slave")//使用数据源打上注解即可
public List<TbUser> findUser() {
return userMapper.findUser();
}
}
controller类
@RestController
public class UserController { @Autowired
private UserService userService;
@RequestMapping("/findUser")
public List<TbUser> findUser(){
return userService.findUser();
}
}
效果:

如图所示,则动态数据源配置成功
SpringBoot动态数据源的更多相关文章
- spring boot动态数据源方案
动态数据源 1.背景 动态数据源在实际的业务场景下需求很多,而且想要沟通多数据库确实需要封装这种工具,针对于bi工具可能涉及到从不同的业务库或者数据仓库中获取数据,动态数据源就更加有意义. 2.依赖 ...
- springboot动态多数据源
参考文章:https://www.cnblogs.com/hehehaha/p/6147096.html 前言 目标是springboot工程支持多个MySQL数据源,在代码层面上,同一个SQL(Ma ...
- springboot 双数据源+aop动态切换
# springboot-double-dataspringboot-double-data 应用场景 项目需要同时连接两个不同的数据库A, B,并且它们都为主从架构,一台写库,多台读库. 多数据源 ...
- springboot动态多数据源切换
application-test.properties #datasource -- mysql multiple.datasource.master.url=jdbc:mysql://localho ...
- SpringBoot(十一)-- 动态数据源
SpringBoot中使用动态数据源可以实现分布式中的分库技术,比如查询用户 就在用户库中查询,查询订单 就在订单库中查询. 一.配置文件application.properties # 默认数据源 ...
- SpringBoot和Mycat动态数据源项目整合
SpringBoot项目整合动态数据源(读写分离) 1.配置多个数据源,根据业务需求访问不同的数据,指定对应的策略:增加,删除,修改操作访问对应数据,查询访问对应数据,不同数据库做好的数据一致性的处理 ...
- SpringBoot整合MyBatisPlus配置动态数据源
目录 SpringBoot整合MyBatisPlus配置动态数据源 SpringBoot整合MyBatisPlus配置动态数据源 推文:2018开源中国最受欢迎的中国软件MyBatis-Plus My ...
- SpringBoot集成Mybatis配置动态数据源
很多人在项目里边都会用到多个数据源,下面记录一次SpringBoot集成Mybatis配置多数据源的过程. pom.xml <?xml version="1.0" encod ...
- SpringBoot多数据源动态切换数据源
1.配置多数据源 spring: datasource: master: password: erp_test@abc url: jdbc:mysql://127.0.0.1:3306/M201911 ...
随机推荐
- 《Cracking the Coding Interview》——第7章:数学和概率论——题目4
2014-03-20 02:16 题目:只用加法和赋值,实现减法.乘法.除法. 解法:我只实现了整数范围内的.减法就是加上相反数.乘法就是连着加上很多个.除法就是减到不能减为止,数数总共减了多少个. ...
- CodeIgniter学习笔记二:CI中的query_builder(AR)、连贯操作
一.开启query_builder 在application\config\database.php中添加如下代码(默认已开启): $query_builder = TRUE; 二.查询数据 //ge ...
- 记录下MoKee编译过程
纯属记录帖 关注和了解这个rom有段时间了. 最近有需要了解odex,折腾了几天还是在坑里. 索性,先编译下MoKee看看. 之前make过 4.2 和 5.1 ,刷到模拟器和N5里. 编译教程可以参 ...
- 【Perceptron Learning Algorithm】林轩田机器学习基石
直接跳过第一讲.从第二讲Perceptron开始,记录这一讲中几个印象深的点: 1. 之前自己的直觉一直对这种图理解的不好,老按照x.y去理解. a) 这种图的每个坐标代表的是features:fea ...
- selenium IDE录制脚本和自定义脚本-->Katalon Recorder(二)
selenium IDE提供了两种生成脚本的方式:录制脚本和自定义脚本 录制脚本:1.打开firefox空白标签,在标签上输入需要录制脚本的网址:2.打开selenium IDE界面中的录制按钮(圆形 ...
- 孤荷凌寒自学python第六十二天学习mongoDB的基本操作并进行简单封装1
孤荷凌寒自学python第六十二天学习mongoDB的基本操作并进行简单封装1 (完整学习过程屏幕记录视频地址在文末) 今天是学习mongoDB数据库的第八天. 今天开始学习mongoDB的简单操作, ...
- 事件Qevent的接受和忽略 和重定义 事件过滤器(转)
转载来源:http://blog.csdn.net/seanyxie/article/details/5821970 事件处理流程:某个事件发生------>exec()循环会接收到这个事件-- ...
- 使用puTTY或Xshell连接阿里云TimeOut超时
根据网上很多主流的说法,我依次检查了 ssh: service sshd status 防火墙:service iptables stop (CentOS 7好像已经没有这个iptable了) 都没有 ...
- android DOM解析Xml
文章转自:http://blog.sina.com.cn/s/blog_a661f16c0101d5qp.html People类是自己写的一个类,主要保存各个字符串数据. 由于没学过Xml语法只 ...
- Client does not support authentication protocol requested by server
关于由于版本号码不同而引起的 Client does not support authentication protocol requested by server 问题 搜索类似的问题,得到的答案类 ...