Spring Boot + Spring JPA + JDBC + Druid实现动态数据源切换

Spring Boot + Spring JPA + JDBC + Druid实现动态数据源切换

目录

Spring Boot + Spring JPA + JDBC + Druid实现动态数据源切换

AbstractRoutingDataSource源码分析

需求代码实现

DynamicDataSource

DBContextHolder

DruidDbConfig

DataSourceProperties

AllDataSourcesExecuteSQLDevice

application.properties

参考:https://blog.csdn.net/aiyo92/article/details/101064167#comments_12008727

最后:

总结:


背景:公司开发一个系统,需要满足根据传入的数据库信息和sql,对应的去该库中执行该sql。

关键点:spring-jdbc的AbstractRoutingDataSource抽象类。

AbstractRoutingDataSource源码分析

属性:

  • targetDataSources: 目标数据源

  • defaultTargetDataSource: 默认目标数据源

  • resolvedDataSources: 存储是数据库标识和数据源的映射关系

方法:

  • determineCurrentLookupKey()

  • setTargetDataSources()

  • setDefaultTargetDataSource()

 

分析:

先看关系,AbstractRoutingDataSource继承AbstractDataSource,AbstractDataSource实现DataSource, 既然是实现DataSource接口那就应该有getConnection()这个方法,一看果然有

@Override
public Connection getConnection() throws SQLException {
    return determineTargetDataSource().getConnection();
}
​
@Override
public Connection getConnection(String username, String password) throws SQLException {
    return determineTargetDataSource().getConnection(username, password);
}

getConnection()方法内容也很简单,determineTargetDataSource().getConnection();determineTargetDataSource(): 看名知意,确定目标数据DataSource,再从DataSource中获取Connection。

下面看determineTargetDataSource()方法:

/**
* Retrieve the current target DataSource. Determines the
* {@link #determineCurrentLookupKey() current lookup key}, performs
* a lookup in the {@link #setTargetDataSources targetDataSources} map,
* falls back to the specified
* {@link #setDefaultTargetDataSource default target DataSource} if necessary.
* @see #determineCurrentLookupKey()
*/
protected DataSource determineTargetDataSource() {
    // resolvedDataSources数据源是否为空
    Assert.notNull(this.resolvedDataSources, "DataSource router not initialized");
    // 确定当前数据源的key
    Object lookupKey = determineCurrentLookupKey();
    // 从resolvedDataSources集合中根据key获取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 + "]");
    }
    return dataSource;
}

关键代码是determineCurrentLookupKey()方法:

/**
* Determine the current lookup key. This will typically be
* implemented to check a thread-bound transaction context.
* <p>Allows for arbitrary keys. The returned key needs
* to match the stored lookup key type, as resolved by the
* {@link #resolveSpecifiedLookupKey} method.
*/
@Nullable
protected abstract Object determineCurrentLookupKey();

这是一个抽象方法,翻译一下注释:确定当前的lookupKey。通常将其实现为检查线程绑定事务的上下文。允许任意key。返回的key需要匹配由resolveSpecifiedLookupKey()方法存储的lookup key类型。

需求代码实现

DynamicDataSource

继承AbstractRoutingDataSource,覆写determineCurrentLookupKey()

package com.zchi.multiple_datasources_dynamic_switch;
​
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.stat.DruidDataSourceStatManager;
import lombok.extern.slf4j.Slf4j;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
​
import javax.sql.DataSource;
import java.sql.Connection;
import java.util.Map;
import java.util.Set;
​
/**
 * @author zchi
 * @version 1.0
 * @description 动态数据源类
 * @createdate 2021/2/4
 */
