动态配置多数据源-从数据库读取连接信息

转载网友的,文章附原文链接,个人找了很久没有找到好的方法解决,
配置多个数据源,每次有新的项目,必须修改程序配置文件,
使用原始的JDBC有太繁琐,
转载过来大家一起学习,交流!
需要我的demo的也可以联系!

一. 背景
公司要求开发一个接口平台,作为我司各系统之间,或我司系统与第三方系统的对接工具。使用微服务架构,通过该接口平台可动态定义并生成restful接口。

二. 思考
系统之间的对接,无外乎就是把己方系统的数据发送给第三方或接收第三方系统发送过来的数据(加密签名等机制本文不谈)。作为一个支持动态定义接口的平台,应该有自己的数据库设计,它不应该和业务系统的耦合度太高。因此,最终决定方案如下:

本平台独立运行且有自己独立的数据库,即不能把本平台的表建立在业务系统的库里面;
可登陆平台创建数据源信息(数据库连接信息),并给每个数据源分配一个唯一的code,支持Oracle、Mysql、SQLServer三种数据库类型;
数据源创建成功后,可以针对该数据源定义增删改查接口,分别对应restful的post、delete、put、get请求;
接口定义完成后,自动生成接口地址,平台接收到调用者的请求后,从请求URL(接口地址)解析出要操作的数据源code,然后基于Durid创建对应的数据库连接池(由于创建数据库连接池是比较费时的操作,因此仅当该数据源第一次被使用时才创建它的连接池,后期不会重复创建),连接池创建完成后再继续后续的操作。
三. 实现
为了方便区分,我们把平台自身的数据源称为“主数据源”,动态创建的数据源称为“客数据源”。

    第1步:在application.properties配置主数据源信息

   spring.datasource.url=jdbc:oracle:thin:@127.0.0.1:1521:orcl
   spring.datasource.username=adi
   spring.datasource.password=adipassword
   spring.datasource.driverClassName = oracle.jdbc.OracleDriver

   spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
   spring.datasource.initialSize=20
   spring.datasource.minIdle=10
   spring.datasource.maxActive=50
   spring.datasource.maxWait=60000



  第2步:创建一个数据源实体类DataSource(类名可以改)

  该类的关键字段:

  url:数据库地址

  userName:数据库用户名

  passWord:数据库密码

  code:数据源编码,保证唯一

  databasetype:数据库类型,支持oracle、mysql、sqlserver2000、sqlserver



   第3步:创建动态数据源类DynamicDataSource(类名可以改)

  注意:这里的动态数据源不是“客数据源”,动态数据源类有一个“袋子”,用来装具体的数据源对象,也就是说可以装主数据源对象和各个客数据源对象。

 该类必须继承AbstractRoutingDataSource,其中的奥妙请参考

http://blog.csdn.net/rj042/article/details/21654627 (感谢这位博主!向您学习了不少!)

代码:

package com.bitservice.adi.datasource;

import java.sql.Connection;
import java.sql.DriverManager;
import java.util.Map;
import java.util.Set;

import javax.sql.DataSource;

import org.apache.log4j.Logger;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.stat.DruidDataSourceStatManager;
import com.bitservice.adi.exception.ADIException;
import com.bitservice.adi.security.Base64;
import com.bitservice.adi.security.SecurityTools;
import com.bitservice.adi.util.ADIPropUtil;
import com.bitservice.adi.util.DBUtil;
import com.bitservice.adi.util.NullUtil;

