spring boot mybatis 多數據源配置

package com.xynet.statistics.config.dataresources;

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
/**
 * 動態數據源
 * Copyright © 2019 xynet Tech Ltd. All rights reserved
 * @author: sund
 * @date: 2019年3月23日 下午4:35:39
 * @remark:
 */
public class DynamicDataSource extends AbstractRoutingDataSource {

    /**
     * 代碼中的determineCurrentLookupKey方法取得一個字符串, 該字符串將與配置文件中的相應字符串進行匹配以定位數據源
     */
    @Override
    protected Object determineCurrentLookupKey() {
        /**
         * DynamicDataSourceContextHolder代碼中使用setDataSourceType
         * 設置當前的數據源,在路由類中使用getDataSourceType進行獲取,
         * 交給AbstractRoutingDataSource進行注入使用
         */
        return DynamicDataSourceContextHolder.getDataSourceType();
    }
}
package com.xynet.statistics.config.dataresources;

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;

/**
 * 數據源切面
 * 
 * @Order(-5)保證該AOP在@Transactional以前執行 order 越小優先級越高
 * Copyright © 2019 xynet Tech Ltd. All rights reserved
 * @author: sund
 * @date: 2019年3月23日 下午4:24:49
 * @remark:
 */
@Aspect
@Order(-5)
@Component
public class DynamicDataSourceAspect {

    /**
     * @Before("@annotation(ds)") @Before:在方法執行以前進行執行: @annotation(
     * targetDataSource): 會攔截註解targetDataSource的方法,不然不攔截;
     * 
     * @param point
     * @param targetDataSource
     * @throws Throwable
     */
    @Before("@annotation(targetDataSource)")
    public void changeDataSource(JoinPoint point, TargetDataSource targetDataSource) throws Throwable {
        // 獲取當前的指定的數據源;
        String dsId = targetDataSource.value();
        // 若是不在咱們注入的全部的數據源範圍以內,那麼輸出警告信息,系統自動使用默認的數據源。
        if (!DynamicDataSourceContextHolder.containsDataSource(dsId)) {
            System.out.println("數據源【" + targetDataSource.value() + "】不存在,使用默認數據源:" + point.getSignature());
        } else {
            System.out.println("Use DataSource:" + targetDataSource.value() + ":" + point.getSignature());
            // 找到的話,那麼設置到動態數據源上下文中指定的數據源
            DynamicDataSourceContextHolder.setDataSourceType(targetDataSource.value());
        }
    }

    @After("@annotation(targetDataSource)")
    public void restoreDataSource(JoinPoint point, TargetDataSource targetDataSource) {
        System.out.println("Revert DataSource:" + targetDataSource.value() + ":" + point.getSignature());
        // 方法執行完畢以後,銷燬當前數據源信息,進行垃圾回收
        DynamicDataSourceContextHolder.clearDataSourceType();
    }
}
package com.xynet.statistics.config.dataresources;

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

/**
 * 動態數據源上下文
 * Copyright © 2019 xynet Tech Ltd. All rights reserved
 * @author: sund
 * @date: 2019年3月23日 下午4:32:45
 * @remark:
 */
public class DynamicDataSourceContextHolder {

    /**
     * 當使用ThreadLocal維護變量時,ThreadLocal爲每一個使用該變量的線程提供獨立的變量副本,
     * 因此每個線程均可以獨立地改變本身的副本,而不會影響其它線程所對應的副本。
     */
    private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();

    /**
     * 管理全部的數據源id
     * 主要是爲了判斷數據源是否存在
     */
    public static List<String> dataSourceIds = new ArrayList<String>();

    /**
     * 使用setDataSourceType設置當前的
     * @param dataSourceType
     */
    public static void setDataSourceType(String dataSourceType) {
        contextHolder.set(dataSourceType);
    }

    public static String getDataSourceType() {
        return contextHolder.get();
    }

    public static void clearDataSourceType() {
        contextHolder.remove();
    }

    /**
     * 判斷指定DataSource當前是否存在
     * @param dataSourceId
     * @return
     */
    public static boolean containsDataSource(String dataSourceId) {
        return dataSourceIds.contains(dataSourceId);
    }

}
package com.xynet.statistics.config.dataresources;

import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.PropertyValues;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
import org.springframework.boot.bind.RelaxedDataBinder;
import org.springframework.boot.bind.RelaxedPropertyResolver;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotationMetadata;

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

/**
 * 動態數據源註冊 Copyright © 2019 xynet Tech Ltd. All rights reserved
 * 
 * @author: sund
 * @date: 2019年3月23日 下午4:34:37
 * @remark:要在啓動類上增長註解  @Import({DynamicDataSourceRegister.class})
 */
public class DynamicDataSourceRegister implements ImportBeanDefinitionRegistrar, EnvironmentAware {