@Slf4j
public class DynamicDataSource extends AbstractRoutingDataSource {
    /**
     * 存放动态数据源
     */
    private Map<Object, Object> dynamicTargetDataSources;
​
    /**
     * 存放动态数据源
     */
    private Object dynamicDefaultTargetDataSource;
​
    @Override
    protected Object determineCurrentLookupKey() {
        String datasource = DBContextHolder.getDataSource();
        return datasource;
    }
​
    @Override
    public void setTargetDataSources(Map<Object, Object> targetDataSources) {
        super.setTargetDataSources(targetDataSources);
        this.dynamicTargetDataSources = targetDataSources;
    }
​
    @Override
    public void setDefaultTargetDataSource(Object defaultTargetDataSource) {
        super.setDefaultTargetDataSource(defaultTargetDataSource);
        this.dynamicDefaultTargetDataSource = defaultTargetDataSource;
    }
​
    /**
     * @param dataSourceProperties 自己封装的数据库连接信息
     * @return DataSource 数据源
     * @throws Exception 异常
     */
    public DataSource createDataSourceWithCheck(DataSourceProperties dataSourceProperties) throws Exception {
        DataSource dataSourceTemp = null;
        String datasourceId = dataSourceProperties.getId();
        log.info("使用数据源: {} name: {}", datasourceId, dataSourceProperties.getText());
        Map<Object, Object> dynamicTargetDataSources2 = this.dynamicTargetDataSources;
        if (dynamicTargetDataSources2.containsKey(datasourceId)) {
            log.info("数据源: {} name: {} 之前已经创建,准备测试数据源是否正常...", datasourceId, dataSourceProperties.getText());
            DruidDataSource druidDataSource = (DruidDataSource) dynamicTargetDataSources2.get(datasourceId);
            boolean rightFlag = true;
            Connection connection = null;
            dataSourceTemp = druidDataSource;
            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 dataSourceTemp;
            } else {
                log.info("准备重新创建数据源...");
                dataSourceTemp = createDataSource(dataSourceProperties);
                log.info("重新创建数据源完成");
            }
        } else {
            dataSourceTemp = createDataSource(dataSourceProperties);
        }
        return dataSourceTemp;
    }
​
    /**
     * 创建数据源
     * @param dataSourceProperties 数据源配置
     * @return
     * @throws Exception
     */
    private DataSource createDataSource(DataSourceProperties dataSourceProperties) throws Exception {
        String key = dataSourceProperties.getId();
        log.info("准备创建数据源 {}", key);
        DruidDataSource druidDataSource = null;
        try {
            druidDataSource = new DruidDataSource();
​
            druidDataSource.setName(key);
            druidDataSource.setDriverClassName(dataSourceProperties.getDbdrive());
            druidDataSource.setUrl(dataSourceProperties.getUrl());
            druidDataSource.setUsername(dataSourceProperties.getUsername());
            druidDataSource.setPassword(dataSourceProperties.getPassword());
            // 初始化时建立物理连接的个数。初始化发生在显示调用init方法,或者第一次getConnection时
            druidDataSource.setInitialSize(dataSourceProperties.getInitialSize());
            // 最小连接池数量
            druidDataSource.setMinIdle(dataSourceProperties.getMinIdle());
            // 最大连接池数量
            druidDataSource.setMaxActive(dataSourceProperties.getMaxActive());
            // 获取连接时最大等待时间,单位毫秒。当链接数已经达到了最大链接数的时候,应用如果还要获取链接就会出现等待的现象,等待链接释放并回到链接池,如果等待的时间过长就应该踢掉这个等待,不然应用很可能出现雪崩现象
            druidDataSource.setMaxWait(60000);
            // 配置一个连接在池中最小生存的时间,单位是毫秒
            druidDataSource.setMinEvictableIdleTimeMillis(dataSourceProperties.getMinEvictableIdleTime());
            druidDataSource.setMaxEvictableIdleTimeMillis(dataSourceProperties.getMaxEvictableIdleTime());
​
            // 申请连接时执行validationQuery检测连接是否有效,这里建议配置为TRUE,防止取到的连接不可用
            druidDataSource.setTestOnBorrow(true);
            // 建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。
            druidDataSource.setTestWhileIdle(true);
            // 属性类型是字符串,通过别名的方式配置扩展插件,常用的插件有:监控统计用的filter:stat日志用的filter:log4j防御sql注入的filter:wall
            druidDataSource.setFilters("stat");
            // 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
            druidDataSource.setTimeBetweenEvictionRunsMillis(60000);
            // 打开druid.keepAlive之后,当连接池空闲时,池中的minIdle数量以内的连接,空闲时间超过minEvictableIdleTimeMillis,则会执行keepAlive操作,即执行druid.validationQuery指定的查询SQL,一般为
            // select * from dual,只要minEvictableIdleTimeMillis设置的小于防火墙切断连接时间,就可以保证当连接空闲时自动做保活检测,不会被防火墙切断
            druidDataSource.setKeepAlive(true); 
​
            // 是否移除泄露的连接/超过时间限制是否回收。
            druidDataSource.setRemoveAbandoned(true);
            // 泄露连接的定义时间(要超过最大事务的处理时间);单位为秒。这里配置为10min
            druidDataSource.setRemoveAbandonedTimeout(600);
            // 移除泄露连接发生是是否记录日志
            druidDataSource.setLogAbandoned(true);
            // 在请求失败以后是否中断
            druidDataSource.setBreakAfterAcquireFailure(true);
            //请求失败尝试次数
            druidDataSource.setConnectionErrorRetryAttempts(0);
​
            druidDataSource.init();
            Map<Object, Object> dynamicTargetDataSources_temp = this.dynamicTargetDataSources;
            // 加入map
            dynamicTargetDataSources_temp.put(key, druidDataSource);
            // 将map赋值给父类的TargetDataSources
            setTargetDataSources(dynamicTargetDataSources_temp);
            // 将TargetDataSources中的连接信息放入resolvedDataSources管理
            super.afterPropertiesSet();
            log.info("数据源 {} 初始化成功", key);
            return druidDataSource;
        } catch (Exception e) {
            druidDataSource.close();
            throw new Exception("数据源" + key + "创建失败: " + e.toString());
        }
    }