public class DynamicDataSource extends AbstractRoutingDataSource {
private boolean debug = false;
private final Logger log = Logger.getLogger(getClass());
private Map<Object, Object> dynamicTargetDataSources;
private Object dynamicDefaultTargetDataSource;
@Override
protected Object determineCurrentLookupKey() {
String datasource = DBContextHolder.getDataSource();
if (debug) {
if (NullUtil.IsAllNotNullOfString(datasource)) {
Map<Object, Object> dynamicTargetDataSources2 = this.dynamicTargetDataSources;
if (dynamicTargetDataSources2.containsKey(datasource)) {
log.info("---当前数据源:" + datasource + "---");
} else {
throw new ADIException("不存在的数据源:"+datasource,500);
}
} else {
log.info("---当前数据源:默认数据源---");
}
}
return datasource;
}

@Override
public void setTargetDataSources(Map<Object, Object> targetDataSources) {
    super.setTargetDataSources(targetDataSources);
    this.dynamicTargetDataSources = targetDataSources;
}
// 创建数据源
public boolean createDataSource(String key, String driveClass, String url, String username, String password, String databasetype) {
    try {
        try { // 排除连接不上的错误
            Class.forName(driveClass);
            DriverManager.getConnection(url, username, password);// 相当于连接数据库
        } catch (Exception e) {
            return false;
        }
        @SuppressWarnings("resource")
        DruidDataSource druidDataSource = new DruidDataSource();
        
        druidDataSource.setName(key);
        druidDataSource.setDriverClassName(driveClass);
        druidDataSource.setUrl(url);
        druidDataSource.setUsername(username);
        druidDataSource.setPassword(password);
        druidDataSource.setInitialSize(50); //初始化时建立物理连接的个数。初始化发生在显示调用init方法,或者第一次getConnection时
        druidDataSource.setMaxActive(200); //最大连接池数量
        druidDataSource.setMaxWait(60000); //获取连接时最大等待时间,单位毫秒。当链接数已经达到了最大链接数的时候,应用如果还要获取链接就会出现等待的现象,等待链接释放并回到链接池,如果等待的时间过长就应该踢掉这个等待,不然应用很可能出现雪崩现象
        druidDataSource.setMinIdle(40); //最小连接池数量
        String validationQuery = "select 1 from dual";
        if("mysql".equalsIgnoreCase(databasetype)) {
            driveClass = DBUtil.mysqldriver;
            validationQuery = "select 1";
        } else if("oracle".equalsIgnoreCase(databasetype)){
            driveClass = DBUtil.oracledriver;
            druidDataSource.setPoolPreparedStatements(true); //是否缓存preparedStatement,也就是PSCache。PSCache对支持游标的数据库性能提升巨大,比如说oracle。在mysql下建议关闭。
            druidDataSource.setMaxPoolPreparedStatementPerConnectionSize(50);
            int sqlQueryTimeout = ADIPropUtil.sqlQueryTimeOut();
            druidDataSource.setConnectionProperties("oracle.net.CONNECT_TIMEOUT=6000;oracle.jdbc.ReadTimeout="+sqlQueryTimeout);//对于耗时长的查询sql,会受限于ReadTimeout的控制,单位毫秒
        } else if("sqlserver2000".equalsIgnoreCase(databasetype)){
            driveClass = DBUtil.sql2000driver;
            validationQuery = "select 1";
        } else if("sqlserver".equalsIgnoreCase(databasetype)){
            driveClass = DBUtil.sql2005driver;
            validationQuery = "select 1";
        }
        
        druidDataSource.setTestOnBorrow(true); //申请连接时执行validationQuery检测连接是否有效,这里建议配置为TRUE,防止取到的连接不可用
        druidDataSource.setTestWhileIdle(true);//建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。
        druidDataSource.setValidationQuery(validationQuery); //用来检测连接是否有效的sql,要求是一个查询语句。如果validationQuery为null,testOnBorrow、testOnReturn、testWhileIdle都不会起作用。
        druidDataSource.setFilters("stat");//属性类型是字符串,通过别名的方式配置扩展插件,常用的插件有:监控统计用的filter:stat日志用的filter:log4j防御sql注入的filter:wall
        druidDataSource.setTimeBetweenEvictionRunsMillis(60000); //配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
        druidDataSource.setMinEvictableIdleTimeMillis(180000); //配置一个连接在池中最小生存的时间,单位是毫秒,这里配置为3分钟180000
        druidDataSource.setKeepAlive(true); //打开druid.keepAlive之后,当连接池空闲时,池中的minIdle数量以内的连接,空闲时间超过minEvictableIdleTimeMillis,则会执行keepAlive操作,即执行druid.validationQuery指定的查询SQL,一般为select * from dual,只要minEvictableIdleTimeMillis设置的小于防火墙切断连接时间,就可以保证当连接空闲时自动做保活检测,不会被防火墙切断
        
        druidDataSource.setRemoveAbandoned(true); //是否移除泄露的连接/超过时间限制是否回收。
        druidDataSource.setRemoveAbandonedTimeout(3600); //泄露连接的定义时间(要超过最大事务的处理时间);单位为秒。这里配置为1小时
        druidDataSource.setLogAbandoned(true); ////移除泄露连接发生是是否记录日志
        
        DataSource createDataSource = (DataSource) druidDataSource;
        druidDataSource.init();
        Map<Object, Object> dynamicTargetDataSources2 = this.dynamicTargetDataSources;
        dynamicTargetDataSources2.put(key, createDataSource);// 加入map
        setTargetDataSources(dynamicTargetDataSources2);// 将map赋值给父类的TargetDataSources
        super.afterPropertiesSet();// 将TargetDataSources中的连接信息放入resolvedDataSources管理
        log.info(key+"数据源初始化成功");
        //log.info(key+"数据源的概况:"+druidDataSource.dump());
        return true;
    } catch (Exception e) {
        log.error(e + "");
        return false;
    }
}
// 删除数据源
public boolean delDatasources(String datasourceid) {
    Map<Object, Object> dynamicTargetDataSources2 = this.dynamicTargetDataSources;
    if (dynamicTargetDataSources2.containsKey(datasourceid)) {
        Set<DruidDataSource> druidDataSourceInstances = DruidDataSourceStatManager.getDruidDataSourceInstances();
        for (DruidDataSource l : druidDataSourceInstances) {
            if (datasourceid.equals(l.getName())) {
                dynamicTargetDataSources2.remove(datasourceid);
                DruidDataSourceStatManager.removeDataSource(l);
                setTargetDataSources(dynamicTargetDataSources2);// 将map赋值给父类的TargetDataSources
                super.afterPropertiesSet();// 将TargetDataSources中的连接信息放入resolvedDataSources管理
                return true;
            }
        }
        return false;
    } else {
        return false;
    }
}

// 测试数据源连接是否有效
public boolean testDatasource(String key, String driveClass, String url, String username, String password) {
    try {
        Class.forName(driveClass);
        DriverManager.getConnection(url, username, password);
        return true;
    } catch (Exception e) {
        return false;
    }
}

/**
 * Specify the default target DataSource, if any.
 * <p>
 * The mapped value can either be a corresponding
 * {@link javax.sql.DataSource} instance or a data source name String (to be
 * resolved via a {@link #setDataSourceLookup DataSourceLookup}).
 * <p>
 * This DataSource will be used as target if none of the keyed
 * {@link #setTargetDataSources targetDataSources} match the
 * {@link #determineCurrentLookupKey()} current lookup key.
 */
@Override
public void setDefaultTargetDataSource(Object defaultTargetDataSource) {
    super.setDefaultTargetDataSource(defaultTargetDataSource);
    this.dynamicDefaultTargetDataSource = defaultTargetDataSource;
}

/**
 * @param debug
 *            the debug to set
 */
public void setDebug(boolean debug) {
    this.debug = debug;
}

/**
 * @return the debug
 */
public boolean isDebug() {
    return debug;
}

/**
 * @return the dynamicTargetDataSources
 */
public Map<Object, Object> getDynamicTargetDataSources() {
    return dynamicTargetDataSources;
}

/**
 * @param dynamicTargetDataSources
 *            the dynamicTargetDataSources to set
 */
public void setDynamicTargetDataSources(Map<Object, Object> dynamicTargetDataSources) {
    this.dynamicTargetDataSources = dynamicTargetDataSources;
}

/**
 * @return the dynamicDefaultTargetDataSource
 */
public Object getDynamicDefaultTargetDataSource() {
    return dynamicDefaultTargetDataSource;
}

/**
 * @param dynamicDefaultTargetDataSource
 *            the dynamicDefaultTargetDataSource to set
 */
public void setDynamicDefaultTargetDataSource(Object dynamicDefaultTargetDataSource) {
    this.dynamicDefaultTargetDataSource = dynamicDefaultTargetDataSource;
}

public void createDataSourceWithCheck(com.bitservice.adi.entity.DataSource dataSource) throws Exception {
    String datasourceId = dataSource.getDatasourceId();
    log.info("准备创建数据源"+datasourceId);
    Map<Object, Object> dynamicTargetDataSources2 = this.dynamicTargetDataSources;
    if (dynamicTargetDataSources2.containsKey(datasourceId)) {
        log.info("数据源"+datasourceId+"之前已经创建,准备测试数据源是否正常...");
        //DataSource druidDataSource = (DataSource) dynamicTargetDataSources2.get(datasourceId);
        DruidDataSource druidDataSource = (DruidDataSource) dynamicTargetDataSources2.get(datasourceId);
        boolean rightFlag = true;
        Connection connection = null;
        try {

// log.info(datasourceId+"数据源的概况->当前闲置连接数:"+druidDataSource.getPoolingCount());
// long activeCount = druidDataSource.getActiveCount();
// log.info(datasourceId+"数据源的概况->当前活动连接数:"+activeCount);
// if(activeCount > 0) {
// log.info(datasourceId+"数据源的概况->活跃连接堆栈信息:"+druidDataSource.getActiveConnectionStackTrace());
// }
log.info("准备获取数据库连接...");
connection = druidDataSource.getConnection();
log.info("数据源"+datasourceId+"正常");
} catch (Exception e) {
log.error(e.getMessage(),e); //把异常信息打印到日志文件
rightFlag = false;
log.info("缓存数据源"+datasourceId+"已失效,准备删除...");
if(delDatasources(datasourceId)) {
log.info("缓存数据源删除成功");
} else {
log.info("缓存数据源删除失败");
}
} finally {
if(null != connection) {
connection.close();
}
}
if(rightFlag) {
log.info("不需要重新创建数据源");
return;
} else {
log.info("准备重新创建数据源...");
createDataSource(dataSource);
log.info("重新创建数据源完成");
}
} else {
createDataSource(dataSource);
}

}

private  void createDataSource(com.bitservice.adi.entity.DataSource dataSource) throws Exception {
    String datasourceId = dataSource.getDatasourceId();
    log.info("准备创建数据源"+datasourceId);
    String databasetype = dataSource.getDatabasetype();
    String username = dataSource.getUserName();
    String password = dataSource.getPassWord();
    password = new String(SecurityTools.decrypt(Base64.decode(password)));
    String url = dataSource.getUrl();
    String driveClass = "";
    if("mysql".equalsIgnoreCase(databasetype)) {
        driveClass = DBUtil.mysqldriver;
    } else if("oracle".equalsIgnoreCase(databasetype)){
        driveClass = DBUtil.oracledriver;
    }  else if("sqlserver2000".equalsIgnoreCase(databasetype)){
        driveClass = DBUtil.sql2000driver;
    } else if("sqlserver".equalsIgnoreCase(databasetype)){
        driveClass = DBUtil.sql2005driver;
    }
    if(testDatasource(datasourceId,driveClass,url,username,password)) {
        boolean result = this.createDataSource(datasourceId, driveClass, url, username, password, databasetype);
        if(!result) {
            throw new ADIException("数据源"+datasourceId+"配置正确,但是创建失败",500);
        }
    } else {
        throw new ADIException("数据源配置有错误",500);
    }
}

}

   第4步:创建数据源配置类DruidDBConfig(类名可以改)

   该类在springboot启动时就会实例化,主要功能是创建主数据源对象和第3步的动态数据源对象。动态数据源对象手上有一个“袋子”,用来装具体的数据源对象,通过代码可以看到,我把主数据源对象也放到了这个“袋子”里面。

