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

其实这个表示有点不太对,应该是 Druid 动态切换数据源的方法,只是应用在了 springboot 框架中,准备代码准备了半天,之前在一次数据库迁移中使用了,发现 Druid 还是很强大的,用来做动态数据源切换很方便。

首先这里的场景跟我原来用的有点点区别,在项目中使用的是通过配置中心控制数据源切换,统一切换,而这里的例子多加了个可以根据接口注解配置

第一部分是最核心的,如何基于 Spring JDBC 和 Druid 来实现数据源切换,是继承了org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource 这个类,他的determineCurrentLookupKey方法会被调用来获得用来决定选择那个数据源的对象,也就是 lookupKey,也可以通过这个类看到就是通过这个 lookupKey 来路由找到数据源。

public class DynamicDataSource extends AbstractRoutingDataSource {

    @Override
    protected Object determineCurrentLookupKey() {
        if (DatabaseContextHolder.getDatabaseType() != null) {
            return DatabaseContextHolder.getDatabaseType().getName();
        }
        return DatabaseType.MASTER1.getName();
    }
}

而如何使用这个 lookupKey 呢,就涉及到我们的 DataSource 配置了,原来就是我们可以直接通过spring 的 jdbc 配置数据源,像这样

现在我们要使用 Druid 作为数据源了,然后配置 DynamicDataSource的参数,通过 key 来选择对应的 DataSource,也就是下面配的 master1 和 master2

<bean id="master1" class="com.alibaba.druid.pool.DruidDataSource" init-method="init"
          destroy-method="close"
          p:driverClassName="com.mysql.cj.jdbc.Driver"
          p:url="${master1.demo.datasource.url}"
          p:username="${master1.demo.datasource.username}"
          p:password="${master1.demo.datasource.password}"
          p:initialSize="5"
          p:minIdle="1"
          p:maxActive="10"
          p:maxWait="60000"
          p:timeBetweenEvictionRunsMillis="60000"
          p:minEvictableIdleTimeMillis="300000"
          p:validationQuery="SELECT 'x'"
          p:testWhileIdle="true"
          p:testOnBorrow="false"
          p:testOnReturn="false"
          p:poolPreparedStatements="false"
          p:maxPoolPreparedStatementPerConnectionSize="20"
          p:connectionProperties="config.decrypt=true"
          p:filters="stat,config"/>

    <bean id="master2" class="com.alibaba.druid.pool.DruidDataSource" init-method="init"
          destroy-method="close"
          p:driverClassName="com.mysql.cj.jdbc.Driver"
          p:url="${master2.demo.datasource.url}"
          p:username="${master2.demo.datasource.username}"
          p:password="${master2.demo.datasource.password}"
          p:initialSize="5"
          p:minIdle="1"
          p:maxActive="10"
          p:maxWait="60000"
          p:timeBetweenEvictionRunsMillis="60000"
          p:minEvictableIdleTimeMillis="300000"
          p:validationQuery="SELECT 'x'"
          p:testWhileIdle="true"
          p:testOnBorrow="false"
          p:testOnReturn="false"
          p:poolPreparedStatements="false"
          p:maxPoolPreparedStatementPerConnectionSize="20"
          p:connectionProperties="config.decrypt=true"
          p:filters="stat,config"/>

    <bean id="dataSource" class="com.nicksxs.springdemo.config.DynamicDataSource">
        <property name="targetDataSources">
            <map key-type="java.lang.String">
                <!-- master -->
                <entry key="master1" value-ref="master1"/>
                <!-- slave -->
                <entry key="master2" value-ref="master2"/>
            </map>
        </property>
        <property name="defaultTargetDataSource" ref="master1"/>
    </bean>

现在就要回到头上,介绍下这个DatabaseContextHolder,这里使用了 ThreadLocal 存放这个 DatabaseType,为啥要用这个是因为前面说的我们想要让接口层面去配置不同的数据源,要把持相互隔离不受影响,就使用了 ThreadLocal,关于它也可以看我前面写的一篇文章聊聊传说中的 ThreadLocal,而 DatabaseType 就是个简单的枚举

