文章目录

  • 1. AbstractRoutingDataSource类介绍
  • 2. ThreadLocal类介绍
  • 3. 环境准备
    • 3.1 数据库准备
    • 3.2 项目创建
  • 4. 具体实现
    • 4.1 定义数据源枚举类
    • 4.2 创建动态多数据源类
    • 4.3 创建动态多数据源配置类
    • 4.4 自定义注解用于指定数据源
    • 4.5 AOP实现动态切换数据源
  • 5. 测试使用
    • 5.1 配置数据源
    • 5.2 创建实体类
    • 5.3 服务层代码
    • 5.4 控制层代码

1. AbstractRoutingDataSource类介绍

Spring Boot提供了AbstractRoutingDataSource 根据用户定义的规则选择要使用的数据源,这样我们可以在每次数据库操作前设置使用的数据源,实现可动态路由的数据源。它的抽象方法determineCurrentLookupKey() 决定使用哪个数据源。

getConnection()获取数据库连接,根据查找lookup key键对不同目标数据源的调用,通常是通过(但不一定)某些线程绑定的事物上下文来实现。通过这我们知道可以实现:数据源的动态切换,在程序运行时,把数据源动态织入到程序中,灵活得进行数据源切换,从而可以不依赖中间件,实现读写分离功能。

AbstractRoutingDataSource实现逻辑:

  • 继承抽象类AbstractRoutingDataSource,并实现determineCurrentLookupKey()方法。自定义LookupKey的选择规则。

  • 把配置的多个数据源放在AbstractRoutingDataSource的 targetDataSources和defaultTargetDataSource中(使用setDefaultTargetDataSource和setTargetDataSources方法),然后通过afterPropertiesSet()方法将数据源分别进行复制到AbstractRoutingDataSource的resolvedDataSources属性和resolvedDefaultDataSource属性中。

  • 调用AbstractRoutingDataSource的getConnection()的方法的时候,先调用determineTargetDataSource()方法返回DataSource在进行getConnection()。


determineTargetDataSource()方法通过调用determineCurrentLookupKey() 方法返回的lookupKey决定使用哪个数据源。

2. ThreadLocal类介绍

https://blog.csdn.net/qq_38293564/article/details/80459827

3. 环境准备

3.1 数据库准备

一个本地环境的MySQL数据库,数据库mydb,创建表t_user