代码:

package com.bitservice.adi.config;

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

import javax.persistence.EntityManager;
import javax.sql.DataSource;

import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import com.alibaba.druid.pool.DruidDataSource;
import com.bitservice.adi.datasource.DynamicDataSource;

/**

  • DruidDBConfig类被@Configuration标注,用作配置信息; DataSource对象被@Bean声明,为Spring容器所管理,

  • @Primary表示这里定义的DataSource将覆盖其他来源的DataSource。

  • @author aiyo92
    */
    @Configuration
    @EnableTransactionManagement
    public class DruidDBConfig {

    private final Logger log = Logger.getLogger(getClass());

    // adi数据库连接信息
    @Value("{spring.datasource.url}") private String dbUrl; @Value("{spring.datasource.username}")
    private String username;
    @Value("{spring.datasource.password}") private String password; @Value("{spring.datasource.driverClassName}")
    private String driverClassName;

    // 连接池连接信息
    @Value("{spring.datasource.initialSize}") private int initialSize; @Value("{spring.datasource.minIdle}")
    private int minIdle;
    @Value("{spring.datasource.maxActive}") private int maxActive; @Value("{spring.datasource.maxWait}")
    private int maxWait;

    @Bean // 声明其为Bean实例
    @Primary // 在同样的DataSource中,首先使用被标注的DataSource
    @Qualifier("adiDataSource")
    public DataSource dataSource() throws SQLException {
    DruidDataSource datasource = new DruidDataSource();
    // 基础连接信息
    datasource.setUrl(this.dbUrl);
    datasource.setUsername(username);
    datasource.setPassword(password);
    datasource.setDriverClassName(driverClassName);
    // 连接池连接信息
    datasource.setInitialSize(initialSize);
    datasource.setMinIdle(minIdle);
    datasource.setMaxActive(maxActive);
    datasource.setMaxWait(maxWait);

     datasource.setPoolPreparedStatements(true); //是否缓存preparedStatement,也就是PSCache。PSCache对支持游标的数据库性能提升巨大,比如说oracle。在mysql下建议关闭。
     datasource.setMaxPoolPreparedStatementPerConnectionSize(50);
     datasource.setConnectionProperties("oracle.net.CONNECT_TIMEOUT=6000;oracle.jdbc.ReadTimeout=60000");//对于耗时长的查询sql,会受限于ReadTimeout的控制,单位毫秒
     datasource.setTestOnBorrow(true); //申请连接时执行validationQuery检测连接是否有效,这里建议配置为TRUE,防止取到的连接不可用
     datasource.setTestWhileIdle(true);//建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。
     String validationQuery = "select 1 from dual";
     datasource.setValidationQuery(validationQuery); //用来检测连接是否有效的sql,要求是一个查询语句。如果validationQuery为null,testOnBorrow、testOnReturn、testWhileIdle都不会起作用。
     datasource.setFilters("stat,wall");//属性类型是字符串,通过别名的方式配置扩展插件,常用的插件有:监控统计用的filter:stat日志用的filter:log4j防御sql注入的filter:wall
     datasource.setTimeBetweenEvictionRunsMillis(60000); //配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
     datasource.setMinEvictableIdleTimeMillis(180000); //配置一个连接在池中最小生存的时间,单位是毫秒,这里配置为3分钟180000
     datasource.setKeepAlive(true); //打开druid.keepAlive之后,当连接池空闲时,池中的minIdle数量以内的连接,空闲时间超过minEvictableIdleTimeMillis,则会执行keepAlive操作,即执行druid.validationQuery指定的查询SQL,一般为select * from dual,只要minEvictableIdleTimeMillis设置的小于防火墙切断连接时间,就可以保证当连接空闲时自动做保活检测,不会被防火墙切断
     
     datasource.setRemoveAbandoned(true); //是否移除泄露的连接/超过时间限制是否回收。
     datasource.setRemoveAbandonedTimeout(3600); //泄露连接的定义时间(要超过最大事务的处理时间);单位为秒。这里配置为1小时
     datasource.setLogAbandoned(true); ////移除泄露连接发生是是否记录日志
     return datasource;
    

    }

    @Bean(name = "dynamicDataSource")
    @Qualifier("dynamicDataSource")
    public DataSource dynamicDataSource() throws SQLException {
    DynamicDataSource dynamicDataSource = new DynamicDataSource();
    dynamicDataSource.setDebug(false);
    dynamicDataSource.setDefaultTargetDataSource(dataSource());
    Map<Object, Object> targetDataSources = new HashMap<Object, Object>();
    targetDataSources.put("adiDataSource", dataSource());
    dynamicDataSource.setTargetDataSources(targetDataSources);
    return dynamicDataSource;
    }

    @Bean(name = "dynamicJdbcTemplate")
    @Qualifier("dynamicJdbcTemplate")
    public NamedParameterJdbcTemplate dynamicJdbcTemplate(@Qualifier("dynamicDataSource") DataSource dataSource) {
    return new NamedParameterJdbcTemplate(dataSource);
    }

    @Bean(name = "adiJdbcTemplate")
    @Qualifier("adiJdbcTemplate")
    @Primary
    public NamedParameterJdbcTemplate adiJdbcTemplate(@Qualifier("adiDataSource") DataSource dataSource) {
    return new NamedParameterJdbcTemplate(dataSource);
    }

    @Bean(name = "entityManagerFactory")
    @Qualifier("entityManagerFactory")
    @Primary
    public LocalContainerEntityManagerFactoryBean entityManageFactory(EntityManagerFactoryBuilder builder) throws SQLException{
    LocalContainerEntityManagerFactoryBean entityManagerFactory = builder.dataSource(dataSource()).packages("com.bitservice.adi.entity").build();
    Properties jpaProperties = new Properties();
    jpaProperties.put("hibernate.dialect", "org.hibernate.dialect.Oracle10gDialect");
    jpaProperties.put("hibernate.physical_naming_strategy", "org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy");
    jpaProperties.put("hibernate.connection.charSet", "utf-8");
    jpaProperties.put("hibernate.show_sql", "false");
    entityManagerFactory.setJpaProperties(jpaProperties);
    return entityManagerFactory;
    }

    @Bean(name = "entityManager")
    @Qualifier("entityManager")
    @Primary
    public EntityManager entityManager(EntityManagerFactoryBuilder builder) throws SQLException{
    return entityManageFactory(builder).getObject().createEntityManager();
    }

@Bean(name = "dynamicEntityManageFactory")
@Qualifier("dynamicEntityManageFactory")
public LocalContainerEntityManagerFactoryBean dynamicEntityManageFactory(EntityManagerFactoryBuilder builder) throws SQLException{
    LocalContainerEntityManagerFactoryBean entityManagerFactory = builder.dataSource(dynamicDataSource()).packages("com.bitservice.dynamic.entity").build();
    Properties jpaProperties = new Properties();
    //jpaProperties.put("hibernate.dialect", "org.hibernate.dialect.MySQL5Dialect");
    jpaProperties.put("hibernate.physical_naming_strategy", "org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy");
    jpaProperties.put("hibernate.connection.charSet", "utf-8");
    jpaProperties.put("hibernate.show_sql", "false");
    entityManagerFactory.setJpaProperties(jpaProperties);
    return entityManagerFactory;
}

@Bean(name = "dynamicEntityManage")
@Qualifier("dynamicEntityManage")
public EntityManager dynamicEntityManage(EntityManagerFactoryBuilder builder) throws SQLException{
    return entityManageFactory(builder).getObject().createEntityManager();
}

}