​
    /**
     * 删除缓存中的数据源
     *
     * @param datasourceid
     * @return
     */
    public boolean delDatasources(String datasourceid) {
        Map<Object, Object> dynamicTargetDataSources2 = this.dynamicTargetDataSources;
        if (dynamicTargetDataSources2.containsKey(datasourceid)) {
            Set<DruidDataSource> druidDataSourceInstances = DruidDataSourceStatManager.getDruidDataSourceInstances();
            for (DruidDataSource druidDataSource : druidDataSourceInstances) {
                if (datasourceid.equals(druidDataSource.getName())) {
                    dynamicTargetDataSources2.remove(datasourceid);
                    DruidDataSourceStatManager.removeDataSource(druidDataSource);
                    druidDataSource.close();
                    setTargetDataSources(dynamicTargetDataSources2);// 将map赋值给父类的TargetDataSources
                    super.afterPropertiesSet();// 将TargetDataSources中的连接信息放入resolvedDataSources管理
                    return true;
                }
            }
            return false;
        } else {
            return false;
        }
    }
}
​

DBContextHolder

/**
 * @author zchi
 * @version 1.0
 * @description 数据源切换类
 * @createdate 2021/2/4
 */
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();
    }
}

 

DruidDbConfig

package com.zchi.multiple_datasources_dynamic_switch;
​
import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
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.JdbcTemplate;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
​
import javax.persistence.EntityManager;
import javax.sql.DataSource;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
​
/**
 * @author zchi
 * @version 1.0
 * @description 数据源配置类 主要功能是创建系统数据源对象和动态数据源对象
 * @createdate 2021/2/4
 */
