软件编程
位置:首页>> 软件编程>> java编程>> SpringBoot多数据源切换实现代码(Mybaitis)

SpringBoot多数据源切换实现代码(Mybaitis)

作者:胡安民  发布时间:2021-11-07 11:15:50 

标签:springboot,多数据源,切换

前言

但是在实际业务场景中,数据量迅速增长,一个库一个表已经满足不了我们的需求的时候,我们就会考虑分库分表的操作,在springboot中如何实现多数据源,动态数据源切换,读写分离等操作。 当你看到这篇文件那么你幸运了,下面直接提供终极通用版代码

如果是非Mybaitis的那么可以进行参照,原理都差不多

配置文件(YML)

spring:
 datasource:
   default-db-key: voidme
   multi-db:
     - voidme:
         driver-class-name: com.mysql.cj.jdbc.Driver
         username: root
         password: root
         url: jdbc:mysql://192.168.42.153:3306/voidme?characterEncoding=utf8&autoReconnect=true&failOverReadOnly=false&maxReconnects=10&useSSL=false
     - xcdef:
         driver-class-name: com.mysql.cj.jdbc.Driver
         username: root
         password: root
         url: jdbc:mysql://192.168.42.153:3306/xcdef?characterEncoding=utf8&autoReconnect=true&failOverReadOnly=false&maxReconnects=10&useSSL=false

mybatis:
 #1.classpath:只会到你的classes路径中查找找文件。
 #2.classpath*:不仅会到classes路径,还包括jar文件中(classes路径)进行查找。
 mapper-locations: classpath*:/mapper/**/*Mapper.xml    # mapper映射文件位置
 type-aliases-package: com.**.entity    # 实体类所在的位置
 configuration:
   log-impl: org.apache.ibatis.logging.stdout.StdOutImpl   #用于控制台打印sql语句
   map-underscore-to-camel-case: true #开启将带有下划线的表字段 映射为驼峰格式的实体类属性

核心代码

SpringBoot多数据源切换实现代码(Mybaitis)

DynamicDataSource

这个类用于获取数据源的(核心)

package com.dynamicdatadource.dynamic;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

public class DynamicDataSource extends AbstractRoutingDataSource {

@Value("${spring.datasource.default-db-key}")
   private String defaultDbKey;

@Override
   protected Object determineCurrentLookupKey() {
       String currentDb = DynamicDataSourceService.currentDb();
       if (currentDb == null) {
           return defaultDbKey;
       }
       return currentDb;
   }
}

DynamicDataSourceService

这个类是数据源切换工具,我们做了线程隔离了所以不用担心多线程数据源会混乱的问题

package com.dynamicdatadource.dynamic;

import com.application.ApplicationContextProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.jdbc.DataSourceBuilder;

import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;

public class DynamicDataSourceService  {
   private static final Logger log = LoggerFactory.getLogger(DynamicDataSourceService.class);

private static final Map<Object, Object> dataSources = new HashMap<>();
   private static final ThreadLocal<String> dbKeys = ThreadLocal.withInitial(() -> null);

/**
    * 动态添加一个数据源
    *
    * @param name       数据源的key
    * @param dataSource 数据源对象
    */
   public static void addDataSource(String name, DataSource dataSource) {
       DynamicDataSource dynamicDataSource = ApplicationContextProvider.getApplicationContext().getBean(DynamicDataSource.class);
       dataSources.put(name, dataSource);
       dynamicDataSource.setTargetDataSources(dataSources);
       dynamicDataSource.afterPropertiesSet();
       log.info("添加了数据源:{}",name);
   }

/**
    * @param name   数据源的key
    * @param driverClassName  驱动
    * @param url     数据库连接地址
    * @param username   数据库账户
    * @param password   数据库密码
    */
   public static void addDataSource(String name, String driverClassName,String url,String username,String password) {
       DataSourceBuilder<?> builder = DataSourceBuilder.create();
       builder.driverClassName(driverClassName);
       builder.username(username);
       builder.password(password);
       builder.url(url);
       addDataSource(name,builder.build());
       log.info("添加了数据源:{}",name);
   }
   /**
    * 切换数据源
    */
   public static void switchDb(String dbKey) {
       dbKeys.set(dbKey);
   }

/**
    * 重置数据源(切换为默认的数据源)
    */
   public static void resetDb() {
       dbKeys.remove();
   }

/**
    * 获取当前数据源的key
    */
   public static String currentDb() {
       return dbKeys.get();
   }
}

DynamicDataSourceConfig

将数据源配置到springboot中和初始化Mybaitis配置

package com.dynamicdatadource.dynamic;

import lombok.Data;
import org.apache.ibatis.logging.Log;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