CREATE TABLE `t_user` (`c_id` varchar(20) NOT NULL,`c_username` varchar(20) DEFAULT NULL,`c_password` varchar(20) DEFAULT NULL,`c_gender` tinyint(2) DEFAULT NULL,PRIMARY KEY (`c_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;INSERT INTO `mydb`.`t_user`(`c_id`, `c_username`, `c_password`, `c_gender`) VALUES ('1', '思思', '123', 1);

一个云服务器的MySQL数据库,创建数据库book_db,创建表t_userinfo。

CREATE TABLE `t_user_info` (`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '用户id',`user_name` varchar(50) DEFAULT NULL COMMENT '用户名',`password` varchar(255) DEFAULT NULL COMMENT '登录密码',`areaObj` varchar(255) DEFAULT NULL COMMENT '所在学院',`name` varchar(20) DEFAULT NULL COMMENT '姓名',`sex` tinyint(255) DEFAULT NULL COMMENT '性别',`user_photo` varchar(255) DEFAULT NULL COMMENT '学生照片',`birthday` varchar(20) DEFAULT NULL COMMENT '出生日期',`telephone` varchar(20) DEFAULT NULL COMMENT '联系电话',`address` varchar(255) DEFAULT NULL COMMENT '家庭地址',PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;INSERT INTO `book_db`.`t_user_info`(`id`, `user_name`, `password`, `areaObj`, `name`, `sex`, `user_photo`, `birthday`, `telephone`, `address`) VALUES (1, '张三', '123', '哈尔滨', '张三散', 1, '123', '02-16', '15756892458', '黑龙江省哈尔滨市');

创建数据库chatroom,创建表admin。

CREATE TABLE `admin` (`id` int(11) NOT NULL AUTO_INCREMENT,`username` varchar(20) NOT NULL COMMENT '登录账号',`nickname` varchar(20) NOT NULL COMMENT '昵称',`password` varchar(255) NOT NULL COMMENT '密码',`user_profile` varchar(255) DEFAULT NULL COMMENT '管理员头像',PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC;INSERT INTO `chatroom`.`admin`(`id`, `username`, `nickname`, `password`, `user_profile`) VALUES (1, 'admin', '系统管理员', '$2a$10$PyloUEVGuO0fUZdfeIaROOTluRmccl.Scifa8S7Os0Wt.s4bDkb', 'https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=1784117537,3335593911&fm=26&gp=0.jpg');

3.2 项目创建

创建SpringBoot项目,整合MyBatis-Plus。pom.xml引入的依赖:

<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.projectlombok</groupId><artifactId>lombok</artifactId></dependency><!--mybatis plus--><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.0.1</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><!--druid--><dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>1.1.10</version></dependency><!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 --><dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId><version>3.9</version></dependency></dependencies>

配置读取resource文件夹下的mapper文件

 <build><resources><resource><directory>src/main/java</directory><includes><include>**/*.xml</include></includes></resource><resource><directory>src/main/resources</directory></resource></resources><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build>

4. 具体实现

4.1 定义数据源枚举类

定义数据源枚举类DataSourceTypeEnum

public enum DataSourceTypeEnum {/*** chatroom*/CHATROOM("chatroom"),/*** book_db*/BOOK_DB("book_db"),/*** mydb*/MY_DB("mydb");private final String name;DataSourceTypeEnum(String name) {this.name = name;}public String getName() {return name;}}

4.2 创建动态多数据源类

定义一个动态多数据源类DynamicDataSource用于管理不同线程间多个数据源的选择和切换,扩展 Spring 提供的 AbstractRoutingDataSource 抽象类,重写 determineCurrentLookupKey 方法,其中的determineCurrentLookupKey() 方法用于决定使用哪个数据源。

public class DynamicDataSource extends AbstractRoutingDataSource {/*** ThreadLocal 用于提供线程局部变量,在多线程环境可以保证各个线程里的变量独立于其它线程里的变量。* 也就是说 ThreadLocal 可以为每个线程创建一个【单独的变量副本】,相当于线程的 private static 类型变量。*/private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>();/*** 决定使用哪个数据源之前需要把多个数据源的信息以及默认数据源信息配置好** @param defaultTargetDataSource 默认数据源* @param targetDataSources       目标数据源*/public DynamicDataSource(DataSource defaultTargetDataSource, Map<Object, Object> targetDataSources) {super.setDefaultTargetDataSource(defaultTargetDataSource);super.setTargetDataSources(targetDataSources);super.afterPropertiesSet();}/*** determineCurrentLookupKey决定使用哪个数据库* @return*/@Overrideprotected Object determineCurrentLookupKey() {return getDataSource();}public static void setDataSource(String dataSource) {CONTEXT_HOLDER.set(dataSource);}public static String getDataSource() {return CONTEXT_HOLDER.get();}public static void clearDataSource() {CONTEXT_HOLDER.remove();}
}

4.3 创建动态多数据源配置类

DynamicDataSourceConfig类作为配置类,读取配置文件的三个数据源的配置,创建对应DataSource类型的Bean。

@Configuration
public class DynamicDataSourceConfig {@Bean(name="chatroom")@ConfigurationProperties("spring.datasource.druid.first")public DataSource dataSource1(){return DruidDataSourceBuilder.create().build();}@Bean(name ="book_db")@ConfigurationProperties("spring.datasource.druid.second")public DataSource dataSource2(){return DruidDataSourceBuilder.create().build();}@Bean(name="mydb")@ConfigurationProperties("spring.datasource.druid.third")public DataSource dataSource3(){return DruidDataSourceBuilder.create().build();}@Bean(name="dynamicDataSource")@Primarypublic DynamicDataSource dataSource() {Map<Object, Object> targetDataSources = new HashMap<>(5);targetDataSources.put(DataSourceTypeEnum.CHATROOM.getName(), dataSource1());targetDataSources.put(DataSourceTypeEnum.BOOK_DB.getName(), dataSource2());targetDataSources.put(DataSourceTypeEnum.MY_DB.getName(), dataSource3());return new DynamicDataSource(dataSource1(), targetDataSources);}
}

4.4 自定义注解用于指定数据源

自定义注解@SpecifyDataSource用于在Service层方法上标记要使用哪个数据源。这里定义默认使用数据源 DataSourceType.CHATROOM。

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SpecifyDataSource {/*** @return*/DataSourceTypeEnum value() default DataSourceTypeEnum.CHATROOM;
}

4.5 AOP实现动态切换数据源

定义数据源界面类DataSourceAspect,用于实现有SpecifyDataSource注解标注的方法前切换注解指定的数据源。

@Aspect
@Component
@Order(value = 1)
public class DataSourceAspect {private Logger logger = LoggerFactory.getLogger(this.getClass());@Pointcut("@annotation(top.javahai.datasource.annotation.SpecifyDataSource)")public void dataSourcePointCut() {}@Around("dataSourcePointCut()")public Object around(ProceedingJoinPoint point) throws Throwable {MethodSignature signature = (MethodSignature) point.getSignature();Method method = signature.getMethod();SpecifyDataSource ds = method.getAnnotation(SpecifyDataSource.class);if (ds == null) {DynamicDataSource.setDataSource(DataSourceType.CHATROOM.getName());logger.info("set datasource is " + DataSourceType.CHATROOM);} else {DynamicDataSource.setDataSource(ds.value().getName());logger.info("set datasource is " + ds.value().getName());}try {return point.proceed();} finally {DynamicDataSource.clearDataSource();logger.info("clean datasource");}}
}

5. 测试使用

5.1 配置数据源

spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.driverClassName=com.mysql.cj.jdbc.Driver
# 数据源1
spring.datasource.druid.first.url=jdbc:mysql://158.156.444.68:3306/chatroom?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=GMT%2B8
spring.datasource.druid.first.username=root
spring.datasource.druid.first.password=123456
# 数据源2
spring.datasource.druid.second.url=jdbc:mysql://158.156.444.68:3306/book_db?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=GMT%2B8
spring.datasource.druid.second.username=root
spring.datasource.druid.second.password=123456#数据源3
spring.datasource.druid.third.url=jdbc:mysql:///mydb?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=GMT%2B8
spring.datasource.druid.third.username=root
spring.datasource.druid.third.password=123456mybatis-plus.mapper-locations=classpath:mapper/*.xml#输出sql执行日志
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl

5.2 创建实体类

创建实体类Admin

public class Admin implements Serializable {private static final long serialVersionUID = 1L;@TableId(value = "id", type = IdType.AUTO)private Integer id;/*** 登录账号*/private String username;/*** 昵称*/private String nickname;/*** 密码*/private String password;/*** 管理员头像*/private String userProfile;//省略getter/setter方法

创建实体类TUser

public class TUser implements Serializable {private static final long serialVersionUID = 1L;@TableId(value = "c_id", type = IdType.AUTO)private Integer cId;private String cUsername;private String cPassword;private Integer cGender;
}

创建实体类TUserinfo

@TableName(value = "t_user_info")
public class TUserinfo implements Serializable {private static final long serialVersionUID = 1L;/*** user_name*/private String userName;/*** 登录密码*/private String password;/*** 所在学院*/@TableField("areaObj")private String areaObj;/*** 姓名*/private String name;/*** 性别*/private Integer sex;/*** 学生照片*/private String userPhoto;/*** 出生日期*/private String birthday;/*** 联系电话*/private String telephone;/*** 家庭地址*/private String address;
}

创建UserVO用于测试

public class UserVO {private List<Admin> adminList;private List<TUserinfo> tUserinfos;private List<TUser> tUsers;
}

5.3 服务层代码

@Service
public class AdminServiceImpl extends ServiceImpl<AdminMapper, Admin> implements IAdminService {public List<Admin> getAll(){return this.list(null);}
}
@Service
public class TUserinfoServiceImpl extends ServiceImpl<TUserinfoMapper, TUserinfo> implements ITUserinfoService {@SpecifyDataSource(value = DataSourceTypeEnum.BOOK_DB)public List<TUserinfo> selectAll(){return this.list(null);}}
@Service
public class TUserServiceImpl extends ServiceImpl<TUserMapper, TUser> implements ITUserService {@SpecifyDataSource(value = DataSourceTypeEnum.MY_DB)public List<TUser> selectAll(){return this.list(null);}
}
public interface AdminMapper extends BaseMapper<Admin> {}public interface TUserinfoMapper extends BaseMapper<TUserinfo> {}public interface TUserMapper extends BaseMapper<TUser> {}

5.4 控制层代码

创建接口/test/list用于测试

@RestController
@RequestMapping("/test")
public class TestController {@Autowiredprivate AdminServiceImpl adminService;@Autowiredprivate TUserinfoServiceImpl userinfoService;@Autowiredprivate TUserServiceImpl userService;@GetMapping("/list")public UserVO list(){List<Admin> adminList= adminService.getAll();List<TUserinfo> tUserinfos = userinfoService.selectAll();List<TUser> tUsers = userService.selectAll();UserVO userVO = new UserVO();userVO.setAdminList(adminList);userVO.settUserinfos(tUserinfos);userVO.settUsers(tUsers);return userVO;}
}

浏览器请求/test/list

查看控制台输出,查看数据源的切换日志

完整Demo代码地址:https://github.com/JustCoding-Hai/learn-everyday/tree/master/learn-multi_data_source

Spring Boot多数据源配置并通过注解实现动态切换数据源相关推荐

  1. Springboot/MybatisPlus动态切换数据源

    1.1 简述 最近项目中有动态切换数据源需求,主要是要动态切换数据源进行数据的获取,现将项目中实现思路及代码提供出来,供大家参考.当然切换数据源还有其他的方式比如使用切面的方式,其实大体思路是一样的. ...

  2. Spring Boot使用spring-data-jpa配置Mysql多数据源

    转载请注明出处 :Spring Boot使用spring-data-jpa配置Mysql多数据源 我们在之前的文章中已经学习了Spring Boot中使用mysql数据库 在单数据源的情况下,Spri ...

  3. 盘点Spring Boot最核心的27个注解

    Spring Boot方式的项目开发已经逐步成为Java应用开发领域的主流框架,它不仅可以方便地创建生产级的Spring应用程序,还能轻松地通过一些注解配置与目前比较火热的微服务框架SpringClo ...

  4. Spring Boot 2.0 配置图文教程

    摘要: 原创出处 https://www.bysocket.com 「公众号:泥瓦匠BYSocket 」欢迎关注和转载,保留摘要,谢谢! 本章内容 自定义属性快速入门 外化配置 自动配置 自定义创建 ...

  5. Spring Boot 2.0 配置图文教程 1

    Spring Boot 2.0 配置图文教程 摘要: 原创出处 https://www.bysocket.com 「公众号:泥瓦匠BYSocket 」欢迎关注和转载,保留摘要,谢谢! 本章内容 自定义 ...

  6. Spring Boot 2.0 配置图文教程第 2 章 Spring Boot 配置## 书信息 demo.book.name=[Spring Boot 2.x Core Action] demo.b

    本章内容 1.自定义属性快速入门 2.外化配置 3.自动配置 4.自定义创建 Starter 组件 摘录:读书是读完这些文字还要好好用心去想想,写书也一样,做任何事也一样 第 2 章 Spring B ...

  7. 详解Spring Boot框架中的@Conditional系列注解

    目录 1. @Conditional 注解 2. Spring boot 扩展 1) @ConditionalOnClass和@ConditionalOnMissingClass注解 2) @Cond ...

  8. SpringBoot - Spring Boot 中的配置体系Profile全面解读

    文章目录 Pre Spring Boot 中的配置体系 配置文件与 Profile 主 application.properties 中指定激活的Profile Profile 配置信息只保存在一个文 ...

  9. Spring Boot 最核心的 3 个注解详解

    最近面试一些 Java 开发者,他们其中有些在公司实际用过 Spring Boot, 有些是自己兴趣爱好在业余自己学习过. 然而,当我问他们 Spring Boot 最核心的 3 个注解是什么,令我失 ...