    private static final Object DATASOURCE_TYPE_DEFAULT = "org.apache.tomcat.jdbc.pool.DataSource";
    private ConversionService conversionService = new DefaultConversionService();
    private PropertyValues dataSourcePropertyValues;
    // 默認數據源
    private DataSource defaultDataSource;
    private Map<String, DataSource> customDataSources = new HashMap<String, DataSource>();

    @Override
    public void setEnvironment(Environment environment) {
        initDefaultDataSource(environment);
        initCustomDataSources(environment);
    }

    /**
     * 加載主數據源配置.
     * 
     * @param env
     */
    private void initDefaultDataSource(Environment env) {
        RelaxedPropertyResolver propertyResolver = new RelaxedPropertyResolver(env, "spring.datasource.");
        Map<String, Object> dsMap = new HashMap<String, Object>();
        dsMap.put("type", propertyResolver.getProperty("type"));
        dsMap.put("driverClassName", propertyResolver.getProperty("driverClassName"));
        dsMap.put("url", propertyResolver.getProperty("url"));
        dsMap.put("username", propertyResolver.getProperty("username"));
        dsMap.put("password", propertyResolver.getProperty("password"));
        defaultDataSource = buildDataSource(dsMap);
        dataBinder(defaultDataSource, env);
    }

    /**
     * 加載更多據源配置.
     * 
     * @param env
     */
    private void initCustomDataSources(Environment env) {
        RelaxedPropertyResolver propertyResolver = new RelaxedPropertyResolver(env, "custom.datasource.");
        String dsPrefixs = propertyResolver.getProperty("names");
        for (String dsPrefix : dsPrefixs.split(",")) {
            Map<String, Object> dsMap = propertyResolver.getSubProperties(dsPrefix + ".");
            DataSource ds = buildDataSource(dsMap);
            customDataSources.put(dsPrefix, ds);
            dataBinder(ds, env);
        }
    }

    public DataSource buildDataSource(Map<String, Object> dsMap) {
        Object type = dsMap.get("type");
        if (type == null) {
            type = DATASOURCE_TYPE_DEFAULT;
        }
        Class<? extends DataSource> dataSourceType;
        try {
            dataSourceType = (Class<? extends DataSource>) Class.forName((String) type);
            String driverClassName = dsMap.get("driverClassName").toString();
            String url = dsMap.get("url").toString();
            String username = dsMap.get("username").toString();
            String password = dsMap.get("password").toString();
            DataSourceBuilder factory = DataSourceBuilder.create().driverClassName(driverClassName).url(url)
                    .username(username).password(password).type(dataSourceType);
            return factory.build();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return null;
    }

    private void dataBinder(DataSource dataSource, Environment env) {
        RelaxedDataBinder dataBinder = new RelaxedDataBinder(dataSource);
        dataBinder.setConversionService(conversionService);
        dataBinder.setIgnoreNestedProperties(false);
        dataBinder.setIgnoreInvalidFields(false);
        dataBinder.setIgnoreUnknownFields(true);
        if (dataSourcePropertyValues == null) {
            Map<String, Object> rpr = new RelaxedPropertyResolver(env, "spring.datasource").getSubProperties(".");
            Map<String, Object> values = new HashMap<>(rpr);
            // 排除已經設置的屬性
            values.remove("type");
            values.remove("driverClassName");
            values.remove("url");
            values.remove("username");
            values.remove("password");
            dataSourcePropertyValues = new MutablePropertyValues(values);
        }
        dataBinder.bind(dataSourcePropertyValues);
    }

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        Map<Object, Object> targetDataSources = new HashMap<Object, Object>();
        // 將主數據源添加到更多數據源中
        targetDataSources.put("dataSource", defaultDataSource);
        DynamicDataSourceContextHolder.dataSourceIds.add("dataSource");
        // 添加更多數據源
        targetDataSources.putAll(customDataSources);
        for (String key : customDataSources.keySet()) {
            DynamicDataSourceContextHolder.dataSourceIds.add(key);
        }
        // 建立DynamicDataSource
        GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
        beanDefinition.setBeanClass(DynamicDataSource.class);
        beanDefinition.setSynthetic(true);
        MutablePropertyValues mpv = beanDefinition.getPropertyValues();
        // 添加屬性:AbstractRoutingDataSource.defaultTargetDataSource
        mpv.addPropertyValue("defaultTargetDataSource", defaultDataSource);
        mpv.addPropertyValue("targetDataSources", targetDataSources);
        registry.registerBeanDefinition("dataSource", beanDefinition);
        System.out.println("============註冊數據源成功==============");
    }
}
package com.xynet.statistics.config.dataresources;

import java.lang.annotation.*;

/**
 * 自定義註解,數據源指定
 * Copyright © 2019 xynet Tech Ltd. All rights reserved
 * @author: sund
 * @date: 2019年3月23日 下午4:37:16
 * @remark:
 */
