springboot进阶学习(六)springboot多数据源

导读:本篇文章讲解 springboot进阶学习(六)springboot多数据源,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

springboot多数据源

本文简单的介绍一下基于SpringBoot框架动态多数据源切换的实现,我们创建了3个数据库用于测试,master、slave01两个数据库是mysql,slave02是sqlserver数据库。

引入相关依赖

引入的依赖是干什么的,大家看注释就行。

<dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    
    <!-- 添加mybatis依赖 -->
    <dependency>
      <groupId>org.mybatis.spring.boot</groupId>
      <artifactId>mybatis-spring-boot-starter</artifactId>
      <version>1.3.5</version>
    </dependency>
    <!--添加mysql驱动-->
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.47</version>
      <scope>runtime</scope>
    </dependency>
    <!--sqlserver-->
    <dependency>
      <groupId>com.microsoft.sqlserver</groupId>
      <artifactId>mssql-jdbc</artifactId>
      <version>6.4.0.jre8</version>
    </dependency>
    <!--添加数据库连接池 -->
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>druid-spring-boot-starter</artifactId>
      <version>1.1.16</version>
    </dependency>
    <!--开启aop所需的依赖 -->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-aop</artifactId>
    </dependency>
    
    <!--代码简化工具-->
    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <version>1.18.8</version>
      <scope>provided</scope>
    </dependency>
</dependencies>

配置数据库信息

application.yml配置3个数据库信息的信息

server:
  port: 8088 # 配置端口
  servlet:
      context-path: /moyundong # 配置项目名称
# 配置数据源
spring:
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    # 主库数据源
    master:
      driver-class-name: com.mysql.jdbc.Driver
      url: jdbc:mysql://127.0.0.1:1433:3306/springbootdata
      username: root
      password: root
    # 从库数据源
    slave01:
      enabled: true
      driver-class-name: com.mysql.jdbc.Driver
      url: jdbc:mysql://127.0.0.1:1433:3306/dh
      username: root
      password: root
    # 测试其它不同类型数据库
    slave02:
      enabled: true
      driver-class-name: com.microsoft.sqlserver.jdbc.SQLServerDriver
      url: jdbc:sqlserver://127.0.0.1:1433;DatabaseName=dh-localtest
      username: sa
      password: root

# 配置数据源
mybatis:
  mapper-locations: classpath:com/mapper/*.xml
  type-aliases-package: com.moyundong.entity # 默认别名就是类名,或者类名首字母小写 SysUser | sysUser

数据源枚举类

/**
 * 创建数据源枚举类
 */
public enum DataSourceType {
    /**
     * 主库
     */
    MASTER,
    /**
     * 从库01
     */
    SLAVE01,
    /**
     * 从库02
     */
    SLAVE02
}

数据源切换

/**
 * 数据源切换处理类,有对数据源变量的获取、设置和清空的方法
 */
@Slf4j
public class DataSourceContextHolder {
    /**
     * 使用ThreadLocal维护变量,ThreadLocal为每个使用该变量的线程提供独立的变量副本,
     *  所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。
     */
    private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>();

    /**
     * 设置数据源变量
     * @param dataSourceType
     */
    public static void setDataSourceType(String dataSourceType){
        log.info("切换到{}数据源", dataSourceType);
        CONTEXT_HOLDER.set(dataSourceType);
    }

    /**
     * 获取数据源变量
     * @return
     */
    public static String getDataSourceType(){
        return CONTEXT_HOLDER.get();
    }

    /**
     * 清空数据源变量
     */
    public static void clearDataSourceType(){
        CONTEXT_HOLDER.remove();
    }
}

动态数据源

public class DynamicDataSource extends AbstractRoutingDataSource {
    /**
     * 根据Key获取数据源的信息
     * @return
     */
    @Override
    protected Object determineCurrentLookupKey() {
        return DataSourceContextHolder.getDataSourceType();
    }
}

数据源配置

@Configuration
public class DataSourceConfig {
    @Bean
    @ConfigurationProperties("spring.datasource.master")
    public DataSource masterDataSource() {
        //return DataSourceBuilder.create().build();如果用这个创建的话,使用的是HikariPool连接池,数据库配置文件里面的url要写成jdbc-url
        return DruidDataSourceBuilder.create().build();
    }