最新文章

  1. jdb java_JDB - 介绍
  2. oracle10g- emctl start dbconsole 启动问题
  3. 观察者模式——解耦利器
  4. [转]Sql Server 分页存储过程
  5. python excel数据分析实战_一次完整的数据分析实战!仅用4步,效率吊打Excel和Python...
  6. 腾讯视频如何设置缓存画质和个数
  7. 安装easy_install 和ipython
  8. HTK语音识别基础教程.ppt
  9. getmenuiteminfo无法读取内存_简单科普系列—内存为什么需要组双通道原理及其作用...
  10. android checkboxpreference属性,如何更改android中CheckBoxPreference标题的文本颜色?
  11. 三维浮雕软件 linux,立体浮雕工具下载 Type3(立体浮雕软件) v4.6.0.0 免费安装版 下载-脚本之家...
  12. 原来把300页Word转成PPT,只需要一个键!别再复制粘贴了
  13. vscode 程序员鼓励师_把软萌程序猿鼓励师装进VScode里?最强交互彩虹屁,GitHub2.5k星标,爱上写代码...
  14. 里氏转换,arraylist,path,file
  15. php系统不能输入中文,word无法输入汉字 如何解决Word不能输入中文的问题
  16. 数理统计内容整理(一)基本概念
  17. 台积电股价突破万亿大关 创下台股历史新高
  18. greenplum数据导入到mysql,greenplum数据库常用操作
  19. 系统架构师(一)选择题
  20. redis哨兵、集群

热门文章

  1. 王者荣耀貂蝉唤灵魅影技能特效展示 唤灵魅影何时上架
  2. 这次,在人工智能面前,人类输得一败涂地
  3. 最简单之获取app签名md5值
  4. 机器学习:什么是无监督学习(Unsupervised Learning)?
  5. Parameter specified as non-null is null method kotlin.jvm.internal.Intrinsics.checkNotNullParameter
  6. Linux基本命令 初级10个
  7. Rational Rose使用说明
  8. 使用CSS绘制一个平行四边形
  9. 新一轮勒索病毒变种全球肆虐 中国已遭攻击
  10. 大数据应用技术实验报告六 Hive和MySQL