【十七】Spring Bootのマルチデータソース(AbstractRoutingDataSourceクラスを拡張し、データソース名に応じて動的に切り替えます.AOP注釈を使用して動的にデータソースを切り替えます)


一、springマルチデータソース構成の一般的な2つの方案:
1、springプロジェクトの起動時に直接複数の異なるデータソースを配置し、各データソースはそれぞれのsessionFactory、トランザクションマネージャを配置する.異なるDAOが異なるデータソースに対応することを指定します.(一般的にはmapperパスに基づいて指定されます.例えば、com.sid.mapper.Aというパケットの下にあるすべてのmapperを指定するにはdatasourceAデータソースを使用し、com.sid.mapper.Bというパケットの下にあるすべてのmapperを指定するにはdatasourceBデータソースを使用します).
2、複数の異なるデータソースを構成し、1つのセッションファクトリを使用する.ビジネスでデータ・ソースを自分で動的に切り替えます.同時セキュリティを保証するためには、threadlocalを使用して、マルチスレッド競合スイッチングデータソースの問題を実現する必要があります.
ここは2つ目です
二、コード
pom.xml



4.0.0

com.sid
springboot
1.0-SNAPSHOT
jar



    org.springframework.boot
    spring-boot-starter-parent
    1.5.8.RELEASE
     



    UTF-8
    UTF-8
    1.8



    
    
        org.springframework.boot
        spring-boot-starter-web
    

    
        org.springframework.boot
        spring-boot-starter-aop
    

    
    
        org.mybatis.spring.boot
        mybatis-spring-boot-starter
        1.3.2
    


    
    
        mysql
        mysql-connector-java
        5.1.38
    

    
    
        com.alibaba
        druid-spring-boot-starter
        1.1.9
    




    
        
            org.springframework.boot
            spring-boot-maven-plugin
        

        
        
            org.mybatis.generator
            mybatis-generator-maven-plugin
            1.3.2
            
                ${basedir}/src/main/resources/generator/generatorConfig.xml
                true
                true
            
        

    


  

application.yml


server:
  port: 8088
  context-path: /sid

spring:
  datasource:
    #   druid   
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.jdbc.Driver
    druid:
      one:  #   1
        url: jdbc:mysql://localhost:3306/sid
        username: root
        password: root
      two: #   2
          url: jdbc:mysql://localhost:3306/test
          username: root
          password: root
      #             
      initialSize: 1
      #       
      maxActive: 20
      #      
      minIdle: 1
      #           ,    
      maxWait: 60000
      #     :
      #1) Destroy            ,            minEvictableIdleTimeMillis       。
      #2) testWhileIdle     ,   testWhileIdle     
      timeBetweenEvictionRunsMillis: 60000
      #                ,     
      minEvictableIdleTimeMillis: 300000
      #   SQL          。  validationQuery=null,testOnBorrow、testOnReturn、testWhileIdle      。
      validationQuery: SELECT 1 FROM DUAL
      #     true,     ,       。         ,        timeBetweenEvictionRunsMillis,  validationQuery        。
      testWhileIdle: true
      #       validationQuery        ,           。
      testOnBorrow: false
      #       validationQuery        ,           。
      testOnReturn: false
      #          filters,       sql    ,'wall'     
      filters: stat,wall,slf4j
      #   connectProperties     mergeSql  ; SQL  
      #connectionProperties.druid.stat.mergeSql: true
      #connectionProperties.druid.stat.slowSqlMillis: 5000
      #     DruidDataSource     
      #useGlobalDataSourceStat: true
      #default-auto-commit: true   

##            ,    spring    
mybatis:
  mapper-locations: classpath:mapping/*.xml  #  :     mapper  xml       
  type-aliases-package: com.sid.model  #   :        
  configuration:
    #log-impl:  org.apache.ibatis.logging.slf4j.Slf4jImpl
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl #     sql


/**
 * @program: springboot
 * @description:         
 * @author: Sid
 * @date: 2018-11-22 13:59
 * @since: 1.0
 **/
public interface  DataSourceNames {
    String ONE = "ONE";
    String TWO = "TWO";
}

AbstractRoutingDataSource ,


package com.sid.configuration.multi.datasource.dynamic;

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

import javax.sql.DataSource;
import java.util.Map;

/**
 * @program: springboot
 * @description:        
 *                     AbstractRoutingDataSource   ,
 *          DataSourcer     ,
 *               key          DataSource 。
 * @author: Sid
 * @date: 2018-11-22 13:59
 * @since: 1.0
 **/
public class DynamicDataSource extends AbstractRoutingDataSource {

    private static final ThreadLocal contextHolder = new ThreadLocal<>();