public class DatabaseContextHolder {
    public static final ThreadLocal<DatabaseType> databaseTypeThreadLocal = new ThreadLocal<>();

    public static DatabaseType getDatabaseType() {
        return databaseTypeThreadLocal.get();
    }

    public static void putDatabaseType(DatabaseType databaseType) {
        databaseTypeThreadLocal.set(databaseType);
    }

    public static void clearDatabaseType() {
        databaseTypeThreadLocal.remove();
    }
}
public enum DatabaseType {
    MASTER1("master1", "1"),
    MASTER2("master2", "2");

    private final String name;
    private final String value;

    DatabaseType(String name, String value) {
        this.name = name;
        this.value = value;
    }

    public String getName() {
        return name;
    }

    public String getValue() {
        return value;
    }

    public static DatabaseType getDatabaseType(String name) {
        if (MASTER2.name.equals(name)) {
            return MASTER2;
        }
        return MASTER1;
    }
}

这边可以看到就是通过动态地通过putDatabaseType设置lookupKey来进行数据源切换,要通过接口注解配置来进行设置的话,我们就需要一个注解

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface DataSource {
    String value();
}

这个注解可以配置在我的接口方法上,比如这样

public interface StudentService {

    @DataSource("master1")
    public Student queryOne();

    @DataSource("master2")
    public Student queryAnother();

}

通过切面来进行数据源的设置

@Aspect
@Component
@Order(-1)
public class DataSourceAspect {

    @Pointcut("execution(* com.nicksxs.springdemo.service..*.*(..))")
    public void pointCut() {

    }

