多数据源的配置和使用

yaml文件里的:

spring:
  datasource:
    one:
      jdbc-url: jdbc:mysql://127.0.0.1:3306/bet?useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true
      username: root
      password: root
    two:
      jdbc-url: jdbc:mysql://127.0.0.2:3306/bet?useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true
      username: root
      password: root
    three:
      jdbc-url: jdbc:mysql://127.0.0.3:3306/bet?useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true
      username: root
      password: root

package com.mybet.server.admin.annotation;

import java.lang.annotation.*;

/**
 * 动态数据源注解
 */
@Target( { ElementType.METHOD, ElementType.TYPE } )
@Retention( RetentionPolicy.RUNTIME )
@Documented
public @interface DataSource {

   /**
    * 数据源key值
    *
    * @return
    */
   String value();

}

package com.mybet.server.admin.aspect;

import com.mybet.server.admin.annotation.DataSource;
import com.mybet.server.admin.config.dds.DynamicDataSourceContextHolder;
import lombok.extern.log4j.Log4j2;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

/**
 * 动态数据源切换处理器
 */
@Log4j2
@Aspect
@Order( -1 )  // 该切面应当先于 @Transactional 执行
@Component
public class DynamicDataSourceAspect {

   /**
    * 切换数据源
    */
   @Before( "@annotation(dataSource))" )
   public void switchDataSource( JoinPoint point, DataSource dataSource ) {
      if ( !DynamicDataSourceContextHolder.containDataSourceKey( dataSource.value() ) ) {
         log.warn( "DataSource [{}] doesn't exist, use default DataSource [{}] " + dataSource.value() );
      } else {
         // 切换数据源
         DynamicDataSourceContextHolder.setDataSourceKey( dataSource.value() );
         log.warn( "Switch DataSource to [" + DynamicDataSourceContextHolder.getDataSourceKey()
               + "] in Method [" + point.getSignature() + "]" );
      }
   }

   /**
    * 重置数据源
    */
   @After( "@annotation(dataSource))" )
   public void restoreDataSource( JoinPoint point, DataSource dataSource ) {
      // 将数据源置为默认数据源
      DynamicDataSourceContextHolder.clearDataSourceKey();
      log.warn( "Restore DataSource to [" + DynamicDataSourceContextHolder.getDataSourceKey()
            + "] in Method [" + point.getSignature() + "]" );
   }
}

package com.mybet.server.admin.config.dds;

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

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

/**
 * 动态数据源实现类
 */
public class DynamicDataSource extends AbstractRoutingDataSource {

   /**
    * 如果不希望数据源在启动配置时就加载好,可以定制这个方法,从任何你希望的地方读取并返回数据源 比如从数据库、文件、外部接口等读取数据源信息,并最终返回一个DataSource实现类对象即可
    */
   @Override
   protected DataSource determineTargetDataSource() {
      return super.determineTargetDataSource();
   }

   /**
    * 如果希望所有数据源在启动配置时就加载好,这里通过设置数据源Key值来切换数据,定制这个方法
    */
   @Override
   protected Object determineCurrentLookupKey() {
      return DynamicDataSourceContextHolder.getDataSourceKey();
   }

   /**
    * 设置默认数据源
    */
   public void setDefaultDataSource( Object defaultDataSource ) {
      super.setDefaultTargetDataSource( defaultDataSource );
   }

   /**
    * 设置数据源
    */
   public void setDataSources( Map<Object, Object> dataSources ) {
      super.setTargetDataSources( dataSources );
      // 将数据源的 key 放到数据源上下文的 key 集合中,用于切换时判断数据源是否有效
      DynamicDataSourceContextHolder.addDataSourceKeys( dataSources.keySet() );
   }
}

package com.mybet.server.admin.config.dds;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

/**
 * 动态数据源上下文
 */
public class DynamicDataSourceContextHolder {

   private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>() {
      /**
       * 将 master 数据源的 key作为默认数据源的 key
       */
      @Override
      protected String initialValue() {
         return "dbdata";
      }
   };


   /**
    * 数据源的 key集合,用于切换时判断数据源是否存在
    */
   public static List<Object> dataSourceKeys = new ArrayList<>();

   /**
    * 切换数据源
    */
   public static void setDataSourceKey( String key ) {
      contextHolder.set( key );
   }