    /**
     *   DataSource, defaultTargetDataSource     
     */
    public DynamicDataSource(DataSource defaultTargetDataSource, Map targetDataSources) {
        //       
        super.setDefaultTargetDataSource(defaultTargetDataSource);
        //       
        super.setTargetDataSources(targetDataSources);
        super.afterPropertiesSet();
    }

    /**
     *               ,
     *                 DataSource key ,
     *    key     resolvedDataSource      DataSource,
     *   key      DataSource         。
     * */
    @Override
    protected Object determineCurrentLookupKey() {
        return getDataSource();
    }

    /**
     *             key
     *          removeRouteKey()    
     */
    public static void setDataSource(String dataSource) {
        contextHolder.set(dataSource);
    }

    /**
     *              key
     */
    public static String getDataSource() {
        return contextHolder.get();
    }

    /**
     *                 key
     */
    public static void clearDataSource() {
        contextHolder.remove();
    }

}


package com.sid.configuration.multi.datasource.dynamic;

import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;

import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;

/**
 * @program: springboot
 * @description:        
 * @author: Sid
 * @date: 2018-11-22 14:01
 * @since: 1.0
 **/
@Configuration
public class DynamicDataSourceConfig {
    /**
     *    ChangeDataSource Bean
     * */
    @Bean
    @ConfigurationProperties("spring.datasource.druid.one")
    public DataSource oneDataSource(){
        DataSource dataSource = DruidDataSourceBuilder.create().build();
        return dataSource;
    }

    @Bean
    @ConfigurationProperties("spring.datasource.druid.two")
    public DataSource twoDataSource(){
        DataSource dataSource = DruidDataSourceBuilder.create().build();
        return dataSource;
    }

    /**
     *        ,       ChangeDataSource Bean
     * */

    @Bean
    @Primary
    public DynamicDataSource dataSource(DataSource oneDataSource, DataSource twoDataSource) {
        Map targetDataSources = new HashMap<>(2);
        targetDataSources.put(DataSourceNames.ONE, oneDataSource);
        targetDataSources.put(DataSourceNames.TWO, twoDataSource);
        //      , targetDataSources     
        System.out.println("DataSources:" + targetDataSources);
        //       oneDataSource
        return new DynamicDataSource(oneDataSource, targetDataSources);
    }
}

, DAO , :

TOW
DynamicDataSource.setDataSource(DataSourceNames.TWO);

//todo

userMapper.insert(user);


DynamicDataSource.clearDataSource();

  , AOP, ,


package com.sid.configuration.multi.datasource.dynamic;

import java.lang.annotation.*;

/**
 * @program: springboot
 * @description:          
 * @author: Sid
 * @date: 2018-11-22 14:02
 * @since: 1.0
 **/
@Documented
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ChangeDataSource {
    String value() default DataSourceNames.ONE;
}

AOP


package com.sid.configuration.multi.datasource.dynamic;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

/**
 * @program: springboot
 * @description:        AOP    
 * @author: Sid
 * @date: 2018-11-22 14:03
 * @since: 1.0
 **/
@Aspect
@Component
public class DataSourceAspect implements Ordered {
    protected Logger logger = LoggerFactory.getLogger(getClass());

    /**
     *   :      ChangeDataSource      
     */
    @Pointcut("@annotation(com.sid.configuration.multi.datasource.dynamic.ChangeDataSource)")
    public void dataSourcePointCut() {}

    @Around("dataSourcePointCut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();
        ChangeDataSource ds = method.getAnnotation(ChangeDataSource.class);
        //      @ChangeDataSource                    
        DynamicDataSource.setDataSource(ds.value());
        System.out.println("     : " + ds.value());
        logger.debug("set datasource is " + ds.value());
        try {
            return point.proceed();
        } finally {
            DynamicDataSource.clearDataSource();
            logger.debug("clean datasource");
        }
    }
    @Override
    public int getOrder() {
        return 1;
    }
}

addUserA @ChangeDataSource DataSource


package com.sid.service.impl;

import com.sid.configuration.multi.datasource.dynamic.ChangeDataSource;
import com.sid.configuration.multi.datasource.dynamic.DataSourceNames;
import com.sid.mapper.UserMapper;
import com.sid.model.User;
import com.sid.service.MultiDataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * @program: springboot
 * @description:
 * @author: Sid
 * @date: 2018-11-22 14:20
 * @since: 1.0
 **/
@Service
public class MultiDataSourceImpl implements MultiDataSource {
    @Autowired
    private UserMapper userMapper;

    @Override
    public User addUserA(User user) {
        userMapper.insert(user);
        return user;
    }

    @ChangeDataSource(DataSourceNames.TWO)
    @Override
    public User addUserB(User user) {
        userMapper.insert(user);
        return user;
    }
}