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;
        }

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

    参考文档:

  • 相关阅读:
    jquery遍历节点 children(),next(),prev(),siblings()closest() 等一些常用方法...
    jq 分页
    java8 array、list操作 汇【4】)- Java8 Lambda表达式 函数式编程【思想】
    java8 array、list操作 汇【2】)- (Function,Consumer,Predicate,Supplier)应用
    Java8 (Function,Consumer,Predicate,Supplier)详解
    Elasticsearch
    org.apache.commons.lang3.Validate
    freemarker逻辑判断写法#if
    11 Sping框架--AOP的相关概念及其应用
    10 Spring框架--基于注解的IOC配置
  • 原文地址:https://www.cnblogs.com/MrSaver/p/10859874.html
Copyright © 2011-2022 走看看