springboot多數據源之AbstractRoutingDataSource

springboot繼承AbstractRoutingDataSource可以實現數據源。

1.先寫繼承AbstractRoutingDataSource的類

(1).DynamicDataSource 是設置默認數據源和已經存在的所有數據源

(2).這個determineCurrentLookupKey是從當前線程獲取設置的數據源

public class DynamicDataSource extends AbstractRoutingDataSource
{
    public DynamicDataSource(DataSource defaultTargetDataSource, Map<Object, Object> targetDataSources)
    {
        super.setDefaultTargetDataSource(defaultTargetDataSource);
        super.setTargetDataSources(targetDataSources);
        super.afterPropertiesSet();
    }

    @Override
    protected Object determineCurrentLookupKey()
    {
        return DynamicDataSourceContextHolder.getDateSoureType();
    }
}

2.DynamicDataSourceContextHolder設置當前線程處理數據源的類

public class DynamicDataSourceContextHolder {

    public static final Logger logger = LoggerFactory.getLogger(DynamicDataSourceContextHolder.class);

    private static final ThreadLocal<String> DATASOURCE_CONTEXT_HOLDER = new ThreadLocal<>();

    /**
     * 設置數據源
     */
    public static void setDateSoureType(String dateSoureType) {
        logger.info("設置數據源={}", dateSoureType);
        DATASOURCE_CONTEXT_HOLDER.set(dateSoureType);
    }

    /**
     * 獲得數據源
     */
    public static String getDateSoureType() {
        return DATASOURCE_CONTEXT_HOLDER.get();
    }

    /**
     * 清空數據源
     */
    public static void clearDateSoureType() {
        DATASOURCE_CONTEXT_HOLDER.remove();
    }
}

3.配置Hikari數據源和包位置

@Configuration
@MapperScan("com.fast.framework.dao")
public class HikariCustomConfig {

    public static final Logger logger = LoggerFactory.getLogger(HikariCustomConfig.class);

    @Value("${mybatis.config-location}")
    private String configLocation;
    @Value("${mybatis.type-aliases-package}")
    private String typeAliasespackage;

    @Bean("master")
    @ConfigurationProperties(prefix = "spring.datasource.hikari.master")
    public HikariDataSource masterDataSource() {
        return DataSourceBuilder.create().type(HikariDataSource.class).build();
    }

    @Bean("second")
    @ConfigurationProperties(prefix = "spring.datasource.hikari.second")
    public HikariDataSource slaveDataSource() {
        return DataSourceBuilder.create().type(HikariDataSource.class).build();
    }

    @Bean(name = "dynamicDataSource")
    public DynamicDataSource dataSource(@Qualifier("master") DataSource masterDataSource, @Qualifier("second") DataSource slaveDataSource) {
        Map<Object, Object> targetDataSources = new HashMap<>();
        targetDataSources.put(AllDatasource.MASTER.name(), masterDataSource);
        targetDataSources.put(AllDatasource.SECOND.name(), slaveDataSource);
        return new DynamicDataSource(masterDataSource, targetDataSources);
    }

    /**
     * @description: 配置mybatis的mapper和dao的位置
     */
    @Bean("sqlSessionFactoryBean")
    public SqlSessionFactoryBean sqlSessionFactoryBean(@Qualifier("dynamicDataSource") DynamicDataSource dynamicDataSource) throws IOException {
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dynamicDataSource);
        sqlSessionFactoryBean.setMapperLocations(
                new PathMatchingResourcePatternResolver().getResources(configLocation));
        sqlSessionFactoryBean.setTypeAliasesPackage(typeAliasespackage);
        return sqlSessionFactoryBean;
    }

    /**
     * 事務管理
     */
    @Bean
    public PlatformTransactionManager transactionManager(@Qualifier("dynamicDataSource") DynamicDataSource dynamicDataSource) {
        return new DataSourceTransactionManager(dynamicDataSource);
    }

}