    @Bean
    @ConfigurationProperties("spring.datasource.slave01")
    @ConditionalOnProperty(prefix = "spring.datasource.slave01", name = "enabled", havingValue = "true")
    public DataSource slave01DataSource() {
        return DruidDataSourceBuilder.create().build();
    }

    @Bean
    @ConfigurationProperties("spring.datasource.slave02")
    @ConditionalOnProperty(prefix = "spring.datasource.slave02", name = "enabled", havingValue = "true")
    public DataSource slave02DataSource() {
        return DruidDataSourceBuilder.create().build();
    }

    @Bean(name = "dynamicDataSource")
    @Primary
    public DynamicDataSource dataSource() {
        DynamicDataSource dynamicDataSource = new DynamicDataSource();
        //配置默认数据源
        dynamicDataSource.setDefaultTargetDataSource(masterDataSource());
        //配置多数据源
        Map<Object, Object> targetDataSources = new HashMap<>();
        targetDataSources.put(DataSourceType.MASTER.name(), masterDataSource());
        targetDataSources.put(DataSourceType.SLAVE01.name(), slave01DataSource());
        targetDataSources.put(DataSourceType.SLAVE02.name(), slave02DataSource());
        dynamicDataSource.setTargetDataSources(targetDataSources);
        return dynamicDataSource;
    }
}

这里要注意,创建DataSource的时候有两种方式

  • 使用DataSourceBuilder .create().build();那么意味着使用的是HikariPool连接池,
    就算在application.yml文件里面配置的druid也还是用的HikariPool,这时候数据库连接的url要写成jdbc-url
  • 使用DruidDataSourceBuilder .create().build(); 使用的就是druid的连接池,这时候数据库连接的url就要用url

自定义数据源切换注解

自定义一个数据源切换的注解,方便在方法上使用。默认是MASTER类型数据库

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSourceRegister {
    /**
     * 切换数据源名称
     */
    DataSourceType value() default DataSourceType.MASTER;
}

AOP拦截实现切换

定义一个拦截类,拦截DataSourceRegister注解,根据注解的参数类型切换数据源,然后执行方法。

@Aspect
@Order(1)
@Component
public class DynamicDataSourceAspect {

    /**
     * 定义切点Pointcut
     */
    @Pointcut("@annotation(com.moyundong.dynamicdata.DataSourceRegister)")
    public void excudeService() {

    }

    /**
     *
     * @param point
     * @return
     * @throws Throwable
     */
    @Around("excudeService()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();
        DataSourceRegister dataSource = method.getAnnotation(DataSourceRegister.class);
        if (dataSource != null) {
            DataSourceContextHolder.setDataSourceType(dataSource.value().name());
        }
        try {
            return point.proceed();
        } finally {
            // 销毁数据源 在执行方法之后
            DataSourceContextHolder.clearDataSourceType();
        }
    }
}

测试service

我们在service里面写了3个查询方法,分别查询不同的数据库,为了测试方便,3个数据库的表我都用的是一样的sys_user

@Override
public List<SysUser> findAllFromMaster() {
    return sysUserDao.findAll();
}

@Override
@DataSourceRegister(value = DataSourceType.SLAVE01)
public List<SysUser> findAllFromSlave01() {
    return sysUserDao.findAll();
}

@Override
@DataSourceRegister(value = DataSourceType.SLAVE02)
public List<SysUser> findAllFromSlave02() {
    return sysUserDao.findAll();
}

测试controller

测试方法里面,根据请求的参数type的不同,调用不同的方法查询数据。

@RequestMapping("findAll/{type}")
public Object findAll(@PathVariable(name = "type") String type){
    if (DataSourceType.SLAVE01.name().equals(type)){
        return sysUserService.findAllFromSlave01();
    } else if(DataSourceType.SLAVE02.name().equals(type)){
        return sysUserService.findAllFromSlave02();
    } else{
        return sysUserService.findAllFromMaster();
    }

}