@Configuration
public class DruidDbConfig {
​
    @Autowired
    private JpaProperties jpaProperties;
​
    @Autowired
    private DataSourceProperties basicProperties;
​
    @Bean(name = "systemDataSource") // 声明其为Bean实例
    @Primary // 在同样的DataSource中,首先使用被标注的DataSource
    @ConfigurationProperties("spring.datasource.druid")
    public DataSource dataSource() {
        DruidDataSource druidDataSource = new DruidDataSource();
        // 將DataSourceProperties基本的屬性(driverClassName、url、username、password、type)塞进druidDataSource
        afterPropertiesSet(druidDataSource);
        return druidDataSource;
    }
​
    @Bean(name = "dynamicDataSource")
    public DynamicDataSource dynamicDataSource() {
        DynamicDataSource dynamicDataSource = new DynamicDataSource();
        dynamicDataSource.setDefaultTargetDataSource(dataSource());
        Map<Object, Object> targetDataSources = new HashMap<>();
        targetDataSources.put("systemDataSource", dataSource());
        dynamicDataSource.setTargetDataSources(targetDataSources);
        return dynamicDataSource;
    }
​
    @Bean(name = "systemNamedJdbcTemplate")
    @Primary
    public NamedParameterJdbcTemplate systemNamedJdbcTemplate(@Qualifier("systemDataSource") DataSource dataSource) {
        return new NamedParameterJdbcTemplate(dataSource);
    }
​
    @Bean(name = "systemJdbcTemplate")
    @Primary
    public JdbcTemplate systemJdbcTemplate(@Qualifier("systemDataSource") DataSource dataSource) {
        return new JdbcTemplate(dataSource);
    }
​
    @Bean(name = "dynamicJdbcTemplate")
    @Primary
    public JdbcTemplate jdbcTemplate(@Qualifier("dynamicDataSource") DataSource dataSource) {
        return new JdbcTemplate(dataSource);
    }
​
    @Bean(name = "dynamicNamedJdbcTemplate")
    public NamedParameterJdbcTemplate dynamicNamedJdbcTemplate(@Qualifier("dynamicDataSource") DataSource dataSource) {
        return new NamedParameterJdbcTemplate(dataSource);
    }
​
    @Bean(name = "entityManagerFactory")
    @Primary
    public LocalContainerEntityManagerFactoryBean entityManageFactory(EntityManagerFactoryBuilder builder) throws SQLException {
        return builder.dataSource(dataSource()).packages("com.dtsz.portalspringboot.pojo.model").properties(jpaProperties.getProperties()).build();
    }
​
    @Bean(name = "entityManager")
    @Primary
    public EntityManager entityManager(EntityManagerFactoryBuilder builder) throws SQLException {
        return entityManageFactory(builder).getObject().createEntityManager();
    }
​
​
    @Bean(name = "dynamicEntityManageFactory")
    public LocalContainerEntityManagerFactoryBean dynamicEntityManageFactory(EntityManagerFactoryBuilder builder) throws SQLException {
        return builder.dataSource(dynamicDataSource()).packages("com.dtsz.portalspringboot.pojo.model").properties(jpaProperties.getProperties()).build();
    }
​
    @Bean(name = "dynamicEntityManage")
    public EntityManager dynamicEntityManage(EntityManagerFactoryBuilder builder) throws SQLException {
        return entityManageFactory(builder).getObject().createEntityManager();
    }
​
    /**
     * 装配事务管理器 (name = "transactionManager")
     * @return
     */
//    @Bean(name="")
//    public DataSourceTransactionManager jdbcTemplateTransactionManager(@Qualifier("dynamicDataSource") DataSource dataSource) {
//        return new DataSourceTransactionManager(dataSource);
//    }
​
    /**
     * 將DataSourceProperties基本的屬性(driverClassName、url、username、password、type)塞进druidDataSource
     * 跟着druid-spring-boot-starter的DruidDataSourceWrapper类学的,具体自己查看体会。
     * @param druidDataSource
     */
    private void afterPropertiesSet(DruidDataSource druidDataSource) {
        if (druidDataSource.getUsername() == null) {
            druidDataSource.setUsername(this.basicProperties.determineUsername());
        }
​
        if (druidDataSource.getPassword() == null) {
            druidDataSource.setPassword(this.basicProperties.determinePassword());
        }
​
        if (druidDataSource.getUrl() == null) {
            druidDataSource.setUrl(this.basicProperties.determineUrl());
        }
​
        if (druidDataSource.getDriverClassName() == null) {
            druidDataSource.setDriverClassName(this.basicProperties.getDriverClassName());
        }
    }
​
}

 

DataSourceProperties

package com.zchi.multiple_datasources_dynamic_switch;
​
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
import org.hibernate.annotations.GenericGenerator;
​
import javax.persistence.Column;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Transient;
​
/**
 * @author zchi
 * @version 1.0
 * @description 数据源配置,封装连接数据库的一些信息
 * @createdate 2021/2/4
 */