第5步:创建数据源切换类DBContextHolder(类名可以改)

代码:

package com.bitservice.adi.datasource;

/**

  • 数据源切换
  • @author aiyo92

*/
public class DBContextHolder {
// 对当前线程的操作-线程安全的
private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();

// 调用此方法,切换数据源
public static void setDataSource(String dataSource) {
    contextHolder.set(dataSource);
}

// 获取数据源
public static String getDataSource() {
    return contextHolder.get();
}

// 删除数据源
public static void clearDataSource() {
    contextHolder.remove();
}

}

核心代码写完了,接下来就是怎么用了!

创建一个DAO类SqlRepository,专门用来操作各个客数据源。

以实现对客数据源的查询请求为例,假如平台创建了多个数据源,并为每个数据源定义了SQL语句,由于平台底层执行SQL的方法是共用的(都在SqlRepository类里面),那么如何在执行查询方法之前动态切换要查询的客数据源呢?上代码!

package com.bitservice.adi.dao;

import java.util.List;
import java.util.Map;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.transaction.Transactional;

import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.stereotype.Service;

import com.bitservice.adi.datasource.DBContextHolder;
import com.bitservice.adi.datasource.DynamicDataSource;
import com.bitservice.adi.entity.DataSource;

@Service
@Transactional
public class SqlRepository {
private final Logger logger = Logger.getLogger(getClass());

@Autowired
@Qualifier("dynamicJdbcTemplate")
private NamedParameterJdbcTemplate jdbcTemplate;

@Autowired
@Qualifier("dynamicDataSource")
private DynamicDataSource dynamicDataSource;

@PersistenceContext(unitName = "dynamicEntityManageFactory")
private EntityManager entityManager;

private static boolean dynamicFlag = true;

public List<Map<String, Object>> doSelect(DataSource dataSource, String sql, Map<String, Object> params) throws Exception {
    if(dynamicFlag) {
        dynamicDataSource.createDataSourceWithCheck(dataSource);
        DBContextHolder.setDataSource(dataSource.getDatasourceId());
    }
    //logger.info("执行sql查询doSelect-sql:" + sql);
    logger.info("sql_params:" + params);
    List<Map<String, Object>> resultList = jdbcTemplate.queryForList(sql, params);
    logger.info("查询数据库结果doSelect-result:" + resultList.toString());
    return resultList;
}

}

我这里是手动切换的,大家可以使用AOP自动切换。

最后温馨提示:以上我给出的只是核心代码,大家直接粘贴使用的话肯定会报错,报错的地方大家一看就知道怎么改,所以你们只需要把报错的代码根据实际情况换成自己的代码即可。如果有任何疑问,咱们可以随时交流!

作者:aiyo92
来源:CSDN
原文:https://blog.csdn.net/aiyo92/article/details/86518217

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,839评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,543评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,116评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,371评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,384评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,111评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,416评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,053评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,558评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,007评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,117评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,756评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,324评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,315评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,539评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,578评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,877评论 2 345

推荐阅读更多精彩内容