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

目录
  • 前言
  • 配置文件(YML)
  • 核心代码
    • DynamicDataSource
    • DynamicDataSourceService
    • DynamicDataSourceConfig
    • 加载YML数据库配置类
    • aop切换
  • 效果
  • 扩展
    • MysqlDataSourceInitialize
    • DataSourceEneity实体类
  • 总结

前言

但是在实际业务场景中,数据量迅速增长,一个库一个表已经满足不了我们的需求的时候,我们就会考虑分库分表的操作,在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 #开启将带有下划线的表字段 映射为驼峰格式的实体类属性

核心代码

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;
    }
    // 还可以扩展包路径切换
}

效果

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

扩展

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();
    }
}

总结

到此这篇关于SpringBoot多数据源切换实现的文章就介绍到这了,更多相关SpringBoot多数据源切换内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

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

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

  • Spring Boot 动态数据源示例(多数据源自动切换)

    本文实现案例场景: 某系统除了需要从自己的主要数据库上读取和管理数据外,还有一部分业务涉及到其他多个数据库,要求可以在任何方法上可以灵活指定具体要操作的数据库. 为了在开发中以最简单的方法使用,本文基于注解和AOP的方法实现,在spring boot框架的项目中,添加本文实现的代码类后,只需要配置好数据源就可以直接通过注解使用,简单方便. 一配置二使用 1. 启动类注册动态数据源 2. 配置文件中配置多个数据源 3. 在需要的方法上使用注解指定数据源 1.在启动类添加 @Import({Dyna

  • SpringBoot +DynamicDataSource切换多数据源的全过程

    目录 固定多个数据源切换 1.由于我这个版本的自带DynamicDataSource包 2.在yml中配置两个数据源 3.启动时启动连接池的方法获取 4.载入各个连接池 5.dynamicDataSource类中 6.在切面中执行切换操作 刚开始用一个数据源,但是上头要求要做多数据源切换的.看了框架有自带的可以切数据源但是数据源是固定在YML文件的: 固定多个数据源切换 1.由于我这个版本的自带DynamicDataSource包 我就不引入了. 各位看官缺包自行去maven上查Dynamic-

  • Springboot项目实现Mysql多数据源切换的完整实例

    一.分析AbstractRoutingDataSource抽象类源码 关注import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource以下变量 @Nullable private Map<Object, Object> targetDataSources; // 目标数据源 @Nullable private Object defaultTargetDataSource; // 默认目标数据源 @Null

  • springboot多数据源配置及切换的示例代码详解

    注:本文的多数据源配置及切换的实现方法是,在框架中封装,具体项目中配置及使用,也适用于多模块项目 配置文件数据源读取 通过springboot的Envioment和Binder对象进行读取,无需手动声明DataSource的Bean yml数据源配置格式如下: spring: datasource: master: type: com.alibaba.druid.pool.DruidDataSource driverClassName: com.mysql.cj.jdbc.Driver url:

  • SpringBoot实现多数据源的切换实践

    目录 前言 工程结构 编码实现 yml文件 主数据源MainDatasourceProperties 其他数据源DynamicDatasourceProperties 数据源配置类DatasourceConfiguration DatasourceChooser DatasourceContext 数据源注解DatasourceScope 数据源切面DynamicDatasourceAspect 使用 前言 我们在进行软件开发的过程中,刚开始的时候因为无法估量系统后期的访问量和并发量,所以一开始

  • SpringBoot AOP方式实现多数据源切换的方法

    最近在做保证金余额查询优化,在项目启动时候需要把余额全量加载到本地缓存,因为需要全量查询所有骑手的保证金余额,为了不影响主数据库的性能,考虑把这个查询走从库.所以涉及到需要在一个项目中配置多数据源,并且能够动态切换.经过一番摸索,完美实现动态切换,记录一下配置方法供大家参考. 设计总体思路 Spring-Boot+AOP方式实现多数据源切换,继承AbstractRoutingDataSource实现数据源动态的获取,在service层使用注解指定数据源. 步骤 一.多数据源配置 在applica

  • springboot中mybatis多数据源动态切换实现

    目录 多数据源配置引入 动态数据源路由实现 动态数据源切换使用 案例源码 在开发中,动态数据源配置还是用的比较多的,比如在多数据源使用方面,又或者是在多个DB之间切换方面.这里给出一个动态数据源的配置方案,两个DB均以mysql为例. 多数据源配置引入 mybatis和mysql在springboot中的引入这里就不在说了,不了解的可以参见springboot中mysql与mybatis的引入. 数据源配置如下: datasource: master: type: com.alibaba.dru

  • spring boot多数据源动态切换代码实例

    这篇文章主要介绍了spring boot多数据源动态切换代码实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 当项目中存在多数据源时,就涉及到数据源的动态切换,通过研究,特此记录一下. 1.maven依赖 <!--数据库连接--> <dependency> <groupId>com.oracle</groupId> <artifactId>ojdbc6</artifactId> &

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

    目录 前言 配置文件(YML) 核心代码 DynamicDataSource DynamicDataSourceService DynamicDataSourceConfig 加载YML数据库配置类 aop切换 效果 扩展 MysqlDataSourceInitialize DataSourceEneity实体类 总结 前言 但是在实际业务场景中,数据量迅速增长,一个库一个表已经满足不了我们的需求的时候,我们就会考虑分库分表的操作,在springboot中如何实现多数据源,动态数据源切换,读写分

  • Mybatis多数据源切换实现代码

    这次要完成的是从一个数据库中读取数据,然后再把数据插入到另一个数据库中.在同一套项目代码中要完成这个操作,就不可避免的涉及到了多数据源.本文即介绍在mybatis中完成多数据源的切换相关内容 指定数据源一 @Configuration // 扫描 Mapper 接口并容器管理 @MapperScan(basePackages = MasterDataSourceConfig.PACKAGE, sqlSessionFactoryRef = "masterSqlSessionFactory"

  • SpringBoot多数据源配置详细教程(JdbcTemplate、mybatis)

    多数据源配置 首先是配置文件 这里采用yml配置文件,其他类型配置文件同理 我配置了两个数据源,一个名字叫ds1数据源,一个名字叫ds2数据源,如果你想配置更多的数据源,继续加就行了 spring: # 数据源配置 datasource: ds1: #数据源1 driver-class-name: com.mysql.jdbc.Driver # mysql的驱动你可以配置别的关系型数据库 url: jdbc:mysql://ip:3306/db1 #数据源地址 username: root #

  • 详解SpringBoot+Mybatis实现动态数据源切换

    业务背景 电商订单项目分正向和逆向两个部分:其中正向数据库记录了订单的基本信息,包括订单基本信息.订单商品信息.优惠卷信息.发票信息.账期信息.结算信息.订单备注信息.收货人信息等:逆向数据库主要包含了商品的退货信息和维修信息.数据量超过500万行就要考虑分库分表和读写分离,那么我们在正向操作和逆向操作的时候,就需要动态的切换到相应的数据库,进行相关的操作. 解决思路 现在项目的结构设计基本上是基于MVC的,那么数据库的操作集中在dao层完成,主要业务逻辑在service层处理,controll

  • 详细聊聊SpringBoot中动态切换数据源的方法

    其实这个表示有点不太对,应该是 Druid 动态切换数据源的方法,只是应用在了 springboot 框架中,准备代码准备了半天,之前在一次数据库迁移中使用了,发现 Druid 还是很强大的,用来做动态数据源切换很方便. 首先这里的场景跟我原来用的有点点区别,在项目中使用的是通过配置中心控制数据源切换,统一切换,而这里的例子多加了个可以根据接口注解配置 第一部分是最核心的,如何基于 Spring JDBC 和 Druid 来实现数据源切换,是继承了org.springframework.jdbc

  • 基于mybatis plus实现数据源动态添加、删除、切换,自定义数据源的示例代码

    目录 简介 代码示例 mavne依赖 数据源增加.移除 数据源切换 基于AOP切换 基于重写处理器 自定义数据源 简介 基于springboot,mybatis plus集成了一套多数据源的解决方案,在使用时引入相应的插件dynamic-datasource-spring-boot-starter,可以实现数据源的动态添加.删除等功能,对于多租户或者分库等操作可以根据AOP切面代理到不同的数据源.实现单一系统数据隔离的目的. 代码示例 mavne依赖 <!--mybatis-plus--> &

  • SpringBoot多数据源配置并通过注解实现动态切换数据源

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

  • Java注解实现动态数据源切换的实例代码

    当一个项目中有多个数据源(也可以是主从库)的时候,我们可以利用注解在mapper接口上标注数据源,从而来实现多个数据源在运行时的动态切换. 实现原理 在Spring 2.0.1中引入了AbstractRoutingDataSource, 该类充当了DataSource的路由中介, 能有在运行时, 根据某种key值来动态切换到真正的DataSource上. 看下AbstractRoutingDataSource: 复制代码 代码如下: public abstract class AbstractR

随机推荐