   /**
    * 获取数据源
    *
    * @return
    */
   public static String getDataSourceKey() {
      return contextHolder.get();
   }

   /**
    * 重置数据源
    */
   public static void clearDataSourceKey() {
      contextHolder.remove();
   }

   /**
    * 判断是否包含数据源
    *
    * @param key 数据源key
    * @return
    */
   public static boolean containDataSourceKey( String key ) {
      return dataSourceKeys.contains( key );
   }

   /**
    * 添加数据源keys
    *
    * @return
    */
   public static boolean addDataSourceKeys( Collection<? extends Object> keys ) {
      return dataSourceKeys.addAll( keys );
   }
}
package com.mybet.server.admin.config;

import com.mybet.server.admin.config.dds.DynamicDataSource;
import com.zaxxer.hikari.HikariDataSource;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.mybatis.spring.boot.autoconfigure.MybatisProperties;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;

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

@Configuration
@MapperScan( basePackages = { "com.mybet.server.admin.mapper" } ) // 扫描Mapper
public class MybatisConfig {

   @Bean( "oneDataSource" )
   @Primary
   @ConfigurationProperties( prefix = "spring.datasource.one" )
   public DataSource oneDataSource() {
      return DataSourceBuilder.create().type( HikariDataSource.class ).build();
   }

   @Bean( "twoDataSource" )
   @ConfigurationProperties( prefix = "spring.datasource.two" )
   public DataSource twoDataSource() {
      return DataSourceBuilder.create().type( HikariDataSource.class ).build();
   }

   @Bean( "threeDataSource" )
   @ConfigurationProperties( prefix = "spring.datasource.three" )
   public DataSource threeDataSource() {
      return DataSourceBuilder.create().type( HikariDataSource.class ).build();
   }

   @Bean( "dynamicDataSource" )
   public DataSource dynamicDataSource() {
      DynamicDataSource   dynamicDataSource = new DynamicDataSource();
      Map<Object, Object> dataSourceMap     = new HashMap<>();
      dataSourceMap.put( "one", oneDataSource() );
      dataSourceMap.put( "two", twoDataSource() );
      dataSourceMap.put( "three", threeDataSource() );
      
      // 将 master 数据源作为默认指定的数据源
      dynamicDataSource.setDefaultDataSource( oneDataSource() );
      // 将 master 和 slave 数据源作为指定的数据源
      dynamicDataSource.setDataSources( dataSourceMap );
      return dynamicDataSource;
   }

   @Bean
   public SqlSessionFactoryBean sqlSessionFactoryBean( @Qualifier( "dynamicDataSource" ) DataSource dynamicDataSource,
                                          MybatisProperties mybatisProperties ) throws Exception {
      SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
      // 配置数据源,此处配置为关键配置,如果没有将 dynamicDataSource作为数据源则不能实现切换
      sessionFactory.setDataSource( dynamicDataSource );
      // 扫描Model
      sessionFactory.setTypeAliasesPackage( "com.mybet.server.admin.domain" );
      PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
      // 扫描映射文件
      sessionFactory.setMapperLocations( resolver.getResources( "classpath*:mapper/*.xml" ) );
      sessionFactory.setConfiguration( mybatisProperties.getConfiguration() );
      return sessionFactory;
   }

   @Bean
   public DataSourceTransactionManager transactionManager( @Qualifier( "dynamicDataSource" ) DataSource dynamicDataSource ) {
      // 配置事务管理, 使用事务时在方法头部添加@Transactional注解即可
      return new DataSourceTransactionManager( dynamicDataSource );
   }
}

-----------------以上是配置,以下是业务使用------------------

package com.mybet.server.admin.utils;

import com.mybet.server.admin.config.dds.DynamicDataSourceContextHolder;

//指定数据源
public class DataSourceHolder {
   public static void setDataSourceKey(String number){
      String dataKey;
      switch (number){
      case "1":
         dataKey = "one";
         break;
      case "2":
         dataKey = "two";
         break;
      case "3":
         dataKey = "three";
         break;
      default:
         dataKey = "one";
      }
      DynamicDataSourceContextHolder.setDataSourceKey(dataKey);
   }

//重置多数据源
   public static void clearDataSourceKey(){
      DynamicDataSourceContextHolder.clearDataSourceKey();
   }
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值