@Configuration
@ConfigurationProperties(prefix = "mybatis")
@Data
public class DynamicDataSourceConfig {

private String mapperLocations;
   private String typeAliasesPackage;
   @Data
   public class MybatisConfiguration{
       private String logImpl;
       private boolean mapUnderscoreToCamelCase;
   }
   private  MybatisConfiguration configuration=new MybatisConfiguration();

/**
    * 动态数据源
    */
   @Bean
   public DynamicDataSource dynamicDataSource() {
       DynamicDataSource dataSource = new DynamicDataSource();
       Map<Object, Object> targetDataSources = new HashMap<>();
       dataSource.setTargetDataSources(targetDataSources);
       return dataSource;
   }

/**
    * 会话工厂Mybaitis
    */
   @Bean
   public SqlSessionFactoryBean sqlSessionFactoryBean() throws IOException, ClassNotFoundException {
       org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();
       configuration.setMapUnderscoreToCamelCase(this.configuration.isMapUnderscoreToCamelCase()); //开启驼峰命名
       configuration.setLogImpl((Class<? extends Log>) Class.forName(this.configuration.getLogImpl())); //控制台打印sql日志
       SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
       sqlSessionFactoryBean.setDataSource(dynamicDataSource());
       sqlSessionFactoryBean.setConfiguration(configuration);
       PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
       sqlSessionFactoryBean.setMapperLocations(resolver.getResources(mapperLocations));
       sqlSessionFactoryBean.setTypeAliasesPackage(typeAliasesPackage);
       return sqlSessionFactoryBean;
   }

/**
    * 事务管理器
    */
   @Bean
   public PlatformTransactionManager transactionManager() {
       return new DataSourceTransactionManager(dynamicDataSource());
   }
}

加载YML数据库配置类

package com.dynamicdatadource.config;

import com.dynamicdatadource.dynamic.DynamicDataSourceService;
import lombok.Data;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.sql.DataSource;
import java.util.List;
import java.util.Map;
import java.util.Set;

@Component
@Data
@ConfigurationProperties(prefix = "spring.datasource")
public class YmlDataSourceProvider  {

private List<Map<String, DataSourceProperties>> multiDb;

private DataSource buildDataSource(DataSourceProperties prop) {
       DataSourceBuilder<?> builder = DataSourceBuilder.create();
       builder.driverClassName(prop.getDriverClassName());
       builder.username(prop.getUsername());
       builder.password(prop.getPassword());
       builder.url(prop.getUrl());
       return builder.build();
   }

public void initDataSource() {
       multiDb.forEach(map -> {
           Set<String> keys = map.keySet();
           keys.forEach(key -> {
               DataSourceProperties properties = map.get(key);
               DataSource dataSource = buildDataSource(properties);
               DynamicDataSourceService.addDataSource(key, dataSource);
           });
       });
   }

//在构造函数之后执行
   @PostConstruct
   public void init() {
       initDataSource();
   }
}

aop切换

package com.dynamicdatadource.aop;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.METHOD,ElementType.TYPE})//作用:方法和类
@Retention(RetentionPolicy.RUNTIME)
public @interface DynamicDataSourceAnno {
   String key() default "";
}

package com.dynamicdatadource.aop;
import com.dynamicdatadource.dynamic.DynamicDataSourceService;
import org.apache.commons.lang.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

// 用于单独的请求或者类进行切换数据库
@Aspect
@Component
public class DynamicDataSourceAspect {
   @Pointcut("@annotation(com.dynamicdatadource.aop.DynamicDataSourceAnno)")
   public void dynamicDataSourceAnno() {
   }

@Around("dynamicDataSourceAnno()")
   public Object DynamicDataSourceAspectAroundAnno(ProceedingJoinPoint joinPoint) {
       Object object = null;
       try {
           MethodSignature signature = (MethodSignature)joinPoint.getSignature();
           DynamicDataSourceAnno dynamicDataSourceAnno  = signature.getMethod().getAnnotation(DynamicDataSourceAnno.class);
           String key = dynamicDataSourceAnno.key();
           if (StringUtils.isNotBlank(key)) {
               //切换为指定数据库
               DynamicDataSourceService.switchDb(key);
           }
           object = joinPoint.proceed();
       } catch (Throwable e) {
           e.printStackTrace();
       }finally {
           //还原为默认配置
           DynamicDataSourceService.resetDb();
       }
       return object;
   }
   // 还可以扩展包路径切换
}

效果

运行程序之后,就会将数据源加入到数据源列表中了

SpringBoot多数据源切换实现代码(Mybaitis)

扩展

MysqlDataSourceInitialize

从数据库中将配置信息查询出来,然后动态添加到数据源列表中

package com.dao.config;

import com.dao.DatasourceDao;
import com.dynamicdatadource.aop.DynamicDataSourceAnno;
import com.dynamicdatadource.dynamic.DynamicDataSourceService;
import com.entity.DataSourceEneity;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.sql.DataSource;
import java.util.List;

//从数据库中查询出全部的数据源,添加到数据源容器中