::: tip 提示
其它业务逻辑的代码就不写了,大家可以自己写,也可以下载整个工程文件看看。
:::

启动类

启动的时候报The dependencies of some of the beans in the application context form a cycle错误,意思就是依赖形成了循环,解决
办法就是在启动类加上@SpringBootApplication(exclude={DataSourceAutoConfiguration.class})@Import({DataSourceConfig.class})

@Import({DataSourceConfig.class})
@SpringBootApplication(exclude={DataSourceAutoConfiguration.class})
@MapperScan("com.moyundong.dao")
public class Springboot2Test06Application extends SpringBootServletInitializer {
    public static void main(String[] args)  {
        SpringApplication.run(Springboot2Test06Application.class, args);
    }
}

测试结果

启动工程后,在浏览器分别运行下面3个连接,会分别从3个不同的数据库查询数据,为了查看效果,每个数据库我们最好值都不一样。

  • http://127.0.0.1:8088/moyundong/sysUser/findAll/MASTER
  • http://127.0.0.1:8088/moyundong/sysUser/findAll/SLAVE01
  • http://127.0.0.1:8088/moyundong/sysUser/findAll/SLAVE02

使用DataSourceBuilder .create().build();创建运行结果如下:

2020-06-10 23:24:20.933  INFO 40560 --- [nio-8088-exec-4] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Starting...
2020-06-10 23:24:21.388  INFO 40560 --- [nio-8088-exec-4] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Start completed.
2020-06-10 23:24:43.685  INFO 40560 --- [nio-8088-exec-8] c.m.dynamicdata.DataSourceContextHolder  : 切换到SLAVE01数据源
2020-06-10 23:24:43.686  INFO 40560 --- [nio-8088-exec-8] com.zaxxer.hikari.HikariDataSource       : HikariPool-2 - Starting...
2020-06-10 23:24:44.285  INFO 40560 --- [nio-8088-exec-8] com.zaxxer.hikari.HikariDataSource       : HikariPool-2 - Start completed.
2020-06-10 23:24:53.192  INFO 40560 --- [nio-8088-exec-9] c.m.dynamicdata.DataSourceContextHolder  : 切换到SLAVE02数据源
2020-06-10 23:24:53.192  INFO 40560 --- [nio-8088-exec-9] com.zaxxer.hikari.HikariDataSource       : HikariPool-3 - Starting...
2020-06-10 23:24:53.940  INFO 40560 --- [nio-8088-exec-9] com.zaxxer.hikari.HikariDataSource       : HikariPool-3 - Start completed.

使用DruidDataSourceBuilder .create().build();创建运行结果如下:

2020-06-10 23:54:39.380  INFO 45456 --- [nio-8088-exec-4] c.m.dynamicdata.DataSourceContextHolder  : 切换到SLAVE01数据源
2020-06-10 23:54:39.445  INFO 45456 --- [nio-8088-exec-4] com.alibaba.druid.pool.DruidDataSource   : {dataSource-1} inited
2020-06-10 23:54:48.864  INFO 45456 --- [io-8088-exec-10] c.m.dynamicdata.DataSourceContextHolder  : 切换到SLAVE02数据源
2020-06-10 23:54:48.866  INFO 45456 --- [io-8088-exec-10] com.alibaba.druid.pool.DruidDataSource   : {dataSource-2} inited
2020-06-10 23:54:54.538  INFO 45456 --- [nio-8088-exec-2] com.alibaba.druid.pool.DruidDataSource   : {dataSource-3} inited

1介绍
2springboot定时任务
3springboot定时任务配置详解
4springboot动态定时任务
5springboot集成websocket
6springboot多数据源
7springboot配置druid监听
8springboot自定义注解
9springboot常见注解详解
10springboot接收参数详解
11springboot验证机制@Valid和@Validated
12springboot集成Swagger2
13springboot集成swagger-bootstrap-ui
14springboot集成shiro
15springboot集成shiro(二)
16springboot集成jwt
17springboot集成ActiveMQ
18springboot缓存机制

🍉🍉🍉 欢迎大家来博客了解更多内容:java乐园 🍉🍉🍉

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/13510.html

(0)
小半的头像小半

相关推荐

极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!