当前位置:   article > 正文

从0到1学SpringCloud——09 多数据源配置及动态切换_springcloud多数据源配置

springcloud多数据源配置

目录

一、前言

二、数据准备

三、配置文件

1、pom依赖

2、appication.properties配置

四、源码实现

1、数据源注解

2、数据源枚举值

3、配置多个数据源bean

4、切层注入

5、创建DynamicDataSource

6、ThreadLocal临时存储数据源

7、Controller访问

8、接口查询及数据获取

8、启动类

五、启动测试

1、访问master数据库

2、访问slave数据库

3、修改开关配置


一、前言

本篇主要介绍springboot如何进行多数据源配置,及一些设计思路。

主要包含以下内容:

1、多数据源通过注解动态切换

2、通过配置文件开关,只启用一个数据源

3、springboot中使用多数据源存在循环引用的问题

二、数据准备

为了方便演示,在两台服务器上创建两个数据库,分别为master和slave

在master数据库创建表z_f_user,表结构及数据如上图。

在slave数据库创建表z_f_msg,表结构及数据如上图。 

三、配置文件

1、pom依赖

  1. <dependencies>
  2. <dependency>
  3. <groupId>mysql</groupId>
  4. <artifactId>mysql-connector-java</artifactId>
  5. <scope>runtime</scope>
  6. </dependency>
  7. <!--druid -->
  8. <dependency>
  9. <groupId>com.alibaba</groupId>
  10. <artifactId>druid</artifactId>
  11. <version>1.2.11</version>
  12. </dependency>
  13. <dependency>
  14. <groupId>org.mybatis.spring.boot</groupId>
  15. <artifactId>mybatis-spring-boot-starter</artifactId>
  16. <version>2.2.2</version>
  17. </dependency>
  18. <dependency>
  19. <groupId>org.aspectj</groupId>
  20. <artifactId>aspectjweaver</artifactId>
  21. </dependency>
  22. </dependencies>

2、appication.properties配置

  1. #服务端口
  2. server.port=8080
  3. #服务名称
  4. spring.application.name=zhufeng-web-msg
  5. #数据源公共配置
  6. spring.datasource.druid.type=com.alibaba.druid.pool.DruidDataSource
  7. spring.datasource.druid.driver-class-name=com.mysql.jdbc.Driver
  8. # master数据源配置
  9. spring.datasource.druid.dynamic.master.jdbc-url=jdbc:mysql://10.211.55.2:3306/master?serverTimezone=UTC&useUnicode=true@characterEncoding=utf-8
  10. spring.datasource.druid.dynamic.master.username=root
  11. spring.datasource.druid.dynamic.master.password=123
  12. # slave数据源配置
  13. spring.datasource.druid.dynamic.slave.jdbc-url=jdbc:mysql://10.211.55.14:3306/slave?serverTimezone=UTC&useUnicode=true@characterEncoding=utf-8
  14. spring.datasource.druid.dynamic.slave.username=root
  15. spring.datasource.druid.dynamic.slave.password=123
  16. #是否启动slave数据源开关,true:启用。默认为false
  17. spring.datasource.druid.dynamic.slave.enabled=true
  18. mybatis.type-aliases-package=com.zhufeng.web.mapper
  19. mybatis.mapper-locations=classpath:mapper/*.xml

四、源码实现

1、数据源注解

  1. package com.zhufeng.base.db;
  2. import java.lang.annotation.*;
  3. /**
  4. * @ClassName: DataSource
  5. * @Description 数据源
  6. * @author 月夜烛峰
  7. * @date 2022/9/5 20:33
  8. */
  9. @Target(ElementType.METHOD)
  10. @Retention(RetentionPolicy.RUNTIME)
  11. @Documented
  12. public @interface DataSource {
  13. /**
  14. *
  15. * 切换数据源名称
  16. */
  17. DataSourceType value() default DataSourceType.MASTER;
  18. }

2、数据源枚举值

  1. package com.zhufeng.base.db;
  2. /**
  3. * @ClassName: DataSourceType
  4. * @Description 数据源枚举值
  5. * @author 月夜烛峰
  6. * @date 2022/9/5 20:33
  7. */
  8. public enum DataSourceType {
  9. MASTER,
  10. SLAVE
  11. }

