• SpringBoot学习---第五篇:动态数据源(多数据源自动切换)


    目录

    一、应用场景

    二、准备工作

    2.1  创建数据表

    2.2 添加依赖

    2.3 生成 bean、dao、mapper

    三、动态数据源

    3.1 配置文件 application.properties

    3.2 动态数据源核心代码

    3.3 启动类添加注解

    四、使用方法

    4.1 Controller

    4.2 Service

    五、测试

    六、Springboot2.0动态多数据源切换

    一、应用场景

    项目需要从自己的数据库上读取和管理数据外,还有一部分业务涉及到其他多个数据库。

    为了能够灵活地指定具体的数据库,本文基于注解和AOP的方法实现多数据源自动切换。在使用过程中,只需要添加注解就可以使用,简单方便。

    二、准备工作

    2.1  创建数据表

    1.  
      USE test;
    2.  
      CREATE TABLE `user` (
    3.  
      `id` int(11) NOT NULL AUTO_INCREMENT,
    4.  
      `name` varchar(255) NOT NULL,
    5.  
      `age` int(11) NOT NULL,
    6.  
      PRIMARY KEY (`id`)
    7.  
      ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8
    8.  
       
    9.  
       
    10.  
      USE test1;
    11.  
      CREATE TABLE `teacher` (
    12.  
      `tid` int(11) NOT NULL AUTO_INCREMENT,
    13.  
      `tname` varchar(255) NOT NULL,
    14.  
      `tage` int(11) NOT NULL,
    15.  
      PRIMARY KEY (`tid`)
    16.  
      ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8
    17.  
       
    18.  
       
    19.  
       
    20.  
      USE test2;
    21.  
      CREATE TABLE `student` (
    22.  
      `sid` int(11) NOT NULL AUTO_INCREMENT,
    23.  
      `sname` varchar(255) NOT NULL,
    24.  
      `sage` int(11) NOT NULL,
    25.  
      PRIMARY KEY (`sid`)
    26.  
      ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8

    2.2 添加依赖

    spring boot:1.5.8.RELEASE

    mysql:5.1.44

    mybatis:1.3.2

    druid:1.1.3

    1.  
      <?xml version="1.0" encoding="UTF-8"?>
    2.  
      <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    3.  
      xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    4.  
      <modelVersion>4.0.0</modelVersion>
    5.  
       
    6.  
      <groupId>com.example</groupId>
    7.  
      <artifactId>dynamic-data-source</artifactId>
    8.  
      <version>0.0.1-SNAPSHOT</version>
    9.  
      <packaging>jar</packaging>
    10.  
       
    11.  
      <name>dynamic-data-source</name>
    12.  
      <description>Demo project for Spring Boot</description>
    13.  
       
    14.  
      <parent>
    15.  
      <groupId>org.springframework.boot</groupId>
    16.  
      <artifactId>spring-boot-starter-parent</artifactId>
    17.  
      <version>1.5.8.RELEASE</version>
    18.  
      <relativePath/> <!-- lookup parent from repository -->
    19.  
      </parent>
    20.  
       
    21.  
      <properties>
    22.  
      <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    23.  
      <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    24.  
      <java.version>1.8</java.version>
    25.  
      </properties>
    26.  
       
    27.  
      <dependencies>
    28.  
      <dependency>
    29.  
      <groupId>org.springframework.boot</groupId>
    30.  
      <artifactId>spring-boot-starter-web</artifactId>
    31.  
      </dependency>
    32.  
       
    33.  
      <!--mysql-->
    34.  
      <dependency>
    35.  
      <groupId>mysql</groupId>
    36.  
      <artifactId>mysql-connector-java</artifactId>
    37.  
      <scope>runtime</scope>
    38.  
      </dependency>
    39.  
      <!--mybatis-->
    40.  
      <dependency>
    41.  
      <groupId>org.mybatis.spring.boot</groupId>
    42.  
      <artifactId>mybatis-spring-boot-starter</artifactId>
    43.  
      <version>1.3.2</version>
    44.  
      </dependency>
    45.  
      <!--aop-->
    46.  
      <dependency>
    47.  
      <groupId>org.springframework.boot</groupId>
    48.  
      <artifactId>spring-boot-starter-aop</artifactId>
    49.  
      </dependency>
    50.  
      <!--数据库连接池-->
    51.  
      <dependency>
    52.  
      <groupId>com.alibaba</groupId>
    53.  
      <artifactId>druid</artifactId>
    54.  
      <version>1.1.3</version>
    55.  
      </dependency>
    56.  
       
    57.  
      <dependency>
    58.  
      <groupId>org.springframework.boot</groupId>
    59.  
      <artifactId>spring-boot-starter-test</artifactId>
    60.  
      <scope>test</scope>
    61.  
      </dependency>
    62.  
      </dependencies>
    63.  
       
    64.  
      <build>
    65.  
      <plugins>
    66.  
      <plugin>
    67.  
      <groupId>org.springframework.boot</groupId>
    68.  
      <artifactId>spring-boot-maven-plugin</artifactId>
    69.  
      </plugin>
    70.  
      <!-- mybatis generator 自动生成代码插件 -->
    71.  
      <plugin>
    72.  
      <groupId>org.mybatis.generator</groupId>
    73.  
      <artifactId>mybatis-generator-maven-plugin</artifactId>
    74.  
      <version>1.3.2</version>
    75.  
      <configuration>
    76.  
      <overwrite>true</overwrite>
    77.  
      <verbose>true</verbose>
    78.  
      </configuration>
    79.  
      </plugin>
    80.  
      </plugins>
    81.  
      </build>
    82.  
      </project>

    2.3 生成 bean、dao、mapper

    使用MyBatis Generator自动生成,方法如下:https://blog.csdn.net/cllaure/article/details/81483858

    三、动态数据源

    3.1 配置文件 application.properties

    1.  
      custom.datasource.defaultname=default
    2.  
      custom.datasource.names=ds1,ds2
    3.  
       
    4.  
      # 默认数据源
    5.  
      custom.datasource.driver-class-name=com.mysql.jdbc.Driver
    6.  
      custom.datasource.url=jdbc:mysql://localhost:3306/test
    7.  
      custom.datasource.username=root
    8.  
      custom.datasource.password=root
    9.  
       
    10.  
      # 更多数据源
    11.  
      custom.datasource.ds1.driver-class-name=com.mysql.jdbc.Driver
    12.  
      custom.datasource.ds1.url=jdbc:mysql://localhost:3306/test1
    13.  
      custom.datasource.ds1.username=root
    14.  
      custom.datasource.ds1.password=root
    15.  
       
    16.  
      custom.datasource.ds2.driver-class-name=com.mysql.jdbc.Driver
    17.  
      custom.datasource.ds2.url=jdbc:mysql://localhost:3306/test2
    18.  
      custom.datasource.ds2.username=root
    19.  
      custom.datasource.ds2.password=root
    20.  
       
    21.  
       
    22.  
      custom.datasource.filters=stat
    23.  
      custom.datasource.maxActive=100
    24.  
      custom.datasource.initialSize=1
    25.  
      custom.datasource.minIdle=1
    26.  
      custom.datasource.timeBetweenEvictionRunsMillis=60000
    27.  
      custom.datasource.minEvictableIdleTimeMillis=300000
    28.  
      custom.datasource.validationQuery=select 'x'
    29.  
      custom.datasource.testWhileIdle=true
    30.  
      custom.datasource.testOnBorrow=false
    31.  
      custom.datasource.testOnReturn=false
    32.  
      custom.datasource.poolPreparedStatements=true
    33.  
      custom.datasource.maxOpenPreparedStatements=100
    34.  
       
    35.  
      mybatis.type-aliases-package=com.example.demo.model.*
    36.  
      mybatis.mapper-locations=classpath:mapper/**/*.xml

    3.2 动态数据源核心代码

    1.  
      DynamicDataSource:动态数据源切换;
    2.  
      DynamicDataSourceAspect:利用AOP切面实现数据源的动态切换;
    3.  
      DynamicDataSourceContextHolder:动态切换数据源;
    4.  
      DynamicDataSourceRegister:动态数据源注册;
    5.  
      TargetDataSource:在方法上使用,用于指定使用哪个数据源。
    1.  
      package com.example.demo.datasource;
    2.  
      import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
    3.  
       
    4.  
      /**
    5.  
      * 动态数据源
    6.  
      */
    7.  
      public class DynamicDataSource extends AbstractRoutingDataSource {
    8.  
      @Override
    9.  
      protected Object determineCurrentLookupKey() {
    10.  
      return DynamicDataSourceContextHolder.getDataSourceType();
    11.  
      }
    12.  
      }
    1.  
      package com.example.demo.datasource;
    2.  
       
    3.  
      import org.aspectj.lang.JoinPoint;
    4.  
      import org.aspectj.lang.annotation.After;
    5.  
      import org.aspectj.lang.annotation.Aspect;
    6.  
      import org.aspectj.lang.annotation.Before;
    7.  
      import org.slf4j.Logger;
    8.  
      import org.slf4j.LoggerFactory;
    9.  
      import org.springframework.core.annotation.Order;
    10.  
      import org.springframework.stereotype.Component;
    11.  
       
    12.  
      /**
    13.  
      * 切换数据源Advice
    14.  
      */
    15.  
      @Aspect
    16.  
      @Order(-1)// 保证该AOP在@Transactional之前执行
    17.  
      @Component
    18.  
      public class DynamicDataSourceAspect {
    19.  
      private static final Logger logger = LoggerFactory.getLogger(DynamicDataSourceAspect.class);
    20.  
       
    21.  
      @Before("@annotation(ds)")
    22.  
      public void changeDataSource(JoinPoint point, TargetDataSource ds) throws Throwable {
    23.  
      String dsId = ds.name();
    24.  
      if (!DynamicDataSourceContextHolder.containsDataSource(dsId)) {
    25.  
      logger.error("数据源[{}]不存在,使用默认数据源 > {}", ds.name(), point.getSignature());
    26.  
      }else {
    27.  
      logger.debug("Use DataSource : {} > {}", dsId, point.getSignature());
    28.  
      DynamicDataSourceContextHolder.setDataSourceType(dsId);
    29.  
      }
    30.  
       
    31.  
      }
    32.  
      @After("@annotation(ds)")
    33.  
      public void restoreDataSource(JoinPoint point, TargetDataSource ds) {
    34.  
      logger.debug("Revert DataSource : {} > {}", ds.name(), point.getSignature());
    35.  
      DynamicDataSourceContextHolder.clearDataSourceType();
    36.  
      }
    37.  
      }
    1.  
      package com.example.demo.datasource;
    2.  
       
    3.  
      import java.util.ArrayList;
    4.  
      import java.util.List;
    5.  
       
    6.  
      public class DynamicDataSourceContextHolder {
    7.  
      private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();
    8.  
      public static List<String> dataSourceIds = new ArrayList<>();
    9.  
       
    10.  
      public static void setDataSourceType(String dataSourceType) {
    11.  
      contextHolder.set(dataSourceType);
    12.  
      }
    13.  
       
    14.  
      public static String getDataSourceType() {
    15.  
      return contextHolder.get();
    16.  
      }
    17.  
       
    18.  
      public static void clearDataSourceType() {
    19.  
      contextHolder.remove();
    20.  
      }
    21.  
       
    22.  
      /**
    23.  
      * 判断指定DataSrouce当前是否存在
    24.  
      */
    25.  
      public static boolean containsDataSource(String dataSourceId){
    26.  
      return dataSourceIds.contains(dataSourceId);
    27.  
      }
    28.  
      }
    1.  
      package com.example.demo.datasource;
    2.  
       
    3.  
      import org.slf4j.Logger;
    4.  
      import org.slf4j.LoggerFactory;
    5.  
      import org.springframework.beans.MutablePropertyValues;
    6.  
      import org.springframework.beans.PropertyValues;
    7.  
      import org.springframework.beans.factory.annotation.Value;
    8.  
      import org.springframework.beans.factory.support.BeanDefinitionRegistry;
    9.  
      import org.springframework.beans.factory.support.GenericBeanDefinition;
    10.  
      import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
    11.  
      import org.springframework.boot.bind.RelaxedDataBinder;
    12.  
      import org.springframework.boot.bind.RelaxedPropertyResolver;
    13.  
      import org.springframework.context.EnvironmentAware;
    14.  
      import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
    15.  
      import org.springframework.core.convert.ConversionService;
    16.  
      import org.springframework.core.convert.support.DefaultConversionService;
    17.  
      import org.springframework.core.env.Environment;
    18.  
      import org.springframework.core.type.AnnotationMetadata;
    19.  
       
    20.  
      import javax.sql.DataSource;
    21.  
      import java.util.HashMap;
    22.  
      import java.util.Map;
    23.  
       
    24.  
      /**
    25.  
      * 动态数据源注册
    26.  
      * 启动动态数据源请在启动类中 添加 @Import(DynamicDataSourceRegister.class)
    27.  
      */
    28.  
      public class DynamicDataSourceRegister
    29.  
      implements ImportBeanDefinitionRegistrar, EnvironmentAware {
    30.  
       
    31.  
      private static final Logger logger = LoggerFactory.getLogger(DynamicDataSourceRegister.class);
    32.  
      private ConversionService conversionService = new DefaultConversionService();
    33.  
      private PropertyValues dataSourcePropertyValues;
    34.  
       
    35.  
      // 如配置文件中未指定数据源类型,使用该默认值
    36.  
      private static final Object DATASOURCE_TYPE_DEFAULT = "com.alibaba.druid.pool.DruidDataSource";
    37.  
       
    38.  
      // 数据源
    39.  
      private DataSource defaultDataSource;
    40.  
      private Map<String, DataSource> customDataSources = new HashMap<>();
    41.  
       
    42.  
      private static String DB_NAME = "names";
    43.  
      private static String DB_DEFAULT_VALUE = "custom.datasource"; //配置文件中前缀
    44.  
      @Value("${bi.datasource.defaultname}")
    45.  
      private String defaultDbname;
    46.  
       
    47.  
      //加载多数据源配置
    48.  
      @Override
    49.  
      public void setEnvironment(Environment env) {
    50.  
      initDefaultDataSource(env);
    51.  
      initCustomDataSources(env);
    52.  
      }
    53.  
       
    54.  
      //初始化主数据源
    55.  
      private void initDefaultDataSource(Environment env) {
    56.  
      // 读取主数据源
    57.  
      RelaxedPropertyResolver propertyResolver = new RelaxedPropertyResolver(env, DB_DEFAULT_VALUE+".");
    58.  
      Map<String, Object> dsMap = new HashMap<>();
    59.  
      dsMap.put("type", propertyResolver.getProperty("type"));
    60.  
      dsMap.put("driver-class-name", propertyResolver.getProperty("driver-class-name"));
    61.  
      dsMap.put("url", propertyResolver.getProperty("url"));
    62.  
      dsMap.put("username", propertyResolver.getProperty("username"));
    63.  
      dsMap.put("password", propertyResolver.getProperty("password"));
    64.  
       
    65.  
      defaultDataSource = buildDataSource(dsMap);
    66.  
      customDataSources.put(defaultDbname,defaultDataSource);//默认数据源放到动态数据源里
    67.  
      dataBinder(defaultDataSource, env);
    68.  
      }
    69.  
       
    70.  
       
    71.  
      //为DataSource绑定更多数据
    72.  
      private void dataBinder(DataSource dataSource, Environment env) {
    73.  
      RelaxedDataBinder dataBinder = new RelaxedDataBinder(dataSource);
    74.  
      //dataBinder.setValidator(new LocalValidatorFactory().run(this.applicationContext));
    75.  
      dataBinder.setConversionService(conversionService);
    76.  
      dataBinder.setIgnoreNestedProperties(false);//false
    77.  
      dataBinder.setIgnoreInvalidFields(false);//false
    78.  
      dataBinder.setIgnoreUnknownFields(true);//true
    79.  
      if (dataSourcePropertyValues == null) {
    80.  
      Map<String, Object> rpr = new RelaxedPropertyResolver(env, DB_DEFAULT_VALUE).getSubProperties(".");
    81.  
      Map<String, Object> values = new HashMap<String, Object>(rpr);
    82.  
      // 排除已经设置的属性
    83.  
      values.remove("type");
    84.  
      values.remove("driver-class-name");
    85.  
      values.remove("url");
    86.  
      values.remove("username");
    87.  
      values.remove("password");
    88.  
      dataSourcePropertyValues = new MutablePropertyValues(values);
    89.  
      }
    90.  
      dataBinder.bind(dataSourcePropertyValues);
    91.  
      }
    92.  
       
    93.  
      //初始化更多数据源
    94.  
      private void initCustomDataSources(Environment env) {
    95.  
      // 读取配置文件获取更多数据源,也可以通过defaultDataSource读取数据库获取更多数据源
    96.  
      RelaxedPropertyResolver propertyResolver = new RelaxedPropertyResolver(env,DB_DEFAULT_VALUE+".");
    97.  
      String dsPrefixs = propertyResolver.getProperty(DB_NAME);
    98.  
      for (String dsPrefix : dsPrefixs.split(",")) {// 多个数据源
    99.  
      Map<String, Object> dsMap = propertyResolver.getSubProperties(dsPrefix + ".");
    100.  
      DataSource ds = buildDataSource(dsMap);
    101.  
      customDataSources.put(dsPrefix, ds);
    102.  
      dataBinder(ds, env);
    103.  
      }
    104.  
      }
    105.  
       
    106.  
      @Override
    107.  
      public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
    108.  
      Map<Object, Object> targetDataSources = new HashMap<Object, Object>();
    109.  
      // 将主数据源添加到更多数据源中
    110.  
      targetDataSources.put("dataSource", defaultDataSource);
    111.  
      DynamicDataSourceContextHolder.dataSourceIds.add("dataSource");
    112.  
      // 添加更多数据源
    113.  
      targetDataSources.putAll(customDataSources);
    114.  
      for (String key : customDataSources.keySet()) {
    115.  
      DynamicDataSourceContextHolder.dataSourceIds.add(key);
    116.  
      }
    117.  
       
    118.  
      // 创建DynamicDataSource
    119.  
      GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
    120.  
      beanDefinition.setBeanClass(DynamicDataSource.class);
    121.  
      beanDefinition.setSynthetic(true);
    122.  
      MutablePropertyValues mpv = beanDefinition.getPropertyValues();
    123.  
      mpv.addPropertyValue("defaultTargetDataSource", defaultDataSource);
    124.  
      mpv.addPropertyValue("targetDataSources", targetDataSources);
    125.  
      registry.registerBeanDefinition("dataSource", beanDefinition);
    126.  
       
    127.  
      logger.info("Dynamic DataSource Registry");
    128.  
      }
    129.  
       
    130.  
      //创建DataSource
    131.  
      @SuppressWarnings("unchecked")
    132.  
      public DataSource buildDataSource(Map<String, Object> dsMap) {
    133.  
      try {
    134.  
      Object type = dsMap.get("type");
    135.  
      if (type == null)
    136.  
      type = DATASOURCE_TYPE_DEFAULT;// 默认DataSource
    137.  
       
    138.  
      Class<? extends DataSource> dataSourceType;
    139.  
      dataSourceType = (Class<? extends DataSource>) Class.forName((String) type);
    140.  
       
    141.  
      String driverClassName = dsMap.get("driver-class-name").toString();
    142.  
      String url = dsMap.get("url").toString();
    143.  
      String username = dsMap.get("username").toString();
    144.  
      String password = dsMap.get("password").toString();
    145.  
       
    146.  
      DataSourceBuilder factory = DataSourceBuilder.create().driverClassName(driverClassName).url(url)
    147.  
      .username(username).password(password).type(dataSourceType);
    148.  
      return factory.build();
    149.  
      } catch (ClassNotFoundException e) {
    150.  
      e.printStackTrace();
    151.  
      }
    152.  
      return null;
    153.  
      }
    154.  
      }
    1.  
      package com.example.demo.datasource;
    2.  
       
    3.  
      import java.lang.annotation.Documented;
    4.  
      import java.lang.annotation.ElementType;
    5.  
      import java.lang.annotation.Retention;
    6.  
      import java.lang.annotation.RetentionPolicy;
    7.  
      import java.lang.annotation.Target;
    8.  
       
    9.  
      // 在方法上使用,用于指定使用哪个数据源
    10.  
      @Target({ ElementType.METHOD, ElementType.TYPE })
    11.  
      @Retention(RetentionPolicy.RUNTIME)
    12.  
      @Documented
    13.  
      public @interface TargetDataSource {
    14.  
      String name();
    15.  
      }

    3.3 启动类添加注解

    添加注解 @Import :注册动态多数据源;

    添加 @MapperScan :将项目中对应的mapper类的路径加进来

    1.  
      package com.example.demo;
    2.  
       
    3.  
      import com.example.demo.datasource.DynamicDataSourceRegister;
    4.  
      import org.mybatis.spring.annotation.MapperScan;
    5.  
      import org.springframework.boot.SpringApplication;
    6.  
      import org.springframework.boot.autoconfigure.SpringBootApplication;
    7.  
      import org.springframework.context.annotation.Import;
    8.  
       
    9.  
      @SpringBootApplication
    10.  
      @Import({DynamicDataSourceRegister.class}) // 注册动态多数据源
    11.  
      @MapperScan("com.example.demo.dao")//将项目中对应的mapper类的路径加进来就可以了
    12.  
      public class DynamicDataSourceApplication {
    13.  
      public static void main(String[] args) {
    14.  
      SpringApplication.run(DynamicDataSourceApplication.class, args);
    15.  
      }
    16.  
      }

    四、使用方法

    4.1 Controller

    1.  
      package com.example.demo.controller;
    2.  
       
    3.  
      import com.example.demo.model.Student;
    4.  
      import com.example.demo.model.Teacher;
    5.  
      import com.example.demo.model.User;
    6.  
      import com.example.demo.service.DynamicDataSourceService;
    7.  
      import org.springframework.beans.factory.annotation.Autowired;
    8.  
      import org.springframework.web.bind.annotation.PathVariable;
    9.  
      import org.springframework.web.bind.annotation.RequestMapping;
    10.  
      import org.springframework.web.bind.annotation.RestController;
    11.  
       
    12.  
      import java.util.List;
    13.  
       
    14.  
      @RestController
    15.  
      @RequestMapping(value = "/dds")
    16.  
      public class DynamicDataSourceController {
    17.  
       
    18.  
      @Autowired
    19.  
      private DynamicDataSourceService service;
    20.  
       
    21.  
      @RequestMapping(value = "/user/{id}")
    22.  
      public User getAllUserData(@PathVariable Integer id){
    23.  
      return service.getUserData(id);
    24.  
      }
    25.  
       
    26.  
      @RequestMapping(value = "/teacher/{id}")
    27.  
      public Teacher getAllTeacherData(@PathVariable Integer id) {
    28.  
      return service.getTeacherData(id);
    29.  
      }
    30.  
       
    31.  
      @RequestMapping(value = "/student/{id}")
    32.  
      public Student getAllStudentData(@PathVariable Integer id) {
    33.  
      return service.getStudentData(id);
    34.  
      }
    35.  
      }

    4.2 Service

    注解@TargetDataSource 不能直接在接口类Mapper上使用,所以在Service上使用。

    1.  
      package com.example.demo.service;
    2.  
       
    3.  
      import com.example.demo.dao.StudentMapper;
    4.  
      import com.example.demo.dao.TeacherMapper;
    5.  
      import com.example.demo.dao.UserMapper;
    6.  
      import com.example.demo.datasource.TargetDataSource;
    7.  
      import com.example.demo.model.Student;
    8.  
      import com.example.demo.model.Teacher;
    9.  
      import com.example.demo.model.User;
    10.  
      import org.springframework.beans.factory.annotation.Autowired;
    11.  
      import org.springframework.stereotype.Service;
    12.  
      import java.util.List;
    13.  
       
    14.  
      @Service
    15.  
      public class DynamicDataSourceService {
    16.  
       
    17.  
      @Autowired
    18.  
      private UserMapper userMapper;
    19.  
      @Autowired
    20.  
      private TeacherMapper teacherMapper;
    21.  
      @Autowired
    22.  
      private StudentMapper studentMapper;
    23.  
       
    24.  
       
    25.  
      //不指定数据源使用默认数据源
    26.  
      public User getUserData(Integer id) {
    27.  
      return userMapper.selectByPrimaryKey(id);
    28.  
      }
    29.  
       
    30.  
      //指定数据源-ds1
    31.  
      @TargetDataSource(name="ds1")
    32.  
      public Teacher getTeacherData(Integer id) {
    33.  
      return teacherMapper.selectByPrimaryKey(id);
    34.  
      }
    35.  
       
    36.  
      //指定数据源-ds2
    37.  
      @TargetDataSource(name="ds2")
    38.  
      public Student getStudentData(Integer id) {
    39.  
      return studentMapper.selectByPrimaryKey(id);
    40.  
      }
    41.  
      }

    五、测试

    • localhost:8080//dds/user/1

    • localhost:8080//dds/teacher/1

    • localhost:8080//dds/student/1

    六、Springboot2.0动态多数据源切换

    Springboot 1.x 到 Springboot 2.0配置变化有一点变化。主要是 RelaxedPropertyResolver不再可以Environment自动处理。

    主要 是 DynamicDataSourceRegister 中有部分方法不一样。其他方法都不需要修改。

    1.  
      package com.example.demo.datasource;
    2.  
       
    3.  
      import org.slf4j.Logger;
    4.  
      import org.slf4j.LoggerFactory;
    5.  
      import org.springframework.beans.MutablePropertyValues;
    6.  
      import org.springframework.beans.factory.annotation.Value;
    7.  
      import org.springframework.beans.factory.support.BeanDefinitionRegistry;
    8.  
      import org.springframework.beans.factory.support.GenericBeanDefinition;
    9.  
      import org.springframework.boot.jdbc.DataSourceBuilder;
    10.  
      import org.springframework.context.EnvironmentAware;
    11.  
      import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
    12.  
      import org.springframework.core.env.Environment;
    13.  
      import org.springframework.core.type.AnnotationMetadata;
    14.  
       
    15.  
      import javax.sql.DataSource;
    16.  
      import java.util.HashMap;
    17.  
      import java.util.Map;
    18.  
       
    19.  
      /**
    20.  
      * 动态数据源注册<br/>
    21.  
      * 启动动态数据源请在启动类中 添加 @Import(DynamicDataSourceRegister.class)
    22.  
      */
    23.  
      public class DynamicDataSourceRegister
    24.  
      implements ImportBeanDefinitionRegistrar, EnvironmentAware {
    25.  
       
    26.  
      private static final Logger logger = LoggerFactory.getLogger(DynamicDataSourceRegister.class);
    27.  
       
    28.  
      // 如配置文件中未指定数据源类型,使用该默认值
    29.  
      private static final Object DATASOURCE_TYPE_DEFAULT = "com.alibaba.druid.pool.DruidDataSource";
    30.  
       
    31.  
      // 数据源
    32.  
      private DataSource defaultDataSource;
    33.  
      private Map<String, DataSource> customDataSources = new HashMap<>();
    34.  
       
    35.  
      private static String DB_NAME = "names";
    36.  
      private static String DB_DEFAULT_VALUE = "custom.datasource";
    37.  
      @Value("${bi.datasource.defaultname}")
    38.  
      private String defaultDbname;
    39.  
       
    40.  
      /**
    41.  
      * 加载多数据源配置
    42.  
      */
    43.  
      @Override
    44.  
      public void setEnvironment(Environment env) {
    45.  
      initDefaultDataSource(env);
    46.  
      initCustomDataSources(env);
    47.  
      }
    48.  
       
    49.  
      /**
    50.  
      * 1.5.8 初始化主数据源
    51.  
      */
    52.  
      // private void initDefaultDataSource(Environment env) {
    53.  
      // // 读取主数据源
    54.  
      // RelaxedPropertyResolver propertyResolver = new RelaxedPropertyResolver(env, DB_DEFAULT_VALUE+".");
    55.  
      // Map<String, Object> dsMap = new HashMap<>();
    56.  
      // dsMap.put("type", propertyResolver.getProperty("type"));
    57.  
      // dsMap.put("driver-class-name", propertyResolver.getProperty("driver-class-name"));
    58.  
      // dsMap.put("url", propertyResolver.getProperty("url"));
    59.  
      // dsMap.put("username", propertyResolver.getProperty("username"));
    60.  
      // dsMap.put("password", propertyResolver.getProperty("password"));
    61.  
      //
    62.  
      // defaultDataSource = buildDataSource(dsMap);
    63.  
      // customDataSources.put(defaultDbname,defaultDataSource);//默认数据源放到动态数据源里
    64.  
      // dataBinder(defaultDataSource, env);
    65.  
      // }
    66.  
      /**
    67.  
      * 2.0.4 初始化主数据源
    68.  
      */
    69.  
      private void initDefaultDataSource(Environment env) {
    70.  
      // 读取主数据源
    71.  
      Map<String, Object> dsMap = new HashMap<>();
    72.  
      dsMap.put("type", env.getProperty(DB_DEFAULT_VALUE + "." + "type"));
    73.  
      dsMap.put("driver-class-name", env.getProperty(DB_DEFAULT_VALUE + "." + "driver-class-name"));
    74.  
      dsMap.put("url", env.getProperty(DB_DEFAULT_VALUE + "." + "url"));
    75.  
      dsMap.put("username", env.getProperty(DB_DEFAULT_VALUE + "." + "username"));
    76.  
      dsMap.put("password", env.getProperty(DB_DEFAULT_VALUE + "." + "password"));
    77.  
      defaultDataSource = buildDataSource(dsMap);
    78.  
      // customDataSources.put(defaultDbname,defaultDataSource);//默认数据源放到动态数据源里
    79.  
      // dataBinder(defaultDataSource, env);
    80.  
      }
    81.  
       
    82.  
       
    83.  
      /**
    84.  
      * 为DataSource绑定更多数据
    85.  
      *
    86.  
      */
    87.  
      // private void dataBinder(DataSource dataSource, Environment env) {
    88.  
      // RelaxedDataBinder dataBinder = new RelaxedDataBinder(dataSource);
    89.  
      // //dataBinder.setValidator(new LocalValidatorFactory().run(this.applicationContext));
    90.  
      // dataBinder.setConversionService(conversionService);
    91.  
      // dataBinder.setIgnoreNestedProperties(false);//false
    92.  
      // dataBinder.setIgnoreInvalidFields(false);//false
    93.  
      // dataBinder.setIgnoreUnknownFields(true);//true
    94.  
      // if (dataSourcePropertyValues == null) {
    95.  
      // Map<String, Object> rpr = new RelaxedPropertyResolver(env, DB_DEFAULT_VALUE).getSubProperties(".");
    96.  
      // Map<String, Object> values = new HashMap<String, Object>(rpr);
    97.  
      // // 排除已经设置的属性
    98.  
      // values.remove("type");
    99.  
      // values.remove("driver-class-name");
    100.  
      // values.remove("url");
    101.  
      // values.remove("username");
    102.  
      // values.remove("password");
    103.  
      // dataSourcePropertyValues = new MutablePropertyValues(values);
    104.  
      // }
    105.  
      // dataBinder.bind(dataSourcePropertyValues);
    106.  
      // }
    107.  
       
    108.  
      // 初始化更多数据源
    109.  
      private void initCustomDataSources(Environment env) {
    110.  
      // 读取配置文件获取更多数据源,也可以通过defaultDataSource读取数据库获取更多数据源
    111.  
      // RelaxedPropertyResolver propertyResolver = new RelaxedPropertyResolver(env,DB_DEFAULT_VALUE+".");
    112.  
      String dsPrefixs = env.getProperty(DB_DEFAULT_VALUE + "." + DB_NAME);
    113.  
      for (String dsPrefix : dsPrefixs.split(",")) {// 多个数据源
    114.  
      Map<String, Object> dsMap = new HashMap<>();
    115.  
       
    116.  
      dsMap.put("type", env.getProperty(DB_DEFAULT_VALUE + "." + dsPrefix + ".type"));
    117.  
      dsMap.put("driver-class-name", env.getProperty(DB_DEFAULT_VALUE + "." + dsPrefix + ".driver-class-name"));
    118.  
      dsMap.put("url", env.getProperty(DB_DEFAULT_VALUE + "." + dsPrefix + ".url"));
    119.  
      dsMap.put("username", env.getProperty(DB_DEFAULT_VALUE + "." + dsPrefix + ".username"));
    120.  
      dsMap.put("password", env.getProperty(DB_DEFAULT_VALUE + "." + dsPrefix + ".password"));
    121.  
       
    122.  
       
    123.  
      DataSource ds = buildDataSource(dsMap);
    124.  
      customDataSources.put(dsPrefix, ds);
    125.  
      }
    126.  
      }
    127.  
       
    128.  
       
    129.  
       
    130.  
      @Override
    131.  
      public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
    132.  
      Map<Object, Object> targetDataSources = new HashMap<Object, Object>();
    133.  
      // 将主数据源添加到更多数据源中
    134.  
      targetDataSources.put("dataSource", defaultDataSource);
    135.  
      DynamicDataSourceContextHolder.dataSourceIds.add("dataSource");
    136.  
      // 添加更多数据源
    137.  
      targetDataSources.putAll(customDataSources);
    138.  
      for (String key : customDataSources.keySet()) {
    139.  
      DynamicDataSourceContextHolder.dataSourceIds.add(key);
    140.  
      }
    141.  
       
    142.  
      // 创建DynamicDataSource
    143.  
      GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
    144.  
      beanDefinition.setBeanClass(DynamicDataSource.class);
    145.  
      beanDefinition.setSynthetic(true);
    146.  
      MutablePropertyValues mpv = beanDefinition.getPropertyValues();
    147.  
      mpv.addPropertyValue("defaultTargetDataSource", defaultDataSource);
    148.  
      mpv.addPropertyValue("targetDataSources", targetDataSources);
    149.  
      registry.registerBeanDefinition("dataSource", beanDefinition);
    150.  
       
    151.  
      logger.info("Dynamic DataSource Registry");
    152.  
      }
    153.  
       
    154.  
      // 创建DataSource
    155.  
      @SuppressWarnings("unchecked")
    156.  
      public DataSource buildDataSource(Map<String, Object> dsMap) {
    157.  
      try {
    158.  
      Object type = dsMap.get("type");
    159.  
      if (type == null)
    160.  
      type = DATASOURCE_TYPE_DEFAULT;// 默认DataSource
    161.  
       
    162.  
      Class<? extends DataSource> dataSourceType;
    163.  
      dataSourceType = (Class<? extends DataSource>) Class.forName((String) type);
    164.  
       
    165.  
      String driverClassName = dsMap.get("driver-class-name").toString();
    166.  
      String url = dsMap.get("url").toString();
    167.  
      String username = dsMap.get("username").toString();
    168.  
      String password = dsMap.get("password").toString();
    169.  
       
    170.  
      DataSourceBuilder factory = DataSourceBuilder.create().driverClassName(driverClassName).url(url)
    171.  
      .username(username).password(password).type(dataSourceType);
    172.  
      return factory.build();
    173.  
      } catch (ClassNotFoundException e) {
    174.  
      e.printStackTrace();
    175.  
      }
    176.  
      return null;
    177.  
      }
    178.  
      }

     

    参考文献

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

    https://blog.csdn.net/cllaure/article/details/81509303?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.channel_param&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.channel_param

  • 相关阅读:
    particcles.js in 安卓WebView
    PDF.js 主页样式以及获取流
    Oracle授权A中的表给B
    安卓Response 获取body的正确方式
    android.util.Base64 和 org.apache.commons.codec.binary.Base64
    DAY62-前端入门-javascript(九)jQurey高级
    DAY61-前端入门-javascript(八)jQurey
    DAY60-前端入门-javascript(七)JS动画 | JS盒模型 | DOM | BOM
    DAY59-前端入门-javascript(六)JS事件
    DAY58-前端入门-javascript(五)JS常用类
  • 原文地址:https://www.cnblogs.com/gzhbk/p/13782041.html
Copyright © 2020-2023  润新知