zoukankan      html  css  js  c++  java
  • SpringBoot学习笔记:动态数据源切换

    SpringBoot学习笔记:动态数据源切换

    数据源

      Java的javax.sql.DataSource接口提供了一种处理数据库连接的标准方法。通常,DataSource使用URL和一些凭据来建立数据库连接。

      SpringBoot默认提供了针对内存数据库的数据源,如H2、hqldb、Derby等。

    配置数据源信息

      当我们引入spring-boot-start-jdbc时,SpringBoot会默认使用其绑定的Tomcat-JDBC的数据源

      DataSource可由spring.datasource.*中的外部配置属性控制。例如,您可以在application.properties中声明以下部分:

    spring.datasource.url=jdbc:mysql://localhost/test
    spring.datasource.username=dbuser
    spring.datasource.password=dbpass
    spring.datasource.driver-class-name=com.mysql.jdbc.Driver

      当然,我们也可以是使用基于Java代码的配置方法:

        @Bean
        public DataSource mysqlDataSource(){
            DriverManagerDataSource dataSource = new DriverManagerDataSource();
            dataSource.setDriverClassName("com.mysql.jdbc.Driver");
            dataSource.setUrl("jdbc:mysql://localhost:3306/demo");
            dataSource.setUsername("root");
            dataSource.setPassword("123456");
            return dataSource;
        }

    使用第三方数据源

      有时候,我们需要使用第三方的数据源,如Druid、C3P0等,在SpringBoot中也很简单。

      以Druid为例,首先添加Maven依赖:

    <dependencies>
    		<!-- 添加MySQL依赖 -->
    		<dependency>
    			<groupId>mysql</groupId>
    			<artifactId>mysql-connector-java</artifactId>
    		</dependency>
    		<!-- 添加JDBC依赖 -->
    		<dependency>
    			<groupId>org.springframework.boot</groupId>
    			<artifactId>spring-boot-starter-jdbc</artifactId>
    		</dependency>
    		<!-- 添加Druid依赖 -->
    		<dependency>
    			<groupId>com.alibaba</groupId>
    			<artifactId>druid</artifactId>
    			<version>1.1.6</version>
    		</dependency>
    </dependencies>
    

      配置数据库连接信息即可:

    @Configuration
    public class DataSourceConfig {
     
    	@Autowired
    	private Environment env;
     
    	@Bean
    	public DataSource getDataSource() {
    		DruidDataSource dataSource = new DruidDataSource();
    		dataSource.setUrl(env.getProperty("spring.datasource.url"));
    		dataSource.setUsername(env.getProperty("spring.datasource.username"));
    		dataSource.setPassword(env.getProperty("spring.datasource.password"));
    		return dataSource;
    	}
    }

    动态新增数据源

      有时候,我们需要在程序运行过程中动态增加或改变数据源。

    改变数据源

      Spring2.0 引入了AbstractRoutingDataSource,该类可以充当DataSource的路由中介,能在运行时,动态切换当前数据源。如下为其核心代码,可见他是根据determineCurrentLookupKey()获取Key值,来从resolvedDataSources中找到要切换的数据源

    private Map<Object, Object> targetDataSources;
    private Map<Object, DataSource> resolvedDataSources;
    
    protected DataSource determineTargetDataSource() {
            Assert.notNull(this.resolvedDataSources, "DataSource router not initialized");
            Object lookupKey = this.determineCurrentLookupKey();
            DataSource dataSource = (DataSource)this.resolvedDataSources.get(lookupKey);
            if (dataSource == null && (this.lenientFallback || lookupKey == null)) {
                dataSource = this.resolvedDefaultDataSource;
            }
    
            if (dataSource == null) {
                throw new IllegalStateException("Cannot determine target DataSource for lookup key [" + lookupKey + "]");
            } else {
                return dataSource;
            }
        }
    

      而这个determineCurrentLookupKey函数需要我们自已来实现,所以我们要定义一个子类DynamicRoutingDataSource来继承AbstractRoutingDataSource,然后实现这个方法,其实就是返回一个Key字符串!。

      为了保证线程安全,我们可以把数据源的Key信息保存在ThreadLocal中,如下:

    public class DynamicDataSourceContextHolder {
        private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>() {
            @Override
            protected String initialValue() {
                return "dynamic_db0";
            }
        };
    
        /**
         * To switch DataSource
         *
         * @param key the key
         */
        public static void setDataSourceKey(String key) {
            contextHolder.set(key);
        }
    
        /**
         * Get current DataSource
         *
         * @return data source key
         */
        public static String getDataSourceKey() {
            return contextHolder.get();
        }
    
        /**
         * To set DataSource as default
         */
        public static void clearDataSourceKey() {
            contextHolder.remove();
        }
    
    }

      这样,determineCurrentLookupKey就可以如下定义:

        @Override
        protected Object determineCurrentLookupKey() {
            System.out.println("Current DataSource is [{}]"+ DynamicDataSourceContextHolder.getDataSourceKey());
            return DynamicDataSourceContextHolder.getDataSourceKey();
        }
    

      所以我们在使用时,只需要使用DynamicDataSourceContextHolder.setDataSourceKey(),并可以切换数据源。

    新增数据源

      新增数据源,其实就是更新targetDataSources字段,一个简单的Demo如下:

    /**
         * 动态增加数据源
         *
         * @param map 数据源属性
         * @return
         */
        public synchronized boolean addDataSource(Map<String, String> map) {
            try {
                String database = map.get("database");//获取要添加的数据库名
                if (database==null||database.equals("")) return false;
                if (DynamicRoutingDataSource.isExistDataSource(database)) return true;
                DriverManagerDataSource dataSource = new DriverManagerDataSource();
                dataSource.setDriverClassName("com.mysql.jdbc.Driver");
                dataSource.setUrl(map.get("url"));
                dataSource.setUsername(map.get("username"));
                dataSource.setPassword(map.get("password"));
    
                Map<Object, Object> targetMap = DynamicRoutingDataSource.targetDataSources;
                targetMap.put(database, dataSource);
                // 当前 targetDataSources 与 父类 targetDataSources 为同一对象 所以不需要set
    //          this.setTargetDataSources(targetMap);
                this.afterPropertiesSet();
            } catch (Exception e) {
                logger.error(e.getMessage());
                return false;
            }
            return true;
        }
    

     

    配置动态数据源

      我们可以将其以Bean形式配置,并在其中设置默认数据源。

        @Bean("dynamicDataSource")
        public DynamicRoutingDataSource dynamicDataSource() {
            DynamicRoutingDataSource dynamicRoutingDataSource = new DynamicRoutingDataSource();
            Map<Object, Object> dataSourceMap = new HashMap<>();
            DriverManagerDataSource dataSource = new DriverManagerDataSource();
            dataSource.setDriverClassName("com.mysql.jdbc.Driver");
            dataSource.setUrl("jdbc:mysql://localhost:3306/demo");
            dataSource.setUsername("root");
            dataSource.setPassword("123456");
            dataSourceMap.put("DEMO", dataSource);
            dynamicRoutingDataSource.setDefaultTargetDataSource(dataSource);// 设置默认数据源
            dynamicRoutingDataSource.setTargetDataSources(dataSourceMap);
            return dynamicRoutingDataSource;
        }

      之后,我们在代码中便可以执行新增数据源、切换数据源操作等等。

    参考文档:

  • 相关阅读:
    POJ3320 Jessica's Reading Problem
    POJ3320 Jessica's Reading Problem
    CodeForces 813B The Golden Age
    CodeForces 813B The Golden Age
    An impassioned circulation of affection CodeForces
    An impassioned circulation of affection CodeForces
    Codeforces Round #444 (Div. 2) B. Cubes for Masha
    2013=7=21 进制转换
    2013=7=15
    2013=7=14
  • 原文地址:https://www.cnblogs.com/MrSaver/p/10859874.html
Copyright © 2011-2022 走看看