/**
* 表结构如下:
*
* CREATE TABLE `t_datasource` (
*   `id` int(11) NOT NULL,
*   `key` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '绑定的key,用于数据源的切换',
*   `url` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '数据库连接地址',
*   `username` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '数据库用户名',
*   `password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '数据库密码',
*   `driverClassName` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '数据库驱动',
*   `type` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '数据库类型:  mysql ,oracle,..',
*   `state` int(2) NOT NULL COMMENT '是否可用: 1可用 ,2不可用',
*   PRIMARY KEY (`id`),
*   UNIQUE KEY `key` (`key`)
* ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
*
* 上表要放入到默认数据源中的数据库里才行
*/
@Component
public class MysqlDataSourceInitialize implements ApplicationRunner  {

@Autowired
   private DatasourceDao datasourceDao;

//项目启动后执行初始化数据源
   @Override
   public void run(ApplicationArguments args) throws Exception {
       try {
           List<DataSourceEneity> dataSources = datasourceDao.getDataSources();
           for (DataSourceEneity dataSource : dataSources) {
               DynamicDataSourceService.addDataSource(dataSource.getKey(),dataSource.getDataSource());
           }
       } catch (Exception e) {
           e.printStackTrace();
       }
   }
}

DataSourceEneity实体类

@Data
public class DataSourceEneity {
   private int id;
   private String key;
   private String url;
   private String username;
   private String password;
   private String driverClassName;
   private String type;
   private int state;

public  DataSource getDataSource() {
       DataSourceBuilder<?> builder = DataSourceBuilder.create();
       builder.driverClassName(driverClassName);
       builder.username(username);
       builder.password(password);
       builder.url(url);
       return  builder.build();
   }
}

来源:https://blog.csdn.net/weixin_45203607/article/details/123897803

0
投稿

猜你喜欢

  • 认识链表结构单向链表单链表在内存中的表示:可以看到,一个链表的节点包含数据域和指向下一个节点的引用,链表最后一个节点指向null(空区域)。
  • 采集器概貌,如下:最近做一个项目,功能类似于CNZZ站长统计功能,要求显示Ip所在的省份市区/提供商等信息。网上的Ip纯真数据库,下载下来一
  • 前言在我们平时使用图形化界面的时候,会发现来建立一个文件夹或者一个文档的时候很简单,只需要在桌面单击鼠标右键就可以了。但是,在我们写项目的时
  • 1. 引入依赖pom文件引入activemq依赖<!--activeMq配置-->       &
  • 一、场景描述仪器数据文件的格式包含Pdf、Word、Excel等多种,不同种格式的文件其数据的采集方式不同,因此定义仪器数据采集接口,并定义
  • 这篇文章主要介绍了Spring boot2X负载均衡和反向代理实现过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参
  • File类File类事java.io包中唯一代表磁盘文件本身的对象。File类定义了一些与平台无关的方法来操作文件,可以通过调用File类中
  • 你是一名体育老师,在某次课距离下课还有五分钟时,你决定搞一个游戏。此时有100名学生在上课。游戏的规则是:1. 你首先说出三个不同的特殊数,
  • 解决@NotBlank不生效在项目开发中,发现一个类中包含有另外一个类,这种包含关系的类上的@NotBlank校验不生效,后来发现需要在内部
  • 关于UIToolbarToolBar工具栏是视图View的属性,可以在工具栏上添加工具栏按钮Bar Button Item(可以是自定义的C
  • BigDecimal类对于不需要任何准确计算精度的数字可以直接使用float或double,但是如果需要精确计算的结果,则必须使用BigDe
  • C#剪切板Clipboard类我们现在先来看一下官方文档的介绍位于:System.Windows.Forms 命名空间下Provides m
  • 这篇文章主要介绍了Java多线程状态及方法实例解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可
  • 本程序通过JFrame实时显示本机摄像头图像,并将图像存储到一个缓冲区,当用户用鼠标点击JFrame中任何区域时,显示抓取图像的简单动画,同
  • 1.SQL注入:程序向后台数据库传递SQL时,用户提交的数据直接拼接到SQL语句中并执行,从而导入SQL注入攻击。字符型注入:黑色部分为拼接
  • 日志是非常重要的,虽然他不会以需求功能提来,但也不会体现在产品方案中。但是,它在系统项目中却占有巨大的地位。为了保证服务的高可用,发现问题一
  • Lombok简介和其他语言相比,Java经常因为不必要的冗长被批评。Lombok提供了一系列注解用以在后台生成模板代码,将其从你的类中删除,
  • 1.Overview经常研究.NET源码库的小伙伴会经常看到一个关键字volatile,那它在开发当中的作用是什么呢?我们一起来看看官方文档
  • 一、前言对于任何框架而言,在使用前都要进行一系列的初始化,MyBatis也不例外。二、MyBatis的初始化做了什么2.1 Mybatis的
  • 前言前面的例子都是多个线程在做相同的操作,比如4个线程都对共享数据做tickets–操作。大多情况下,程序中需要不同的线程做不同的事,比如一
手机版 软件编程 asp之家 www.aspxhome.com