Spring Boot + Spring JPA + JDBC + Druid实现动态数据源切换
目录
Spring Boot + Spring JPA + JDBC + Druid实现动态数据源切换
AllDataSourcesExecuteSQLDevice
参考: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
最后:
感谢上边博主的分享
总结:
-
本博客在该博主基础上优化了一下默认数据源的注入,默认数据源的druid配置直接在配置文件中配置,不用再修改
DruidDbConfig.dataSource()
的代码。 -
DynamicDataSource.createDataSource()
异常时close()掉和DynamicDataSource.
delDatasources()
时close()掉。DruidDataSourceStatManager.removeDataSource(druidDataSource);
并没有真正的关闭连接导致数据库的session一直存在。