JdbcTemplate动态多数据源配置

一、前言

多数据源的配置,是一个相对比较常见的需求。

什么是数据源?数据源就是javax.sql.DataSource,所有实现了这个接口的DataSource就叫做数据源,现在比较常用阿里巴巴的DruidDataSource,支持监控多数据源下的sql运行状况,便于以此实现以sql为核心的应用系统,比如BI报表系统、BI工具、ETL工具等。而这些场景下的业务sql通常是属于动态数据源,它们的操作对象来自于不同的数据库类型,或不同的数据库实例,通常被存放在某个业务表中,还可能需要被新增、删除和修改,因而我们不能像使用Mybatis那样,预先定义好要执行的所有sql,然后放在Mybatis的mapper配置文件中,这种Jdbc操作方式就显得不恰当,换用spring-jdbc的JdbcTemplate刚好可以完美地解决这个问题。

二、实现思路

2.1 配置数据源属性

这里选用DruidDataSource作为我们的业务数据源,一般在application.properties中为其配置各项Druid连接池属性,如下:

2.2 自定义业务数据源Bean

实现自己的DruidDataSource,加载上面的Druid数据源配置,初始化DruidDataSource实例,以线程安全的方式缓存在一Map<String, DruidDataSource>中,并提供获取数据源和关闭数据源的方法。如下:

package com.cjia.common.jdbc;

import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.util.StringUtils;
import com.cjia.common.exception.SourceException;
import com.cjia.model.SqlDBConfig;
import com.cjia.utils.MD5Util;

import lombok.extern.slf4j.Slf4j;

@Slf4j
@Component
public class JdbcDataSource extends DruidDataSource {

    @Value("${spring.datasource.type}")
    private String type;

    @Value("${source.max-active:10}")
    private int maxActive;

    @Value("${source.initial-size:5}")
    private int initialSize;

    @Value("${source.min-idle:3}")
    private int minIdle;

    @Value("${source.max-wait:30000}")
    private int maxWait;

    @Value("${spring.datasource.druid.time-between-eviction-runs-millis}")
    private int timeBetweenEvictionRunsMillis;

    @Value("${spring.datasource.druid.min-evictable-idle-time-millis}")
    private int minEvictableIdleTimeMillis;

    @Value("${spring.datasource.druid.test-while-idle}")
    private boolean testWhileIdle;

    @Value("${spring.datasource.druid.test-on-borrow}")
    private boolean testOnBorrow;

    @Value("${spring.datasource.druid.test-on-return}")
    private boolean testOnReturn;

    @Value("${spring.datasource.druid.filters}")
    private String filters;

    @Value("${source.break-after-acquire-failure:true}")
    private boolean breakAfterAcquireFailure;

    @Value("${source.connection-error-retry-attempts:0}")
    private int connectionErrorRetryAttempts;

    private static volatile Map<String, DruidDataSource> map = new HashMap<>();

    public synchronized void removeDatasource(String jdbcUrl, String username, String password) {
        String key = getKey(jdbcUrl, username, password);
        if (map.containsKey(key)) {
            map.remove(key);
        }
    }

    public synchronized DruidDataSource getDataSource(SqlDBConfig sqlDBConfig) throws SourceException {
    	String jdbcUrl = sqlDBConfig.getUrl();
    	String username = sqlDBConfig.getUsername();
    	String password = sqlDBConfig.getPassword();
    	String key = getKey(jdbcUrl, username, password);
        if (!map.containsKey(key) || null == map.get(key)) {
            DruidDataSource instance = new JdbcDataSource();
            String className = null;
            try {
                className = DriverManager.getDriver(jdbcUrl.trim()).getClass().getName();
            } catch (SQLException e) {
            }
            if (StringUtils.isEmpty(className)) {
            	throw new SourceException("Driver Class Not null: DbId=" + sqlDBConfig.getDbId());
            } else {
                instance.setDriverClassName(className);
            }
            instance.setUrl(jdbcUrl.trim());
            instance.setUsername(username);
            instance.setPassword(password);
            instance.setInitialSize(initialSize);
            instance.setMinIdle(minIdle);
            instance.setMaxActive(maxActive);
            instance.setMaxWait(maxWait);
            instance.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
            instance.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
            instance.setTestWhileIdle(false);
            instance.setTestOnBorrow(testOnBorrow);
            instance.setTestOnReturn(testOnReturn);
            try {
				instance.setFilters(filters);
			} catch (SQLException ex) {
				log.error("druid configuration initialization filter", ex);
			}
            instance.setConnectionErrorRetryAttempts(connectionErrorRetryAttempts);
            instance.setBreakAfterAcquireFailure(breakAfterAcquireFailure);

            try {
                instance.init();
            } catch (Exception e) {
                log.error("Exception during pool initialization", e);
                throw new SourceException(e.getMessage());
            }
            map.put(key, instance);
        }
        return map.get(key);
    }

    private String getKey(String jdbcUrl, String username, String password) {
        StringBuilder sb = new StringBuilder();
        if (!StringUtils.isEmpty(username)) {
            sb.append(username);
        }
        if (!StringUtils.isEmpty(password)) {
            sb.append(":").append(password);
        }
        sb.append("@").append(jdbcUrl.trim());
        return MD5Util.md5(sb.toString(), MD5Util.SEC_KEY);
    }
}

2.3 动态构造JdbcTemplate

首先,注入上一步自定义的业务数据源JdbcDataSource,接着,JdbcTemplate提供了传入DataSource的构造方式,获取到JdbcTemplate对象后,调用它丰富的API执行业务sql,如下:

// 获取初始化后的druid数据源,SqlDBConfig存放了jdbcUrl、username、password
DruidDataSource dataSource = jdbcDataSource.getDataSource(sqlDBConfig);
List<Map<String, Object>> result = new JdbcTemplate(dataSource).queryForList(sql, paramArgs);

以上。

发布了62 篇原创文章 · 获赞 22 · 访问量 7万+

猜你喜欢

转载自blog.csdn.net/songzehao/article/details/100548930
今日推荐