    @Before("pointCut()")
    public void before(JoinPoint point)
    {
        Object target = point.getTarget();
        System.out.println(target.toString());
        String method = point.getSignature().getName();
        System.out.println(method);
        Class<?>[] classz = target.getClass().getInterfaces();
        Class<?>[] parameterTypes = ((MethodSignature) point.getSignature())
                .getMethod().getParameterTypes();
        try {
            Method m = classz[0].getMethod(method, parameterTypes);
            System.out.println("method"+ m.getName());
            if (m.isAnnotationPresent(DataSource.class)) {
                DataSource data = m.getAnnotation(DataSource.class);
                System.out.println("dataSource:"+data.value());
                DatabaseContextHolder.putDatabaseType(DatabaseType.getDatabaseType(data.value()));
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @After("pointCut()")
    public void after() {
    DatabaseContextHolder.clearDatabaseType();
    }
}

通过接口判断是否带有注解跟是注解的值,DatabaseType 的配置不太好,不过先忽略了,然后在切点后进行清理

这是我 master1 的数据,

master2 的数据

然后跑一下简单的 demo,

@Override
public void run(String...args) {
 LOGGER.info("run here");
 System.out.println(studentService.queryOne());
 System.out.println(studentService.queryAnother());

}

看一下运行结果

其实这个方法应用场景不止可以用来迁移数据库,还能实现精细化的读写数据源分离之类的,算是做个简单记录和分享。

总结

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

(0)

相关推荐

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

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

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

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

  • SpringBoot Mybatis动态数据源切换方案实现过程

    背景 最近让我做一个大数据的系统,分析了一下,麻烦的地方就是多数据源切换抽取数据.考虑到可以跨服务器跨数据库抽数,再整理数据,就配置了这个动态数据源的解决方案.在此分享给大家. 实现方案 数据库配置文件 我们项目使用的是yml形式的配置文件,采用的是hikari的数据库连接池.第一步我们自然是配置多个数据库源头. 我们找到spring的datasource,在下方配置三个数据源. spring: application: name: dynamicDatasource datasource: t

  • spring boot动态切换数据源的实现

    当数据量比较大的时候,我们就需要考虑读写分离了,也就是动态切换数据库连接,对指定的数据库进行操作.在spring中实现动态的切换无非就是利用AOP实现.我们可以使用mybatis-plus作者开发的插件dynamic-datasource-spring-boot-starter. demo地址:https://github.com/songshijun1995/spring-boot-dynamic-demo 新建项目引入依赖 <dependency> <groupId>com.b

  • spring boot + mybatis实现动态切换数据源实例代码

    前言 前几天有个需求,需要使用不同的数据源,例如某业务要用A数据源,另一个业务要用B数据源.我上网收集了一些资料整合了一下,虽然最后这个需求不了了之了,但是多数据源动态切换还是蛮好用的,所以记录一下,或许以后有用呢?或者自己感兴趣又想玩呢! 下面话不多说了,随着小编来一起看看详细的介绍吧 方法如下: 1.加个依赖 <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybat

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

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

  • Spring+Mybatis动态切换数据源的方法

    功能需求是公司要做一个大的运营平台: 1.运营平台有自身的数据库,维护用户.角色.菜单.部分以及权限等基本功能. 2.运营平台还需要提供其他不同服务(服务A,服务B)的后台运营,服务A.服务B的数据库是独立的. 所以,运营平台至少要连三个库:运营库,A库,B库,并且希望达到针对每个功能请求能够自动切换到对应的数据源(我最终实现是针对Service的方法级别进行切换的,也可以实现针对每个DAO层的方法进行切换.我们系统的功能是相互之间比较独立的). 第一步:配置多数据源 1.定义数据源: 我采用的

  • spring-data-redis 动态切换数据源的方法

    最近遇到了一个麻烦的需求,我们需要一个微服务应用同时访问两个不同的 Redis 集群.一般我们不会这么使用 Redis,但是这两个 Redis 本来是不同业务集群,现在需要一个微服务同时访问. 其实我们在实际业务开发的时候,可能还会遇到类似的场景.例如 Redis 读写分离,这个也是 spring-data-redis 没有提供的功能,底层连接池例如 Lettuce 或者 Jedis 都提供了获取只读连接的 API,但是缺陷有两个: 上层 spring-data-redis 并没有封装这种接口

  • SpringBoot 自定义+动态切换数据源教程

    目录 1.添加maven依赖 2.配置application.yml 3.配置动态数据源 4.配置数据源操作Holder 5.读取自定义数据源,并配置 6.动态切换关键--AOP进行切换 7.使用 1).配置mapper 2).配置service 3).单元测试调用 4).测试结果 1.添加maven依赖 <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</ar

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

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

  • Springboot动态切换数据源的具体实现与原理分析

    目录 前言 具体实现: 原理分析: 总结 前言 在springboot项目中只需一句代码即可实现多个数据源之间的切换: // 切换sqlserver数据源: DataSourceContextHolder.setDataBaseType(DataSourceEnum.SQLSERVER_DATASOURCE); ...... // 切换mysql数据源 DataSourceContextHolder.setDataBaseType(DataSourceEnum.MYSQL_DATASOURCE)

  • 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数据库,数据库

  • 亲手教你SpringBoot中的多数据源集成问题

    目录 引言 应用场景 集成方案 引言 其实对于分库分表这块的场景,目前市场上有很多成熟的开源中间件,eg:MyCAT,Cobar,sharding-JDBC等. 本文主要是介绍基于springboot的多数据源切换,轻量级的一种集成方案,对于小型的应用可以采用这种方案,我之前在项目中用到是因为简单,便于扩展以及优化. 应用场景 假设目前我们有以下几种数据访问的场景:1.一个业务逻辑中对不同的库进行数据的操作(可能你们系统不存在这种场景,目前都时微服务的架构,每个微服务基本上是对应一个数据库比较多

  • Spring + Mybatis 项目实现动态切换数据源实例详解

    项目背景:项目开发中数据库使用了读写分离,所有查询语句走从库,除此之外走主库. 最简单的办法其实就是建两个包,把之前数据源那一套配置copy一份,指向另外的包,但是这样扩展很有限,所有采用下面的办法. 参考了两篇文章如下: http://www.jb51.net/article/111840.htm http://www.jb51.net/article/111842.htm 这两篇文章都对原理进行了分析,下面只写自己的实现过程其他不再叙述. 实现思路是: 第一步,实现动态切换数据源:配置两个D

随机推荐