• 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}),这个很关键不然加载不了多数据源,只会调用默认数据源

    @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中上否则不会生效

    
    

    @Service
    public class BigDataServiceImpl implements BigDataService {

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

    application-dev.yaml 配置文件如下:

    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}      
        
        
        

    最上附上项目结构图:标红的地方为要修改的地方

  • 相关阅读:
    mysql中删除重复记录,并保留重复数据中的一条数据的SQL语句
    架构设计文档提纲简描
    各大互联网公司架构演进之路汇总
    大数据安全规范
    生活启示
    mysql数据库分库分表(Sharding)
    大数据风控指标----查准率与查全率
    YARN和MapReduce的内存设置参考
    不要在linux上启用net.ipv4.tcp_tw_recycle参数
    [Spark经验一]Spark RDD计算使用的函数里尽量不要使用全局变量
  • 原文地址:https://www.cnblogs.com/aegisada/p/10584719.html
Copyright © 2020-2023  润新知