@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TargetDataSource {
    String value();
}

將上面五個類創建好後在啓動類中加入@Import({DynamicDataSourceRegister.class}),這個很關鍵否則加載不了多數據源,只會調用默認數據源java

@EnableDiscoveryClient
@EnableHystrix
@EnableEurekaClient
@SpringBootApplication
@EnableCircuitBreaker
@ServletComponentScan
@EnableRedisHttpSession
@EnableTransactionManagement 
@EnableFeignClients
@EnableScheduling
@MapperScan(basePackages = "com.xynet.statistics.dao")
@Import({DynamicDataSourceRegister.class})
public class XynetServiceStatisticsApplication {

    public static void main(String[] args) {
        SpringApplication.run(XynetServiceStatisticsApplication.class, args);
    }
    
    @Bean
    public AsyncTaskExecutor paraTaskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setThreadNamePrefix("statistics-paraTaskExecutor-Executor");
        executor.setCorePoolSize(24);
        executor.setQueueCapacity(100);
        executor.setMaxPoolSize(500);
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardPolicy());

        executor.initialize();
        /*
         * // 設置拒絕策略 executor.setRejectedExecutionHandler(new
         * RejectedExecutionHandler() {
         * 
         * @Override public void rejectedExecution(Runnable r,
         * ThreadPoolExecutor executor) { // ..... } }); // 使用預約義的異常處理類
         * executor.setRejectedExecutionHandler(new
         * ThreadPoolExecutor.CallerRunsPolicy());
         */
        return executor;
    }
}

數據源切換註解要加到service上不要加到Mapper中上不然不會生效mysql

 
 

@Service
public class BigDataServiceImpl implements BigDataService {redis

    
@Override @TargetDataSource(
"slave1") public List<T_jq_jqxx> selectJqbhByShbh(String shbh) { return t_jq_jqxxMapper.selectJqbhByShbh(shbh); }

application-dev.yaml 配置文件以下:spring

spring:
  profiles: dev
  #mysql
  datasource:      
      type: com.alibaba.druid.pool.DruidDataSource 
      initialSize: 5  
      minIdle: 5  
      maxActive: 20  
      maxWait: 60000  
      timeBetweenEvictionRunsMillis: 60000  
      minEvictableIdleTimeMillis: 300000  
      validationQuery: SELECT NOW()  
      testWhileIdle: true  
      testOnBorrow: true  
      testOnReturn: false  
      poolPreparedStatements: true  
      maxPoolPreparedStatementPerConnectionSize: 20  
      filters: stat,log4j  
      connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000 
      driverClassName: com.mysql.jdbc.Driver
      username: root
      password: 1234
      url: jdbc:mysql://192.168.1.253:3306/xy-platform?characterEncoding=utf-8&serverTimezone=GMT%2B8&useSSL=false 

  #jpa
  jpa: 
      show-sql: true
      open-in-view: true  
            
#redis
  redis:
    database: 15
    host: 192.168.1.253
    password:  
    port: 6379
    pool:
      min-idle: 1
      max-idle: 8
      max-active: 100
      max-wait: 1    
    timeout: 10000 

custom:
    datasource:  
      names: slave1,slave2
      slave1:
        type: com.alibaba.druid.pool.DruidDataSource
        driverClassName: com.mysql.jdbc.Driver
        username: root
        password: 1234
        url: jdbc:mysql://192.168.1.253:3306/test?characterEncoding=utf-8&serverTimezone=GMT%2B8&useSSL=false
      slave2:    
        type: com.alibaba.druid.pool.DruidDataSource
        driverClassName: com.mysql.jdbc.Driver
        username: root
        password: 1234
        url: jdbc:mysql://192.168.1.253:3306/sy?characterEncoding=utf-8&serverTimezone=GMT%2B8&useSSL=false
        
 #log
logging:
    level:
      root: INFO
      com.xynet: DEBUG
    file: logs/service-statistics.log
      
#server
server:
  port: 8081  

app:
    fileHome: C:\
    tempFileHome: tmp/
    fileDownloadUrl: C:\

auth:
    #url: http://localhost:7011/remote/authRemoteService
    url: http://192.168.1.253:8899/authentication/remote/authRemoteService
       
eureka:
  client:
    #service-url.defaultZone: http://localhost:8761/eureka      
    service-url.defaultZone: http://admin:123@192.168.1.253:7010/eureka 
        
    enabled: true  
    registerWithEureka: true
    fetchRegistry: true  
    healthcheck.enabled: true
    
  instance:
    lease-renewal-interval-in-seconds: 5
    lease-expiration-duration-in-seconds: 5
    prefer-ip-address: true    
    instance-id: ${spring.cloud.client.ipAddress}:${server.port}      
    
    
    

最上附上項目結構圖:標紅的地方爲要修改的地方sql

相關文章
相關標籤/搜索