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动态数据源的更多相关文章

  1. spring boot动态数据源方案

    动态数据源 1.背景 动态数据源在实际的业务场景下需求很多,而且想要沟通多数据库确实需要封装这种工具,针对于bi工具可能涉及到从不同的业务库或者数据仓库中获取数据,动态数据源就更加有意义. 2.依赖 ...

  2. springboot动态多数据源

    参考文章:https://www.cnblogs.com/hehehaha/p/6147096.html 前言 目标是springboot工程支持多个MySQL数据源,在代码层面上,同一个SQL(Ma ...

  3. springboot 双数据源+aop动态切换

    # springboot-double-dataspringboot-double-data 应用场景 项目需要同时连接两个不同的数据库A, B,并且它们都为主从架构,一台写库,多台读库. 多数据源 ...

  4. springboot动态多数据源切换

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

  5. SpringBoot(十一)-- 动态数据源

    SpringBoot中使用动态数据源可以实现分布式中的分库技术,比如查询用户 就在用户库中查询,查询订单 就在订单库中查询. 一.配置文件application.properties # 默认数据源 ...

  6. SpringBoot和Mycat动态数据源项目整合

    SpringBoot项目整合动态数据源(读写分离) 1.配置多个数据源,根据业务需求访问不同的数据,指定对应的策略:增加,删除,修改操作访问对应数据,查询访问对应数据,不同数据库做好的数据一致性的处理 ...

  7. SpringBoot整合MyBatisPlus配置动态数据源

    目录 SpringBoot整合MyBatisPlus配置动态数据源 SpringBoot整合MyBatisPlus配置动态数据源 推文:2018开源中国最受欢迎的中国软件MyBatis-Plus My ...

  8. SpringBoot集成Mybatis配置动态数据源

    很多人在项目里边都会用到多个数据源,下面记录一次SpringBoot集成Mybatis配置多数据源的过程. pom.xml <?xml version="1.0" encod ...

  9. SpringBoot多数据源动态切换数据源

    1.配置多数据源 spring: datasource: master: password: erp_test@abc url: jdbc:mysql://127.0.0.1:3306/M201911 ...

随机推荐

  1. 《Cracking the Coding Interview》——第2章:链表——题目2

    2014-03-18 02:24 题目:给定一个单链表,找出倒数第K个节点. 解法:让一个指针先走K步,然后俩指针一起走到尽头.当然也可以先走到尽头数出链表的长度,然后第二次少走K步.其实耗费的工夫是 ...

  2. USACO Section2.1 Ordered Fractions 解题报告

    frac1解题报告 —— icedream61 博客园(转载请注明出处)---------------------------------------------------------------- ...

  3. (转\整)UE4游戏优化 多人大地型游戏的优化(一)游戏线程的优化

    施主分享随缘,评论随心,@author:白袍小道 小道暗语: 1.因为小道这里博客目录没自己整,暂时就用随笔目录结构,所以二级目录那啥就忽略了.标题格式大致都是(原or转) 二级目录 (标题) 2.因 ...

  4. Java 打印* 三角形

    package anli1; public class sanjiaoxing { public static void main(String[] agrs){ System.out.println ...

  5. oom 和 jvm crash的问题

    很多次生产环境jvm进程无故消失的时候都留下了hs_err[pid].log文件  然后通过mat分析大多数情况是oom导致的  所以以前一直认为OOM一定会导致jvm crash  也就是说java ...

  6. 九宫重排_康拓展开_bfs

      历届试题 九宫重排   时间限制:1.0s   内存限制:256.0MB        问题描述 如下面第一个图的九宫格中,放着 1~8 的数字卡片,还有一个格子空着.与空格子相邻的格子中的卡片可 ...

  7. poj1273 网络流入门题 dinic算法解决,可作模板使用

    Drainage Ditches Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 62078   Accepted: 2384 ...

  8. ACM-渊子赛马

    题目: 赛马是一古老的游戏,早在公元前四世纪的中国,处在诸侯割据的状态,历史上称为“战国时期”.在魏国作官的孙膑,因为受到同僚庞涓的迫害,被齐国使臣救出后,到达齐国国都. 赛马是当时最受齐国贵族欢迎的 ...

  9. 转:mysql group by

    group by 用法解析 group by语法可以根据给定数据列的每个成员对查询结果进行分组统计,最终得到一个分组汇总表. SELECT子句中的列名必须为分组列或列函数.列函数对于GROUP BY子 ...

  10. Linux用户态定时器用法以及犯错总结【转】

    转自:http://blog.csdn.net/csdn_logo/article/details/48525703 版权声明:本文为博主原创文章,欢迎转载,转载请注明出处,多谢合作. 采样的时候要用 ...