3、配置多个数据源bean

  1. package com.zhufeng.base.db;
  2. import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
  3. import org.springframework.boot.context.properties.ConfigurationProperties;
  4. import org.springframework.boot.jdbc.DataSourceBuilder;
  5. import org.springframework.context.annotation.Bean;
  6. import org.springframework.context.annotation.Configuration;
  7. import org.springframework.context.annotation.Primary;
  8. import javax.sql.DataSource;
  9. import java.util.HashMap;
  10. import java.util.Map;
  11. /**
  12. * @ClassName: DataSourceConfig
  13. * @Description 配置多数据源bean,默认使用master数据源
  14. * @author 月夜烛峰
  15. * @date 2022/9/5 20:33
  16. */
  17. @Configuration
  18. public class DataSourceConfig {
  19. @Bean
  20. @ConfigurationProperties("spring.datasource.druid.dynamic.master")
  21. public DataSource masterDataSource() {
  22. return DataSourceBuilder.create().build();
  23. }
  24. @Bean
  25. @ConfigurationProperties("spring.datasource.druid.dynamic.slave")
  26. @ConditionalOnProperty(prefix = "spring.datasource.druid.dynamic.slave", name = "enabled", havingValue = "true")
  27. public DataSource slaveDataSource() {
  28. return DataSourceBuilder.create().build();
  29. }
  30. @Bean(name = "dynamicDataSource")
  31. @Primary
  32. public DynamicDataSource dataSource(DataSource masterDataSource,DataSource slaveDataSource) {
  33. Map<Object, Object> targetDataSources = new HashMap<>(8);
  34. targetDataSources.put(DataSourceType.MASTER.name(), masterDataSource);
  35. targetDataSources.put(DataSourceType.SLAVE.name(), slaveDataSource);
  36. return new DynamicDataSource(masterDataSource, targetDataSources);
  37. }
  38. }

4、切层注入

  1. package com.zhufeng.base.db;
  2. import org.aspectj.lang.ProceedingJoinPoint;
  3. import org.aspectj.lang.annotation.Around;
  4. import org.aspectj.lang.annotation.Aspect;
  5. import org.aspectj.lang.annotation.Pointcut;
  6. import org.aspectj.lang.reflect.MethodSignature;
  7. import org.springframework.core.annotation.Order;
  8. import org.springframework.stereotype.Component;
  9. import java.lang.reflect.Method;
  10. /**
  11. * @ClassName: DataSourceAspect
  12. * @Description spring切层注入
  13. * @author 月夜烛峰
  14. * @date 2022/9/5 20:33
  15. */
  16. @Aspect
  17. @Order(1)
  18. @Component
  19. public class DataSourceAspect {
  20. @Pointcut("@annotation(com.zhufeng.base.db.DataSource)")
  21. public void dsPointCut() {
  22. }
  23. @Around("dsPointCut()")
  24. public Object around(ProceedingJoinPoint point) throws Throwable {
  25. MethodSignature signature = (MethodSignature) point.getSignature();
  26. Method method = signature.getMethod();
  27. DataSource dataSource = method.getAnnotation(DataSource.class);
  28. if (dataSource != null) {
  29. DynamicDataSourceContextHolder.setDataSourceType(dataSource.value().name());
  30. }
  31. try {
  32. return point.proceed();
  33. } finally {
  34. // 销毁数据源 在执行方法之后
  35. DynamicDataSourceContextHolder.clearDataSourceType();
  36. }
  37. }
  38. }

5、创建DynamicDataSource

  1. package com.zhufeng.base.db;
  2. import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
  3. import javax.sql.DataSource;
  4. import java.util.Map;
  5. /**
  6. * @ClassName: DynamicDataSource
  7. * @Description 动态数据源创建
  8. * @author 月夜烛峰
  9. * @date 2022/9/5 20:33
  10. */
  11. public class DynamicDataSource extends AbstractRoutingDataSource {
  12. public DynamicDataSource(DataSource defaultTargetDataSource, Map<Object, Object> targetDataSources) {
  13. super.setDefaultTargetDataSource(defaultTargetDataSource);
  14. super.setTargetDataSources(targetDataSources);
  15. super.afterPropertiesSet();
  16. }
  17. /**
  18. * 根据Key获取数据源的信息
  19. *
  20. * @return
  21. */
  22. @Override
  23. protected Object determineCurrentLookupKey() {
  24. return DynamicDataSourceContextHolder.getDataSourceType();
  25. }
  26. }

6、ThreadLocal临时存储数据源

  1. package com.zhufeng.base.db;
  2. /**
  3. * @ClassName: DynamicDataSourceContextHolder
  4. * @Description 数据源临时处理
  5. * @author 月夜烛峰
  6. * @date 2022/9/5 20:33
  7. */
  8. public class DynamicDataSourceContextHolder {
  9. /**
  10. * 使用ThreadLocal临时存储
  11. */
  12. private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>();
  13. /**
  14. * 设置数据源变量
  15. * @param dataSourceType
  16. */
  17. public static void setDataSourceType(String dataSourceType){
  18. CONTEXT_HOLDER.set(dataSourceType);
  19. }
  20. /**
  21. * 获取数据源变量
  22. * @return
  23. */
  24. public static String getDataSourceType(){
  25. return CONTEXT_HOLDER.get();
  26. }
  27. /**
  28. * 清空数据源变量
  29. */
  30. public static void clearDataSourceType(){
  31. CONTEXT_HOLDER.remove();
  32. }
  33. }

