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. IIS 部署网站--浏览--“该页无法显示”

    解决办法: 打开IIS管理器--web站点(网站)--右键点击对应的站点--属性--主目录--执行权限改为(脚本和和执行文件) 点击“应用”--确定. 重启一下站点,OK.

  2. pytest 运行指定用例

    pytest运行指定用例 随着软件功能的增加,模块越来越多,也意味用例越来越多,为了节约执行时间,快速得到测试报告与结果,在工作中可以通过运行指定用例,达到快速执行用例 例子目录 spec_sub1_ ...

  3. C编译器MinGW安装、下载及在notepad++中运行C程序

    一.C编译器MinGW的下载及安装步骤 打开MinGW官网:http://www.mingw.org/ 图一 图二 图三 图四 图五 图六 系统中配置环境变量: 图七 验证是否安装成功: CMD中运行 ...

  4. Leetcode 661.图片平滑器

    图片平滑器 包含整数的二维矩阵 M 表示一个图片的灰度.你需要设计一个平滑器来让每一个单元的灰度成为平均灰度 (向下舍入) ,平均灰度的计算是周围的8个单元和它本身的值求平均,如果周围的单元格不足八个 ...

  5. Android开发实例总结

    写一个修改密码的界面 1画界面总结: 需要弄清楚什么地方用相对布局,什么地方使用线性布局 希望这过后自己花时间去弄清楚他们内嵌的的所有组件以及组件的属性包括用法. 2逻辑总结: 逻辑描述总是那么几步的 ...

  6. springboot13 Hikari 和Introspector

    SpringBoot Initializr Introspector(内省) class TestReflect { @Test fun testReflect() { //获取字节码对象 val c ...

  7. URAL 1684. Jack's Last Word ( KMP next函数应用 )

    题意:问第二行的串能不能恰好分割成几个串,使得这几个串都是第一行串的前缀.如果是,输出No, 并输出这几个串,否则输出Yes. 这题是Special Judge,把两个串连接起来,中间用一个未出现过的 ...

  8. SVN基本介绍

    SVN是一种项目合作开发的软件,参与项目的人员可以在不同的地方实现文件和目录的超时空共享. 两个重要的概念: 1.配置库(Repository) SVN的核心是配置库,储存所有的数据,配置库按照文件树 ...

  9. web自动化测试:watir+minitest(五)

    测试报告: 加载minitest-reporters库,并设置相关的参数.既可以在每次运行测试后生成响应的测试报告. 默认会生成一份html的报告在当前目录的test目录下 我们可以指定参数对报告的标 ...

  10. 【bzoj4636】蒟蒻的数列 离散化+线段树

    原文地址:http://www.cnblogs.com/GXZlegend/p/6801379.html 题目描述 蒟蒻DCrusher不仅喜欢玩扑克,还喜欢研究数列 题目描述 DCrusher有一个 ...