谈谈Spring Boot 数据源加载及其多数据源简单实现(小结)

业务需求

  • 提供所有微服务数据源的图形化维护功能
  • 代码生成可以根据选择的数据源加载表等源信息
  • 数据源管理要支持动态配置,实时生效

附录效果图

实现思路

本文提供方法仅供类似简单业务场景,在生产环境和复杂的业务场景 请使用分库分表的中间件(例如mycat)或者框架 sharding-sphere (一直在用)等

先来看Spring 默认的数据源注入策略,如下代码默认的事务管理器在初始化时回去加载数据源实现。这里就是我们动态数据源的入口

// 默认的事务管理器
ppublic class DataSourceTransactionManager extends AbstractPlatformTransactionManager
  implements ResourceTransactionManager, InitializingBean {

 // 启动时候注入一个数据源
 public void setDataSource(@Nullable DataSource dataSource) {
  if (dataSource instanceof TransactionAwareDataSourceProxy) {
   this.dataSource = ((TransactionAwareDataSourceProxy) dataSource).getTargetDataSource();
  }
  else {
   this.dataSource = dataSource;
  }
 }
」

通过注入一个新的DataSourceTransactionManager 实现,并且给它设置多个 DataSource 来实现多数据源实现

看下Spring 默认提供的路由数据源字段

public abstract class AbstractRoutingDataSource extends AbstractDataSource implements InitializingBean {

 // 用户设置的全部的数据源配置
 @Nullable
 private Map<Object, Object> targetDataSources;
 // 为空默认的数据源配置
 @Nullable
 private Object defaultTargetDataSource;

 // 路由键查找实现
 private DataSourceLookup dataSourceLookup = new JndiDataSourceLookup();

 // 最终有效的数据源配置(一般清空对应上边用户的设置)
 @Nullable
 private Map<Object, DataSource> resolvedDataSources;
}

开始动手

实现AbstractRoutingDataSource,定一个动态数据源实现,只需要实现他的路由key 查找方法即可。

这里的路由key 对应其实是resolvedDataSources Map 的key哟

@Slf4j
public class DynamicDataSource extends AbstractRoutingDataSource {

 /**
  * 指定路由Key,这里很简单 获取 threadLocal 中目标key 即可
  *
  * @return
  */
 @Override
 protected Object determineCurrentLookupKey() {
  return DynamicDataSourceContextHolder.getDataSourceType();
 }
}

把我们动态数据源实现注入到Spring 的事务管理器,去数据库查询出来全部的数据源信息,定义一个个具体的数据源实现 我这里使用的HikariDataSource 给他赋值等等

@Slf4j
@Configuration
@AllArgsConstructor
public class DynamicDataSourceConfig implements TransactionManagementConfigurer {
 private final Map<Object, Object> dataSourceMap = new HashMap<>(8);
 private final DataSourceProperties dataSourceProperties;

 @Bean("dynamicDataSource")
 public DynamicDataSource dataSource() {
  JdbcTemplate(dds).queryForList(DataSourceConstant.QUERY_DS_SQL);
  log.info("开始 -> 初始化动态数据源");
  Optional.of(dbList).ifPresent(list -> list.forEach(db -> {
   log.info("数据源:{}", db.get(DataSourceConstant.DS_NAME));
   HikariDataSource ds = new HikariDataSource();
   dataSourceMap.put(db.get(DataSourceConstant.DS_ROUTE_KEY), ds);
  }));

  DynamicDataSource ds = new DynamicDataSource();
  ds.setTargetDataSources(dataSourceMap);
  return ds;
 }

 @Bean
 public PlatformTransactionManager txManager() {
  return new DataSourceTransactionManager(dataSource());
 }

 @Override
 public PlatformTransactionManager annotationDrivenTransactionManager() {
  return txManager();
 }

}

怎么使用

只需要根据用户前台选择的数据源key ,在业务类保存到TTL 即可,会自动根据选择路由数据源

DynamicDataSourceContextHolder.setDataSourceType(key)

这里当然也可以根据AOP 自定义注解等实现。

如何动态数据源动态配置

上边其实已经完成了 我们想要的需求功能,但是有什么问题呢?
我们在数据源管理面维护了数据源,动态去修改这个 dataSourceMap 其实是无效的,不能做到实时刷新

我们来看下 AbstractRoutingDataSource 的加载map 数据源的源码,只有在初始化的时候调用 afterPropertiesSet 去初始数据源map.

那我们只要获取当前的DynamicDataSource bean 手动调用afterPropertiesSet 即可。

整个代码如下

public class DynamicDataSourceConfig implements TransactionManagementConfigurer {
 private final Map<Object, Object> dataSourceMap = new HashMap<>(8);
 private final DataSourceProperties dataSourceProperties;
 private final StringEncryptor stringEncryptor;

 @Bean("dynamicDataSource")
 public DynamicDataSource dataSource() {
  DynamicDataSource ds = new DynamicDataSource();
  HikariDataSource cads = new HikariDataSource();
  cads.setJdbcUrl(dataSourceProperties.getUrl());
  cads.setDriverClassName(dataSourceProperties.getDriverClassName());
  cads.setUsername(dataSourceProperties.getUsername());
  cads.setPassword(dataSourceProperties.getPassword());
  ds.setDefaultTargetDataSource(cads);
  dataSourceMap.put(0, cads);
  ds.setTargetDataSources(dataSourceMap);
  return ds;
 }

 /**
  * 组装默认配置的数据源,查询数据库配置
  */
 @PostConstruct
 public void init() {
  DriverManagerDataSource dds = new DriverManagerDataSource();
  dds.setUrl(dataSourceProperties.getUrl());
  dds.setDriverClassName(dataSourceProperties.getDriverClassName());
  dds.setUsername(dataSourceProperties.getUsername());
  dds.setPassword(dataSourceProperties.getPassword());

  List<Map<String, Object>> dbList = new JdbcTemplate(dds).queryForList(DataSourceConstant.QUERY_DS_SQL);
  log.info("开始 -> 初始化动态数据源");
  Optional.of(dbList).ifPresent(list -> list.forEach(db -> {
   log.info("数据源:{}", db.get(DataSourceConstant.DS_NAME));
   HikariDataSource ds = new HikariDataSource();
   ds.setJdbcUrl(String.valueOf(db.get(DataSourceConstant.DS_JDBC_URL)));
   ds.setDriverClassName(Driver.class.getName());
   ds.setUsername((String) db.get(DataSourceConstant.DS_USER_NAME));

   String decPwd = stringEncryptor.decrypt((String) db.get(DataSourceConstant.DS_USER_PWD));
   ds.setPassword(decPwd);
   dataSourceMap.put(db.get(DataSourceConstant.DS_ROUTE_KEY), ds);
  }));

  log.info("完毕 -> 初始化动态数据源,共计 {} 条", dataSourceMap.size());
 }

 /**
  * 重新加载数据源配置
  */
 public Boolean reload() {
  init();
  DynamicDataSource dataSource = dataSource();
  dataSource.setTargetDataSources(dataSourceMap);
  dataSource.afterPropertiesSet();
  return Boolean.FALSE;
 }

 @Bean
 public PlatformTransactionManager txManager() {
  return new DataSourceTransactionManager(dataSource());
 }

 @Override
 public PlatformTransactionManager annotationDrivenTransactionManager() {
  return txManager();
 }

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • springboot下配置多数据源的方法

    一.springboot 简介 SpringBoot使开发独立的,产品级别的基于Spring的应用变得非常简单,你只需"just run". 我们为Spring平台及第三方库提 供开箱即用的设置,这样你就可以有条不紊地开始.多数Spring Boot应用需要很少的Spring配置. 你可以使用SpringBoot创建Java应用,并使用 java -jar 启动它或采用传统的war部署方式.我们也提供了一个运行"spring 脚本"的命令行工具. 二.传统的Dat

  • SpringBoot项目中的多数据源支持的方法

    1.概述 项目中经常会遇到一个应用需要访问多个数据源的情况,本文介绍在SpringBoot项目中利用SpringDataJpa技术如何支持多个数据库的数据源. 具体的代码参照该 示例项目 2.建立实体类(Entity) 首先,我们创建两个简单的实体类,分别属于两个不同的数据源,用于演示多数据源数据的保存和查询. Test实体类: package com.example.demo.test.data; import javax.persistence.Entity; import javax.pe

  • spring boot springjpa 支持多个数据源的实例代码

    1.SpringBoot的程序启动类 import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.boot.context.web

  • Spring Boot 集成Mybatis实现主从(多数据源)分离方案示例

    本文将介绍使用Spring Boot集成Mybatis并实现主从库分离的实现(同样适用于多数据源).延续之前的Spring Boot 集成MyBatis.项目还将集成分页插件PageHelper.通用Mapper以及Druid. 新建一个Maven项目,最终项目结构如下: 多数据源注入到sqlSessionFactory POM增加如下依赖: <!--JSON--> <dependency> <groupId>com.fasterxml.jackson.core<

  • springboot + mybatis配置多数据源示例

    在实际开发中,我们一个项目可能会用到多个数据库,通常一个数据库对应一个数据源. 代码结构: 简要原理: 1)DatabaseType列出所有的数据源的key---key 2)DatabaseContextHolder是一个线程安全的DatabaseType容器,并提供了向其中设置和获取DatabaseType的方法 3)DynamicDataSource继承AbstractRoutingDataSource并重写其中的方法determineCurrentLookupKey(),在该方法中使用Da

  • spring boot+mybatis 多数据源切换(实例讲解)

    由于公司业务划分了多个数据库,开发一个项目会同事调用多个库,经过学习我们采用了注解+aop的方式实现的 1.首先定义一个注解类 @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface TargetDataSource { String value();//此处接收的是数据源的名称 } 2.然后建一个配置类,这个在项目启动时会加载数据源,一开始采用了HikariCP,查资料说是最快性能最好的

  • 通过Spring Boot配置动态数据源访问多个数据库的实现代码

    之前写过一篇博客<Spring+Mybatis+Mysql搭建分布式数据库访问框架>描述如何通过Spring+Mybatis配置动态数据源访问多个数据库.但是之前的方案有一些限制(原博客中也描述了):只适用于数据库数量不多且固定的情况.针对数据库动态增加的情况无能为力. 下面讲的方案能支持数据库动态增删,数量不限. 数据库环境准备 下面一Mysql为例,先在本地建3个数据库用于测试.需要说明的是本方案不限数据库数量,支持不同的数据库部署在不同的服务器上.如图所示db_project_001.d

  • Spring Boot + Mybatis多数据源和动态数据源配置方法

    网上的文章基本上都是只有多数据源或只有动态数据源,而最近的项目需要同时使用两种方式,记录一下配置方法供大家参考. 应用场景 项目需要同时连接两个不同的数据库A, B,并且它们都为主从架构,一台写库,多台读库. 多数据源 首先要将spring boot自带的DataSourceAutoConfiguration禁掉,因为它会读取application.properties文件的spring.datasource.*属性并自动配置单数据源.在@SpringBootApplication注解中添加ex

  • Spring Boot+Mybatis+Druid+PageHelper实现多数据源并分页的方法

    前言 本篇文章主要讲述的是SpringBoot整合Mybatis.Druid和PageHelper 并实现多数据源和分页.其中SpringBoot整合Mybatis这块,在之前的的一篇文章中已经讲述了,这里就不过多说明了.重点是讲述在多数据源下的如何配置使用Druid和PageHelper . Druid介绍和使用 在使用Druid之前,先来简单的了解下Druid. Druid是一个数据库连接池.Druid可以说是目前最好的数据库连接池!因其优秀的功能.性能和扩展性方面,深受开发人员的青睐. D

  • SpringBoot使用Druid数据源的配置方法

    Druid是阿里巴巴开源平台上一个数据库连接池实现,它结合了C3P0.DBCP.PROXOOL等DB池的优点,同时加入了日志监控,可以很好的监控DB池连接和SQL的执行情况,可以说是针对监控而生的DB连接池(据说是目前最好的连接池) 一.依赖 为了测试,使用jdbcTemplate <!-- jdbcTemplate --> <dependency> <groupId>org.springframework.boot</groupId> <artifa

随机推荐