7、Controller访问

  1. package com.zhufeng.web.controller;
  2. import com.alibaba.fastjson.JSONObject;
  3. import com.zhufeng.base.db.DataSource;
  4. import com.zhufeng.base.db.DataSourceType;
  5. import com.zhufeng.web.service.MsgService;
  6. import org.springframework.web.bind.annotation.RequestMapping;
  7. import org.springframework.web.bind.annotation.RestController;
  8. import javax.annotation.Resource;
  9. /**
  10. * @ClassName: DataController
  11. * @Description 多数据源切换演示
  12. * @author 月夜烛峰
  13. * @date 2022/9/5 20:40
  14. */
  15. @RestController
  16. @RequestMapping("data")
  17. public class DataController {
  18. @Resource
  19. MsgService msgService;
  20. @RequestMapping("master")
  21. public JSONObject showUser(){
  22. return msgService.showFirstUser();
  23. }
  24. @RequestMapping("slave")
  25. public JSONObject showMsg(){
  26. return msgService.showFirstMsg();
  27. }
  28. }

8、接口查询及数据获取

service接口:

  1. /**
  2. * @ClassName: MsgService
  3. * @Description TODO
  4. * @author 月夜烛峰
  5. * @date 2022/9/5 20:06
  6. */
  7. public interface MsgService {
  8. /**
  9. * 获取第一条消息信息
  10. * @return
  11. */
  12. JSONObject showFirstMsg();
  13. /**
  14. * 获取第一个用户信息
  15. * @return
  16. */
  17. JSONObject showFirstUser();
  18. }

实现类:

  1. package com.zhufeng.web.service.impl;
  2. import com.alibaba.fastjson.JSONObject;
  3. import com.zhufeng.base.db.DataSource;
  4. import com.zhufeng.base.db.DataSourceType;
  5. import com.zhufeng.web.mapper.MsgMapper;
  6. import com.zhufeng.web.service.MsgService;
  7. import org.springframework.stereotype.Service;
  8. import javax.annotation.Resource;
  9. /**
  10. * @ClassName: MsgServiceImpl
  11. * @Description TODO
  12. * @author 月夜烛峰
  13. * @date 2022/9/5 15:06
  14. */
  15. @Service
  16. public class MsgServiceImpl implements MsgService {
  17. @Resource
  18. MsgMapper msgMapper;
  19. /**
  20. * 获取第一条消息信息
  21. * @return
  22. */
  23. @DataSource(value = DataSourceType.SLAVE)
  24. @Override
  25. public JSONObject showFirstMsg() {
  26. return msgMapper.showFirstMsg();
  27. }
  28. /**
  29. * 获取第一个用户信息
  30. * @return
  31. */
  32. @Override
  33. public JSONObject showFirstUser() {
  34. return msgMapper.showFirstUser();
  35. }
  36. }

dao层代码:

  1. /**
  2. * @ClassName: MsgMapper
  3. * @Description TODO
  4. * @author 月夜烛峰
  5. * @date 2022/9/5 14:55
  6. */
  7. @Mapper
  8. public interface MsgMapper {
  9. /**
  10. * 获取第一条消息信息
  11. * @return
  12. */
  13. JSONObject showFirstMsg();
  14. /**
  15. * 获取第一个用户信息
  16. * @return
  17. */
  18. JSONObject showFirstUser();
  19. }

查询语句:

  1. <!--获取第一个用户信息-->
  2. <select id="showFirstUser" resultType="com.alibaba.fastjson.JSONObject">
  3. SELECT
  4. *
  5. FROM
  6. z_f_user
  7. LIMIT 0,1
  8. </select>
  9. <!--获取第一个消息信息-->
  10. <select id="showFirstMsg" resultType="com.alibaba.fastjson.JSONObject">
  11. SELECT
  12. *
  13. FROM
  14. z_f_msg
  15. LIMIT 0,1
  16. </select>

8、启动类

  1. /**
  2. * @ClassName: MsgApplication
  3. * @Description msg启动类
  4. * @author 月夜烛峰
  5. * @date 2022/7/22 08:53
  6. */
  7. @EnableDiscoveryClient
  8. @SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
  9. public class MsgApplication {
  10. public static void main(String[] args) {
  11. SpringApplication.run(MsgApplication.class, args);
  12. }
  13. }

@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})这里是启动时把DataSourceAutoConfiguration排除,避免在启动过程中出现循环引用问题

五、启动测试

1、访问master数据库

2、访问slave数据库

3、修改开关配置

在application.properties中将以下配置删除或者改为false,重新启动项目

#是否启动slave数据源开关,true:启用。默认为false
spring.datasource.druid.dynamic.slave.enabled=false

再次访问slave,报错如下:

 控制台报错信息:

### The error may involve defaultParameterMap
### The error occurred while setting parameters
### SQL: SELECT    *   FROM    z_f_msg   LIMIT 0,1
### Cause: java.sql.SQLSyntaxErrorException: Table 'master.z_f_msg' doesn't exist
; bad SQL grammar []; nested exception is java.sql.SQLSyntaxErrorException: Table 'master.z_f_msg' doesn't exist] with root cause

错误日志显示,去master数据源中查询z_f_msg,也就是没有切换到slave数据源,在servce中添加的slave数据源的动态切换注解没有生效,使用默认的master数据源。

    @DataSource(value = DataSourceType.SLAVE)
    @Override
    public JSONObject showFirstMsg() {
        return msgMapper.showFirstMsg();
    }

master作为默认数据源配置,在初始化数据源时已经指定

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/blog/article/detail/52408
推荐阅读
相关标签
  

闽ICP备14008679号