zoukankan      html  css  js  c++  java
  • springboot利用AbstractRoutingDataSource实现动态切换数据源

    需求:

    编写一个代码生成器,前端下拉选择需要自动生成代码的数据库名。后端切换数据库并且生成对应的代码。

    动态切换数据源:

    springboot提供了一个AbstractRoutingDataSource类。我们可以实现一个类继承AbstractRoutingDataSource并且determineCurrentLookUpKey()方法。

    具体步骤:

    数据源配置

    spring:
      datasource:
        type: com.alibaba.druid.pool.DruidDataSource
        #MySQL配置
        driverClassName: com.mysql.jdbc.Driver
        url: jdbc:mysql://127.0.0.1:3306/graduate?useUnicode=true&characterEncoding=UTF-8&useSSL=false
        username: root
        password: root
        #MySQL配置
        pqmanager:
          driverClassName: com.mysql.jdbc.Driver
          url: jdbc:mysql://127.0.0.1:3306/hfb?useUnicode=true&characterEncoding=UTF-8&useSSL=false
          username: root
          password: root
        pq37:
          driverClassName: com.mysql.jdbc.Driver
          url: jdbc:mysql://127.0.0.1:3306/sys?useUnicode=true&characterEncoding=UTF-8&useSSL=false
          username: root
          password: root
        pqmanager37:
          driverClassName: com.mysql.jdbc.Driver
          url: jdbc:mysql://127.0.0.1:3306/srb_core?useUnicode=true&characterEncoding=UTF-8&useSSL=false
          username: root
          password: root

    编写 DataSourceContextHolder设置和保存当前线程使用的数据源

    public class DataSourceContextHolder {
    
        //默认数据源
        private static final String DEFAULT_DATASOURCE = "pq";
    
        //保存线程连接的数据源
        private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>();
    
        public static String getDataSource() {
            return CONTEXT_HOLDER.get();
        }
    
        public static void setDataSource(String key) {
            CONTEXT_HOLDER.set(key);
        }
    
        public static void cleanDataSource() {
            CONTEXT_HOLDER.remove();
        }
    }

    编写DynamicDataSource 

    public class DynamicDataSource extends AbstractRoutingDataSource {
    
        /**
         * @return 切换数据源的时候该方法会被调用
         */
        @Override
        protected Object determineCurrentLookupKey() {
            return DataSourceContextHolder.getDataSource();
        }
    
    
    }

    配置类

    /**
     * 多数据源配置类
     */
    @Configuration
    public class DataSourceConfig {
    
        //@Primary
        @Bean(name = "pq")
        @ConfigurationProperties(prefix = "spring.datasource")
        public DataSource dataSourcePq() {
            return new DruidDataSource();
        }
    
        @Bean("pq_37")
        @ConfigurationProperties("spring.datasource.pq37")
        public DataSource dataSourcePq37() {
            return new DruidDataSource();
        }
    
        @Bean("pq_manager_37")
        @ConfigurationProperties("spring.datasource.pqmanager37")
        public DataSource dataSourcePqManager37() {
            return new DruidDataSource();
        }
    
        @Primary //必须有一个数据源标记为Primary
        @Bean("pq_manager")
        @ConfigurationProperties("spring.datasource.pqmanager")
        public DataSource dataSourcePqManager() {
            return new DruidDataSource();
        }
    
        /**
         * 数据源选择器 如果此处标记@Primary会导致循环依赖问题
         *
         * @return
         */
        @Bean(name = "dynamicDataSource")
        public DataSource dynamicDataSource(@Qualifier("pq") DataSource PqSource,
                                            @Qualifier("pq_manager") DataSource PqManagerSource,
                                            @Qualifier("pq_37") DataSource PqSource37,
                                            @Qualifier("pq_manager_37") DataSource PqManagerSource37) {
            DynamicDataSource dynamicDataSource = new DynamicDataSource();
            //配置默认数据源
            dynamicDataSource.setDefaultTargetDataSource(PqSource);
            //保存所有可切换的数据源
            Map<Object, Object> dataSourceMap = new HashMap<>();
            dataSourceMap.put("pq", PqSource);
            dataSourceMap.put("pq_manager", PqManagerSource);
            dataSourceMap.put("pq_37", PqSource37);
            dataSourceMap.put("pq_manager_37", PqManagerSource37);
            dynamicDataSource.setTargetDataSources(dataSourceMap);
            return dynamicDataSource;
        }
    
    
    }
    @EnableTransactionManagement
    @Configuration
    public class MyBatisConfig {
    
        @Resource(name = "dynamicDataSource")
        private DataSource dynamicDataSource;
    
        @Autowired
        private MybatisProperties mybatisProperties;
    
        @Bean
        public SqlSessionFactory sqlSessionFactory() throws Exception {
            SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
            sqlSessionFactoryBean.setDataSource(dynamicDataSource);
            sqlSessionFactoryBean.setMapperLocations(mybatisProperties.resolveMapperLocations());
            sqlSessionFactoryBean.setConfiguration(mybatisProperties.getConfiguration());
            return sqlSessionFactoryBean.getObject();
        }
    
        @Bean
        public PlatformTransactionManager platformTransactionManager() {
            return new DataSourceTransactionManager(dynamicDataSource);
        }
    
    }

    利用拦截器拦截请求,查看请求参数是否存在某种参数(代表需要切换数据源,不存在则为默认数据源)。也可以使用AOP的方法作用于某个方法,

    利用自定义注解配置需要切换的数据源,在切面那里只需利用反射得到对应的数据源在进行切换。

    /**
     * 拦截请求切换数据源
     */
    public class DataSourceInterceptor extends HandlerInterceptorAdapter {
    
        /**
         * 拦截请求
         *
         * @param request
         * @param response
         * @param handler
         * @return
         * @throws Exception
         */
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            String dataSourceKey = request.getParameter("dataSourceKey");
            System.out.println(dataSourceKey);
            if (StringUtils.isNotEmpty(dataSourceKey)) {
                DataSourceContextHolder.setDataSource(dataSourceKey);//重点
            }
            return true;
        }
    
        @Override
        public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
    
    
            // System.out.println(DataSourceContextHolder.getDataSource());
            DataSourceContextHolder.cleanDataSource();
        }
    
        @Override
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
            super.afterCompletion(request, response, handler, ex);
        }
    }

    配置拦截器

    @Configuration
    public class InterceptorsConfig implements WebMvcConfigurer {
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            InterceptorRegistration registration = registry.addInterceptor(new DataSourceInterceptor());
            //拦截所有路径
            registration.addPathPatterns("/sys/**");
        }
    }

    执行自动生成代码逻辑。

  • 相关阅读:
    Reusable action with query database
    Oracle实现分组统计记录
    Oracle行列转换的几种实现方法
    junit私有方法测试
    Junit实现抽象类测试(二)
    C++的性能C#的产能?! .Net Native 系列《二》:.NET Native开发流程详解
    C++的性能C#的产能?! .Net Native 系列向导
    c++的性能, c#的产能?!鱼和熊掌可以兼得,.NET NATIVE初窥
    辞职敬礼
    WPF 心路历程
  • 原文地址:https://www.cnblogs.com/swqblog/p/14824621.html
Copyright © 2011-2022 走看看