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

原文地址:https://www.cnblogs.com/gzhbk/p/13782041.html