• 基于Spring的动态路由AbstractRoutingDataSource实现动态分库


    1.定义数据库配置文件

    server.port=8081
    
    spring.datasource.jdbcUrl=jdbc:mysql://******:3306/spring-db-2019?useUnicode=true&characterEncoding=utf8
    spring.datasource.username=root
    spring.datasource.password=*****
    spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
    
    y2018.spring.datasource.jdbcUrl=jdbc:mysql://******:3306/spring-db-2018?useUnicode=true&characterEncoding=utf8
    y2018.spring.datasource.username=root
    y2018.spring.datasource.password=*****
    y2018.spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
    
    logging.level.com.ijianghu.dynamic.web=debug
    
    

    2.配置多数据源、sessionFactory和事务管理器

    package com.ijianghu.dynamic.web.config;
    
    import org.apache.ibatis.session.SqlSessionFactory;
    import org.mybatis.spring.SqlSessionFactoryBean;
    import org.springframework.beans.factory.annotation.Autowired;
    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 org.springframework.transaction.PlatformTransactionManager;
    
    import javax.sql.DataSource;
    import java.util.HashMap;
    
    @Configuration
    public class BaseDataSourceConfig {
    
        /**
         * 配置默认数据源,并设置高优先级
         * @return
         */
        @Primary
        @Bean
        @ConfigurationProperties(prefix = "spring.datasource")
        public DataSource dataSource(){
            return DataSourceBuilder.create().build();
        }
    
        /**
         * 配置多数据源
         * @return
         */
        @Bean("y2018DataSource")
        @ConfigurationProperties(prefix = "y2018.spring.datasource")
        public DataSource y2018DataSource(){
            return DataSourceBuilder.create().build();
        }
    
        /**
         * 设置多数据源,配置动态路由数据源
         * @return
         */
        @Bean("dynamincDataSource")
        public DataSource dynamincDataSource(){
            DynamicRoutingDataSource dynamicRoutingDataSource = new DynamicRoutingDataSource();
            HashMap<Object, Object> dataSourceMap = new HashMap<>();
            dataSourceMap.put("dataSource",dataSource());
            dataSourceMap.put("y2018DataSource",y2018DataSource());
            //配置默认目标数据源
            dynamicRoutingDataSource.setDefaultTargetDataSource(dataSource());
            //设置目标数据源集合,供路由选择
            dynamicRoutingDataSource.setTargetDataSources(dataSourceMap);
            return dynamicRoutingDataSource;
        }
    
        /**
         * 设置会话工厂
         * @return
         * @throws Exception
         */
        @Bean(name="sqlSessionFactory")
        public SqlSessionFactory sqlSessionFactory() throws Exception {
            SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
            //配置数据源为多数据源
            factoryBean.setDataSource(dynamincDataSource());
            //设置mybaits的xml文件路径
            factoryBean.setMapperLocations(new PathMatchingResourcePatternResolver()
                    .getResources("classpath:mapper/*.xml"));
            return factoryBean.getObject();
        }
    
        /**
         * 配置事务管理器
         * @return
         */
        @Bean(name="transactionManager")
        public PlatformTransactionManager transactionManager(){
            return new DataSourceTransactionManager(dynamincDataSource());
        }
    
    }
    
    
    
    

    3.继承AbstractRoutingDataSource类,实现选择目标数据源

    package com.ijianghu.dynamic.web.config;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
    
    /**
     * Multiple DataSource Configurer
     */
    public class DynamicRoutingDataSource extends AbstractRoutingDataSource {
    
        private final Logger logger = LoggerFactory.getLogger(getClass());
    
        @Override
        protected Object determineCurrentLookupKey() {
                
            logger.info("Current DataSource is {}",DynamicDataSourceContextHolder.getDataSourceKey());
            //从动态数据源上下文持有者里面获取
            return DynamicDataSourceContextHolder.getDataSourceKey();
        }
    }
    

    4.配置多数据源上下文持有者

    package com.ijianghu.dynamic.web.config;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * Multiple DataSource Context Holder
     */
    public class DynamicDataSourceContextHolder {
    
        private final Logger logger = LoggerFactory.getLogger(getClass());
    
        /**
         * Maintain variable for every thread, to avoid effect other thread
         */
        private static ThreadLocal<String> CONTEXT_HOLDER = ThreadLocal.withInitial(DataSourceKey.dataSource::name);;
    
    
        /**
         * All DataSource List
         */
        public static List<Object> dataSourceKeys = new ArrayList<>();
    
        /**
         * Get current DataSource
         *
         * @return data source key
         */
        public static String getDataSourceKey() {
            return CONTEXT_HOLDER.get();
        }
    
        /**
         * To switch DataSource
         *
         * @param key the key
         */
        public static void setDataSourceKey(String key) {
            CONTEXT_HOLDER.set(key);
        }
    
        /**
         * To set DataSource as default
         */
        public static void clearDataSourceKey() {
            CONTEXT_HOLDER.remove();
        }
    
        /**
         * Check if give DataSource is in current DataSource list
         *
         * @param key the key
         * @return boolean boolean
         */
        public static boolean containDataSourceKey(String key) {
            return dataSourceKeys.contains(key);
        }
    
        /**
         * Use slave data source.
         */
        public static void useSlaveDataSource(String ds) {
            DataSourceKey dataSourceKey = DataSourceKey.valueOf(ds);
            //if there is no suitable enum,then use default dataSource
            if(dataSourceKey == null){
                setDataSourceKey(DataSourceKey.dataSource.dataSourceName);
            }
            setDataSourceKey(dataSourceKey.dataSourceName);
        }
    
        /**
         * Use master data source.
         */
        public static void useMasterDataSource() {
            CONTEXT_HOLDER.set(DataSourceKey.dataSource.dataSourceName);
        }
    
    }
    

    5.根据数据源名称,配置多数据源枚举类,根据参数获取对应的数据源名称

    package com.ijianghu.dynamic.web.config;
    
    public enum DataSourceKey {
        dataSource("dataSource","dataSource"),
        y2018("key","y2018DataSource");
    
        public String key;
        public String dataSourceName;
        DataSourceKey(String key, String dataSourceName){
            this.key = key;
            this.dataSourceName = dataSourceName;
        }
    }
    

    6.配置AOP切面,根据参数设置对应的数据源名称

    package com.ijianghu.dynamic.web.aop;
    
    
    import com.ijianghu.dynamic.web.config.DynamicDataSourceContextHolder;
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    import org.aspectj.lang.annotation.Pointcut;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.core.annotation.Order;
    import org.springframework.stereotype.Component;
    import org.springframework.web.context.request.RequestContextHolder;
    import org.springframework.web.context.request.ServletRequestAttributes;
    
    import javax.servlet.http.HttpServletRequest;
    
    @Aspect
    @Order(-1)
    @Component
    public class DynamicDataSourceAspect {
    
        private final Logger logger = LoggerFactory.getLogger(getClass());
    
    
        @Pointcut("@annotation(org.springframework.web.bind.annotation.PostMapping)||" +
                "@annotation(org.springframework.web.bind.annotation.GetMapping)||" +
                "@annotation(org.springframework.web.bind.annotation.RequestMapping)")
        public void daoAspect(){ }
    
    
        @Before("daoAspect()")
        public void switchDataSource(JoinPoint point){
            HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
            String ds = request.getHeader("ds");
            if(ds != null){
                DynamicDataSourceContextHolder.useSlaveDataSource(ds);
            }else{
                DynamicDataSourceContextHolder.useMasterDataSource();
            }
    
        }
    
    }
    

    7.关键点

    1、数据源是如何切换的

    根据参数获取到对应的数据源名称,然后动态数据源路由根据获取到的name,从数据源集合里面获取对应的dataSource

  • 相关阅读:
    tcpdump高级过滤
    Flask简单学习
    nginx+keepalived高可用web负载均衡
    Golang基础(5):Go语言反射规则
    Golang基础(4):Go结构体
    分布式SESSION一致性
    JSON WEB TOKEN (JWT)
    Table布局
    GRID布局
    三种方式实现轮播图功能
  • 原文地址:https://www.cnblogs.com/nangonghui/p/12975786.html
Copyright © 2020-2023  润新知