4.設置aop註解,通過註解設置數據源

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

    @Value("${spring.datasource.hikari.master.default-package}")
    private String masterPackage;
    @Value("${spring.datasource.hikari.second.default-package}")
    private String secondPackage;

    public static final Logger logger = LoggerFactory.getLogger(DataSourceAop.class);

    @Pointcut("execution(* com.fast.framework.dao..*.*(..))  ||@annotation(com.fast.common.annotation.ChooseDataSource)")
    public void switchDataSource() {
    }

    @Before("switchDataSource()")
    public void doBefore(JoinPoint joinPoint) {
        Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
        ChooseDataSource chooseDataSource = method.getAnnotation(ChooseDataSource.class);//獲取方法上的註解
        if (chooseDataSource == null) {
            chooseDataSource = joinPoint.getTarget().getClass().getAnnotation(ChooseDataSource.class);//獲取類上面的註解
            if (chooseDataSource == null) {
                String declaringTypeName = joinPoint.getSignature().getDeclaringTypeName();
                if (declaringTypeName.startsWith(masterPackage)) {
                    DynamicDataSourceContextHolder.setDateSoureType(AllDatasource.MASTER.name());
                    logger.info("使用包默認數據源,包={},數據源={}", masterPackage, AllDatasource.MASTER.name());
                } else if (declaringTypeName.startsWith(secondPackage)) {
                    DynamicDataSourceContextHolder.setDateSoureType(AllDatasource.SECOND.name());
                    logger.info("使用包默認數據源,包={},數據源={}", secondPackage, AllDatasource.SECOND.name());
                }
                return;
            } else {
                logger.info("類註解生效,切換數據源={}", chooseDataSource.dataSource().name());
            }
        } else {
            logger.info("方法註解生效,切換數據源={}", chooseDataSource.dataSource().name());
        }
        //獲取註解上的數據源的值的信息
        String dataSourceName = chooseDataSource.dataSource().name();
        if (dataSourceName != null) {
            //給當前的執行SQL的操作設置特殊的數據源的信息
            DynamicDataSourceContextHolder.setDateSoureType(dataSourceName);
        }
        String nowDatasource = "".equals(dataSourceName) ? "默認數據源master" : dataSourceName;
        logger.info("AOP註解切換數據源,className" + joinPoint.getTarget().getClass().getName() + "methodName" + method.getName() + ";dataSourceName:" + nowDatasource);
    }

    @After("switchDataSource()")
    public void after(JoinPoint point) {
        //清理掉當前設置的數據源,讓默認的數據源不受影響
        DynamicDataSourceContextHolder.clearDateSoureType();
    }
}

5.配置數據源和包位置

spring:
  datasource:
    type: com.zaxxer.hikari.HikariDataSource
    hikari:
      driver-class-name: com.mysql.cj.jdbc.Driver
      maximum-pool-size: 20
      max-lifetime: 30000
      idle-timeout: 30000
      master:
        default-package: com.fast.framework.dao.master
        jdbc-url: jdbc:mysql://localhost:3306/fastblack?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&serverTimezone=GMT%2B8&useSSL=true
        username: root
        password: 123456
      second:
        default-package: com.fast.framework.dao.second
        jdbc-url: jdbc:mysql://localhost:3306/base?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&serverTimezone=GMT%2B8&useSSL=true
        username: root
        password: 123456

mybatis:
  config-location: classpath*:/mapper/*/*.xml
  type-aliases-package: com.fast.framework.model

6.枚舉和切換數據源註解

public enum AllDatasource {
     MASTER,
     SECOND;
}

註解:

/**
 * @Auther: Administrator
 * @Date: 2019/6/9 16:36
 * @Description:設置數據源 優先級:1.方法註解 2.類註解 3.根據包路徑設置的數據源
 */
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ChooseDataSource {

    AllDatasource dataSource() default AllDatasource.MASTER;
}

最後,這個是經過測試可以使用的,各位需要有基礎,配置的時候直接拷貝修改一下就好了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章