@Data
@Getter
@Setter
public class DataSourceProperties {
​
    private String id;
    private String code;
    private String name;
    /**
     * 名称
     */
    @Column(name = "TEXT", length = 150)
    private String text;
    /**
     * 数据库名称
     */
    private String database;
    /**
     * 链接地址
     */
    private String url;
​
    private String username;
​
    private String password;
​
    private String dbdrive;
​
    private Integer minActionNum;
​
    private Integer maxActionNum;
​
    // 连接最小存活时间 毫秒
    private Integer minEvictableIdleTime = 300000;
​
    // 连接最大存活时间,必须小于数据库的配置的时间毫秒
    private Integer maxEvictableIdleTime = 900000;
​
    private Integer initialSize;
​
    private Integer minIdle;
​
    private Integer maxActive;
​
}
​

AllDataSourcesExecuteSQLDevice

package com.zchi.multiple_datasources_dynamic_switch;
​
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.neo4j.driver.internal.spi.ConnectionPool;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Component;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.DefaultTransactionDefinition;
​
import javax.sql.DataSource;
import java.util.List;
import java.util.Map;
​
/**
 * @author zchi
 * @version 1.0
 * @description 所有的数据源sql执行器
 * @createdate 2021/2/4
 */
@Slf4j
@Component
@Transactional
public class AllDataSourcesExecuteSQLDevice {
​
    @Autowired
    @Qualifier("systemJdbcTemplate")
    private JdbcTemplate systemJdbcTemplate;
​
    @Autowired
    @Qualifier("systemNamedJdbcTemplate")
    private NamedParameterJdbcTemplate systemNamedJdbcTemplate;
​
    @Autowired
    @Qualifier("dynamicJdbcTemplate")
    private JdbcTemplate dynamicJdbcTemplate;
​
    @Autowired
    @Qualifier("dynamicNamedJdbcTemplate")
    private NamedParameterJdbcTemplate dynamicNamedJdbcTemplate;
​
    @Autowired
    @Qualifier("dynamicDataSource")
    private DynamicDataSource dynamicDataSource;
​
    /**
     * 任意数据源做Select操作
     *
     * @param dataSourceProperties 数据源 若为null则使用系统数据源
     * @param sql        要执行的sql :xxx 形式的占位符
     * @param params     需要替换的参数
     * @return
     * @throws Exception
     */
    public List<Map<String, Object>> doSelect(DataSourceProperties dataSourceProperties, String sql, Map<String, Object> params) throws Exception {
        log.info("执行sql查询doSelect-sql: {},sql_params: {}", sql, JSON.toJSONString(params));
        List<Map<String, Object>> resultList = null;
        if (params == null) {
            resultList = getJdbcTemplate(dataSourceProperties).queryForList(sql);
        } else {
            resultList = getNamedParameterJdbcTemplate(dataSourceProperties).queryForList(sql, params);
        }
        return resultList;
    }
​
    /**
     * 任意数据源做Select操作
     *
     * @param dataSourceProperties 数据源 若为null则使用系统数据源
     * @param sql        要执行的sql ? 形式的占位符
     * @param params     需要替换的参数
     * @return
     * @throws Exception
     */
    public List<Map> doSelect(DataSourceProperties dataSourceProperties, String sql, List<Object> params) throws Exception {
        log.info("执行sql查询doSelect-sql: {}, sql_params: {}", sql, JSON.toJSONString(params));
        List<Map> resultList = null;
        if (params != null) {
            resultList = getJdbcTemplate(dataSourceProperties).queryForList(sql, params.toArray(), Map.class);
        } else {
            resultList = getJdbcTemplate(dataSourceProperties).queryForList(sql, Map.class);
        }
        return resultList;
    }
​
    /**
     * 任意数据源做update操作
     *
     * @param dataSourceProperties 数据源 若为null则使用系统数据源
     * @param sql        要执行的sql :xxx 形式的占位符
     * @param params     需要替换的参数
     * @return
     */
    public int doUpdate(DataSourceProperties dataSourceProperties, String sql, Map<String, Object> params) throws Exception {
        log.info("执行sql更新doUpdate-sql: {}, sql_params: {}", sql, JSON.toJSONString(params));
        int row = 0;
        if (params == null) {
            row = getJdbcTemplate(dataSourceProperties).update(sql);
        } else {
            row = getNamedParameterJdbcTemplate(dataSourceProperties).update(sql, params);
        }
        return row;
    }
​
    /**
     * 任意数据源做update操作
     *
     * @param dataSourceProperties 数据源 若为null则使用系统数据源
     * @param sql        要执行的sql ? 形式的占位符
     * @param params     需要替换的参数
     * @return
     */
    public int doUpdate(DataSourceProperties dataSourceProperties, String sql, Object[] params) throws Exception {
        log.info("执行sql更新doUpdate-sql: {}, sql_params: {}", sql, JSON.toJSONString(params));
        int row = 0;
        if (params == null) {
            row = getJdbcTemplate(dataSourceProperties).update(sql);
        } else {
            row = getJdbcTemplate(dataSourceProperties).update(sql, params);
        }
        return row;
    }
​
    /**
     * 根据数据源获得JdbcTemplate
     *
     * @param dataSourceProperties 数据源
     * @return
     * @throws Exception
     */
    private JdbcTemplate getJdbcTemplate(DataSourceProperties dataSourceProperties) throws Exception {
        if (dataSourceProperties == null) { // 空则使用默认的system数据源
            return systemJdbcTemplate;
        }
        // 切换数据源
        changeDataSource(dataSourceProperties);
        return dynamicJdbcTemplate;
    }
​
    /**
     * 根据数据换获得NamedParameterJdbcTemplate
     *
     *
     * @param dataSourceProperties 数据源
     * @return
     * @throws Exception
     */
    private NamedParameterJdbcTemplate getNamedParameterJdbcTemplate(DataSourceProperties dataSourceProperties) throws Exception {
        if (dataSourceProperties == null) { // 空则使用默认的system数据源
            return systemNamedJdbcTemplate;
        }
        // 切换数据源
        changeDataSource(dataSourceProperties);
        return dynamicNamedJdbcTemplate;
    }
​
​
​
    /**
     * 切换数据源
     *
     * @param dataSourceProperties
     * @return 事务管理器
     * @throws Exception
     */
    private DataSourceTransactionManager changeDataSource(DataSourceProperties dataSourceProperties) throws Exception {
        DataSource dataSource1 = dynamicDataSource.createDataSourceWithCheck(dataSourceProperties);
        DBContextHolder.setDataSource(dataSourceProperties.getId());
        return new DataSourceTransactionManager(dataSource1);
    }
​
    /**
     * 开启事务
     */
    private TransactionStatus beginTransaction(DataSourceTransactionManager transactionManager) {
        DefaultTransactionDefinition def = new DefaultTransactionDefinition();
        // 设置事务隔离级别
        def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
        // 返回事务对象
        TransactionStatus status = transactionManager.getTransaction(def);
        return status;
    }
​
    /**
     * 提交事务
     *
     * @param transactionManager
     * @param status
     */
    private void commitTransaction(DataSourceTransactionManager transactionManager, TransactionStatus status) {
        transactionManager.commit(status);
    }
​
    /**
     * 事务回滚
     *
     * @param transactionManager
     * @param status
     */
    private void rollbackTransaction(DataSourceTransactionManager transactionManager, TransactionStatus status) {
        transactionManager.rollback(status);
    }
}
​

application.properties

spring.datasource.driverClassName=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/zchi_test
spring.datasource.username=root
spring.datasource.password=nopassword
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
​
spring.datasource.druid.initial-size=1
spring.datasource.druid.max-active=10
spring.datasource.druid.min-idle=1
spring.datasource.druid.max-wait=-1

参考:https://blog.csdn.net/aiyo92/article/details/101064167#comments_12008727

最后:

感谢上边博主的分享

总结:

  1. 本博客在该博主基础上优化了一下默认数据源的注入,默认数据源的druid配置直接在配置文件中配置,不用再修改DruidDbConfig.dataSource()的代码。

  2. DynamicDataSource.createDataSource()异常时close()掉和DynamicDataSource.delDatasources()

时close()掉。DruidDataSourceStatManager.removeDataSource(druidDataSource);并没有真正的关闭连接导致数据库的session一直存在。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值