準(zhǔn)備多個數(shù)據(jù)庫
? ? ? ? 首先準(zhǔn)備多個數(shù)據(jù)庫,主庫smiling-datasource,其它庫test1、test2、test3
? ? ? ? 接下來,我們在主庫smiling-datasource中,創(chuàng)建表databasesource,用于存儲多數(shù)據(jù)源相關(guān)信息。表結(jié)構(gòu)設(shè)計如下
? ? ? ? 創(chuàng)建好表之后,向表databasesource中存儲test1、test2、test3三個數(shù)據(jù)庫的相關(guān)配置信息
? ? ? ? 在主庫的表databasesource中存儲好數(shù)據(jù)源信息后,接下來分別在test1、test2、test3以及主庫smiling-datasource中都創(chuàng)建sys_user數(shù)據(jù)表,用于后續(xù)測試數(shù)據(jù)源切換是否正常。sys_user的表結(jié)構(gòu)如下所示
? ? ? ? 創(chuàng)建好sys_user之后,分別向sys_user表中添加一條數(shù)據(jù),添加的數(shù)據(jù)中分表用不同的內(nèi)容表示屬于哪個庫,方便后續(xù)測試。
依賴
? ? ? ? 該實現(xiàn)主要用到JDK1.8、spring-boot-starter-parent的版本為2.2.0.RELEASE,除此之外需要用到mysql、druid以及mybatis-plus的依賴
配置
? ? ? ? 在配置文件application.yml中主要配置主庫的數(shù)據(jù)源信息,配置如下
spring:
aop:
proxy-target-class: true #true為使用CGLIB代理
datasource:
url: jdbc:mysql://localhost:3306/smiling-datasource?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8&zeroDateTimeBehavior=convertToNull
username: root
password: zhangpei@123
driver-class-name: com.mysql.cj.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource
druid:
initial-size: 5 #初始化大小
min-idle: 5 #最小連接數(shù)
max-active: 20 #最大連接數(shù)
time-between-eviction-runs-millis: 60000 #配置間隔多久進(jìn)行一次檢測,檢測需要關(guān)閉的空閑連接,單位毫秒
min-evictable-idle-time-millis: 300000 #配置一個連接在池中最小生存的時間,單位毫秒
validation-query: SELECT 1 FROM DUAL
test-while-idle: true
test-on-borrow: false
test-on-return: false
pool-prepared-statements: true
max-pool-prepared-statement-per-connection-size: 20
filters: stat,wall,log4j #配置監(jiān)控統(tǒng)計攔截的filters,去掉后監(jiān)控界面sql無法統(tǒng)計,wall用于防火墻
connection-properties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000 #通過該屬性打開mergeSQl功能:慢sql記錄功能
use-global-data-source-stat: true #合并多個DruidDataSource的監(jiān)控數(shù)據(jù)
max-wait: 60000 #配置獲取連接等待超時時間
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
mapper-locations: classpath*:/mapping/*.xml
基類DataSource
? ? ? ? 創(chuàng)建類DataSource,用于數(shù)據(jù)源信息的裝配
@Data
@ToString
public class DataSource {
String datasourceId;
String url;
String username;
String password;
String code;
String databasetype;
String driverclass;
String key;
}
DruidDBConfig配置類
? ? ? ? 該配置類主要用于配置默認(rèn)的主數(shù)據(jù)源信息,配置Druid數(shù)據(jù)庫連接池,配置sql工廠加載mybatis的文件,掃描實體類等
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.support.http.StatViewServlet;
import com.alibaba.druid.support.http.WebStatFilter;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import javax.sql.DataSource;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
/**
* 配置默認(rèn)數(shù)據(jù)源
* 配置Druid數(shù)據(jù)庫連接池
* 配置sql工廠加載mybatis的文件,掃描實體等
*
* @author smiling
* @date 2023/10/7 19:04
*/
@Slf4j
@Configuration
@EnableTransactionManagement
public class DruilDBConfig {
@Value("${spring.datasource.url}")
private String dbUrl;
@Value("${spring.datasource.username}")
private String username;
@Value("${spring.datasource.password}")
private String password;
@Value("${spring.datasource.driver-class-name}")
private String driverClassName;
// 連接池連接信息
@Value("${spring.datasource.druid.initial-size}")
private int initialSize;
@Value("${spring.datasource.druid.min-idle}")
private int minIdle;
@Value("${spring.datasource.druid.max-active}")
private int maxActive;
@Value("${spring.datasource.druid.max-wait}")
private int maxWait;
@Bean
@Primary
@Qualifier("mainDataSource")
public DataSource dataSource() throws SQLException {
DruidDataSource dataSource = new DruidDataSource();
// 基礎(chǔ)連接信息
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對支持游標(biāo)的數(shù)據(jù)庫性能提升巨大,比如說oracle。在mysql下建議關(guān)閉。
dataSource.setMaxPoolPreparedStatementPerConnectionSize(20);
// datasource.setConnectionProperties("oracle.net.CONNECT_TIMEOUT=6000;oracle.jdbc.ReadTimeout=60000");//對于耗時長的查詢sql,會受限于ReadTimeout的控制,單位毫秒
dataSource.setConnectionProperties("druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000");//對于耗時長的查詢sql,會受限于ReadTimeout的控制,單位毫秒
dataSource.setTestOnBorrow(true); //申請連接時執(zhí)行validationQuery檢測連接是否有效,這里建議配置為TRUE,防止取到的連接不可用
dataSource.setTestWhileIdle(true);//建議配置為true,不影響性能,并且保證安全性。申請連接的時候檢測,如果空閑時間大于timeBetweenEvictionRunsMillis,執(zhí)行validationQuery檢測連接是否有效。
String validationQuery = "select 1 from dual";
dataSource.setValidationQuery(validationQuery); //用來檢測連接是否有效的sql,要求是一個查詢語句。如果validationQuery為null,testOnBorrow、testOnReturn、testWhileIdle都不會起作用。
dataSource.setFilters("stat,wall");//屬性類型是字符串,通過別名的方式配置擴(kuò)展插件,常用的插件有:監(jiān)控統(tǒng)計用的filter:stat日志用的filter:log4j防御sql注入的filter:wall
dataSource.setTimeBetweenEvictionRunsMillis(60000); //配置間隔多久才進(jìn)行一次檢測,檢測需要關(guān)閉的空閑連接,單位是毫秒
dataSource.setMinEvictableIdleTimeMillis(180000); //配置一個連接在池中最小生存的時間,單位是毫秒,這里配置為3分鐘180000
dataSource.setKeepAlive(true); //打開druid.keepAlive之后,當(dāng)連接池空閑時,池中的minIdle數(shù)量以內(nèi)的連接,空閑時間超過minEvictableIdleTimeMillis,則會執(zhí)行keepAlive操作,即執(zhí)行druid.validationQuery指定的查詢SQL,一般為select * from dual,只要minEvictableIdleTimeMillis設(shè)置的小于防火墻切斷連接時間,就可以保證當(dāng)連接空閑時自動做保活檢測,不會被防火墻切斷
dataSource.setRemoveAbandoned(true); //是否移除泄露的連接/超過時間限制是否回收。
dataSource.setRemoveAbandonedTimeout(3600); //泄露連接的定義時間(要超過最大事務(wù)的處理時間);單位為秒。這里配置為1小時
dataSource.setLogAbandoned(true); //移除泄露連接發(fā)生是是否記錄日志
return dataSource;
}
/**
* 注冊一個statViewServlet druid監(jiān)控頁面配置
* 賬號密碼配置
*
* @return
*/
@Bean
public ServletRegistrationBean druidStatViewServlet() {
ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(new StatViewServlet(), "/druid/*");
servletRegistrationBean.addInitParameter("loginUsername", "admin");
servletRegistrationBean.addInitParameter("loginPassword", "123456");
servletRegistrationBean.addInitParameter("resetEnable", "false");
return servletRegistrationBean;
}
/**
* 注冊一個:filterRegistrationBean druid監(jiān)控頁面配置2-允許頁面正常瀏覽
*
* @return filter registration bean
*/
@Bean
public FilterRegistrationBean druidStatFilter() {
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(new WebStatFilter());
// 添加過濾規(guī)則.
filterRegistrationBean.addUrlPatterns("/*");
// 添加不需要忽略的格式信息.
filterRegistrationBean.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*");
return filterRegistrationBean;
}
@Bean(name = "dynamicDataSource")
@Qualifier("dynamicDataSource")
public DynamicDataSource dynamicDataSource() throws SQLException {
DynamicDataSource dynamicDataSource = new DynamicDataSource();
dynamicDataSource.setDebug(false);
//配置缺省的數(shù)據(jù)源
// 默認(rèn)數(shù)據(jù)源配置 DefaultTargetDataSource
dynamicDataSource.setDefaultTargetDataSource(dataSource());
Map<Object, Object> targetDataSources = new HashMap<Object, Object>();
//額外數(shù)據(jù)源配置 TargetDataSources
targetDataSources.put("mainDataSource", dataSource());
dynamicDataSource.setTargetDataSources(targetDataSources);
return dynamicDataSource;
}
@Bean
public SqlSessionFactory sqlSessionFactory() throws Exception {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dynamicDataSource());
//解決手動創(chuàng)建數(shù)據(jù)源后字段到bean屬性名駝峰命名轉(zhuǎn)換失效的問題
sqlSessionFactoryBean.setConfiguration(configuration());
// 設(shè)置mybatis的主配置文件
ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
// Resource mybatisConfigXml = resolver.getResource("classpath:mybatis/mybatis-config.xml");
// sqlSessionFactoryBean.setConfigLocation(mybatisConfigXml);
// 設(shè)置別名包
// sqlSessionFactoryBean.setTypeAliasesPackage("com.testdb.dbsource.pojo");
//手動配置mybatis的mapper.xml資源路徑,如果單純使用注解方式,不需要配置該行
sqlSessionFactoryBean.setMapperLocations(resolver.getResources("classpath:/mapping/*.xml"));
return sqlSessionFactoryBean.getObject();
}
/**
* 讀取駝峰命名設(shè)置
*
* @return
*/
@Bean
@ConfigurationProperties(prefix = "mybatis-plus.configuration")
public org.apache.ibatis.session.Configuration configuration() {
return new org.apache.ibatis.session.Configuration();
}
}
DBContextHolder類
? ? ? ? 該類主要用于手動切換數(shù)據(jù)源
import lombok.extern.slf4j.Slf4j;
/**
* @author smiling
* @date 2023/10/7 19:16
*/
@Slf4j
public class DBContextHolder {
//對當(dāng)前線程的操作-線程安全的
private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();
//調(diào)用此方法,切換數(shù)據(jù)源
public static void setDataSource(String dataSource){
contextHolder.set(dataSource);
log.info("已切換到數(shù)據(jù)源:{}",dataSource);
}
//獲取數(shù)據(jù)源
public static String getDataSource(){
return contextHolder.get();
}
//刪除數(shù)據(jù)源
public static void clearDataSource(){
contextHolder.remove();
log.info("已切換到主數(shù)據(jù)源");
}
}
DynamicDataSource類
? ? ? ? 該類實現(xiàn)手動加載默認(rèn)數(shù)據(jù)源、創(chuàng)建數(shù)據(jù)源連接、檢查數(shù)據(jù)源連接、刪除數(shù)據(jù)源連接等
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.stat.DruidDataSourceStatManager;
import com.smiling.datasource.model.DataSource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import org.springframework.util.ObjectUtils;
import java.sql.Connection;
import java.sql.DriverManager;
import java.util.Map;
import java.util.Set;
/**
* 手動加載默認(rèn)數(shù)據(jù)源
* 創(chuàng)建數(shù)據(jù)源連接
* 檢查數(shù)據(jù)源連接
* 刪除數(shù)據(jù)源連接等
*
* @author smiling
* @date 2023/10/8 11:03
*/
@Slf4j
public class DynamicDataSource extends AbstractRoutingDataSource {
private boolean debug = true;
private Map<Object, Object> dynamicTargetDataSources;
private Object dynamicDefaultTargetDataSource;
@Override
protected Object determineCurrentLookupKey() {
String datasource = DBContextHolder.getDataSource();
if (!ObjectUtils.isEmpty(datasource)) {
Map<Object, Object> targetDataSource = this.dynamicTargetDataSources;
if (targetDataSource.containsKey(datasource)) {
log.info("當(dāng)前數(shù)據(jù)源:" + datasource);
} else {
log.info("不存在的數(shù)據(jù)源");
return null;
}
} else {
log.info("當(dāng)前數(shù)據(jù)源:默認(rèn)數(shù)據(jù)源");
}
return datasource;
}
@Override
public void setTargetDataSources(Map<Object, Object> targetDataSources) {
super.setTargetDataSources(targetDataSources);
this.dynamicTargetDataSources = targetDataSources;
}
//創(chuàng)建數(shù)據(jù)源
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(1); //初始化時建立物理連接的個數(shù)。初始化發(fā)生在顯示調(diào)用init方法,或者第一次getConnection時
druidDataSource.setMaxActive(20); //最大連接池數(shù)量
druidDataSource.setMaxWait(60000); //獲取連接時最大等待時間,單位毫秒。當(dāng)鏈接數(shù)已經(jīng)達(dá)到了最大鏈接數(shù)的時候,應(yīng)用如果還要獲取鏈接就會出現(xiàn)等待的現(xiàn)象,等待鏈接釋放并回到鏈接池,如果等待的時間過長就應(yīng)該踢掉這個等待,不然應(yīng)用很可能出現(xiàn)雪崩現(xiàn)象
druidDataSource.setMinIdle(5); //最小連接池數(shù)量
String validationQuery = "select 1 from dual";
druidDataSource.setTestOnBorrow(true); //申請連接時執(zhí)行validationQuery檢測連接是否有效,這里建議配置為TRUE,防止取到的連接不可用
druidDataSource.setTestWhileIdle(true);//建議配置為true,不影響性能,并且保證安全性。申請連接的時候檢測,如果空閑時間大于timeBetweenEvictionRunsMillis,執(zhí)行validationQuery檢測連接是否有效。
druidDataSource.setValidationQuery(validationQuery); //用來檢測連接是否有效的sql,要求是一個查詢語句。如果validationQuery為null,testOnBorrow、testOnReturn、testWhileIdle都不會起作用。
druidDataSource.setFilters("stat");//屬性類型是字符串,通過別名的方式配置擴(kuò)展插件,常用的插件有:監(jiān)控統(tǒng)計用的filter:stat日志用的filter:log4j防御sql注入的filter:wall
druidDataSource.setTimeBetweenEvictionRunsMillis(60000); //配置間隔多久才進(jìn)行一次檢測,檢測需要關(guān)閉的空閑連接,單位是毫秒
druidDataSource.setMinEvictableIdleTimeMillis(180000); //配置一個連接在池中最小生存的時間,單位是毫秒,這里配置為3分鐘180000
druidDataSource.setKeepAlive(true); //打開druid.keepAlive之后,當(dāng)連接池空閑時,池中的minIdle數(shù)量以內(nèi)的連接,空閑時間超過minEvictableIdleTimeMillis,則會執(zhí)行keepAlive操作,即執(zhí)行druid.validationQuery指定的查詢SQL,一般為select * from dual,只要minEvictableIdleTimeMillis設(shè)置的小于防火墻切斷連接時間,就可以保證當(dāng)連接空閑時自動做?;顧z測,不會被防火墻切斷
druidDataSource.setRemoveAbandoned(true); //是否移除泄露的連接/超過時間限制是否回收。
druidDataSource.setRemoveAbandonedTimeout(3600); //泄露連接的定義時間(要超過最大事務(wù)的處理時間);單位為秒。這里配置為1小時
druidDataSource.setLogAbandoned(true); //移除泄露連接發(fā)生是是否記錄日志
druidDataSource.init();
this.dynamicTargetDataSources.put(key, druidDataSource);
setTargetDataSources(this.dynamicTargetDataSources);// 將map賦值給父類的TargetDataSources
super.afterPropertiesSet();// 將TargetDataSources中的連接信息放入resolvedDataSources管理
log.info(key + "數(shù)據(jù)源初始化成功");
return true;
} catch (Exception e) {
log.error(e + "");
return false;
}
}
//刪除數(shù)據(jù)源
public boolean delDatasources(String datasourceid) {
Map<Object, Object> targetDataSource = this.dynamicTargetDataSources;
if (targetDataSource.containsKey(datasourceid)) {
Set<DruidDataSource> druidDataSourceInstances = DruidDataSourceStatManager.getDruidDataSourceInstances();
for (DruidDataSource source : druidDataSourceInstances) {
if (datasourceid.equals(source.getName())) {
targetDataSource.remove(datasourceid);
DruidDataSourceStatManager.removeDataSource(source);
setTargetDataSources(targetDataSource);// 將map賦值給父類的TargetDataSources
super.afterPropertiesSet();// 將TargetDataSources中的連接信息放入resolvedDataSources管理
return true;
}
}
return false;
} else {
return false;
}
}
// 測試數(shù)據(jù)源連接是否有效
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;
}
}
@Override
public void setDefaultTargetDataSource(Object defaultTargetDataSource) {
super.setDefaultTargetDataSource(defaultTargetDataSource);
this.dynamicDefaultTargetDataSource = defaultTargetDataSource;
}
public void setDebug(boolean debug) {
this.debug = debug;
}
public boolean isDebug() {
return debug;
}
public Map<Object, Object> getDynamicTargetDataSources() {
return dynamicTargetDataSources;
}
public void setDynamicTargetDataSources(Map<Object, Object> dynamicTargetDataSources) {
this.dynamicTargetDataSources = dynamicTargetDataSources;
}
public Object getDynamicDefaultTargetDataSource() {
return dynamicDefaultTargetDataSource;
}
public void setDynamicDefaultTargetDataSource(Object dynamicDefaultTargetDataSource) {
this.dynamicDefaultTargetDataSource = dynamicDefaultTargetDataSource;
}
public void createDataSourceWithCheck(DataSource dataSource) throws Exception {
String datasourceId = dataSource.getDatasourceId();
log.info("正在檢查數(shù)據(jù)源:" + datasourceId);
Map<Object, Object> targetDataSources = this.dynamicTargetDataSources;
if (targetDataSources.containsKey(datasourceId)) {
log.info("數(shù)據(jù)源" + datasourceId + "之前已經(jīng)創(chuàng)建,準(zhǔn)備測試數(shù)據(jù)源是否正常...");
//DataSource druidDataSource = (DataSource) dynamicTargetDataSources2.get(datasourceId);
DruidDataSource druidDataSource = (DruidDataSource) targetDataSources.get(datasourceId);
boolean rightFlag = true;
Connection connection = null;
try {
log.info(datasourceId + "數(shù)據(jù)源的概況->當(dāng)前閑置連接數(shù):" + druidDataSource.getPoolingCount());
long activeCount = druidDataSource.getActiveCount();
log.info(datasourceId + "數(shù)據(jù)源的概況->當(dāng)前活動連接數(shù):" + activeCount);
if (activeCount > 0) {
log.info(datasourceId + "數(shù)據(jù)源的概況->活躍連接堆棧信息:" + druidDataSource.getActiveConnectionStackTrace());
}
log.info("準(zhǔn)備獲取數(shù)據(jù)庫連接...");
connection = druidDataSource.getConnection();
log.info("數(shù)據(jù)源" + datasourceId + "正常");
} catch (Exception e) {
log.error(e.getMessage(), e); //把異常信息打印到日志文件
rightFlag = false;
log.info("緩存數(shù)據(jù)源" + datasourceId + "已失效,準(zhǔn)備刪除...");
if (delDatasources(datasourceId)) {
log.info("緩存數(shù)據(jù)源刪除成功");
} else {
log.info("緩存數(shù)據(jù)源刪除失敗");
}
} finally {
if (null != connection) {
connection.close();
}
}
if (rightFlag) {
log.info("不需要重新創(chuàng)建數(shù)據(jù)源");
return;
} else {
log.info("準(zhǔn)備重新創(chuàng)建數(shù)據(jù)源...");
createDataSource(dataSource);
log.info("重新創(chuàng)建數(shù)據(jù)源完成");
}
} else {
createDataSource(dataSource);
}
}
private void createDataSource(DataSource dataSource) throws Exception {
String datasourceId = dataSource.getDatasourceId();
log.info("準(zhǔn)備創(chuàng)建數(shù)據(jù)源" + datasourceId);
String databasetype = dataSource.getDatabasetype();
String username = dataSource.getUsername();
String password = dataSource.getPassword();
String url = dataSource.getUrl();
String driveClass = "com.mysql.cj.jdbc.Driver";
if (testDatasource(datasourceId, driveClass, url, username, password)) {
boolean result = this.createDataSource(datasourceId, driveClass, url, username, password, databasetype);
if (!result) {
log.error("數(shù)據(jù)源" + datasourceId + "配置正確,但是創(chuàng)建失敗");
}
} else {
log.error("數(shù)據(jù)源配置有錯誤");
}
}
}
切換數(shù)據(jù)源使用的方法
? ? ? ? 首先獲取主數(shù)據(jù)源的表databasesource里面的所有西悉尼,然后根據(jù)我們傳入對應(yīng)的數(shù)據(jù)源id進(jìn)行數(shù)據(jù)源的切換
DataSourceMapper
@Mapper
public interface DataSourceMapper {
@Select("select * from databasesource")
List<DataSource> get();
}
DBChangeService
public interface DBChangeService {
List<DataSource> get();
boolean changeDb(String datasourceId) throws Exception;
}
DBChangeServiceImpl
@Service
public class DBChangeServiceImpl implements DBChangeService {
private DataSourceMapper dataSourceMapper;
private DynamicDataSource dynamicDataSource;
public DBChangeServiceImpl(DataSourceMapper dataSourceMapper,
DynamicDataSource dynamicDataSource){
this.dataSourceMapper = dataSourceMapper;
this.dynamicDataSource = dynamicDataSource;
}
@Override
public List<DataSource> get() { return dataSourceMapper.get(); }
@Override
public boolean changeDb(String datasourceId) throws Exception {
//默認(rèn)切換到主數(shù)據(jù)源,進(jìn)行整體資源的查找
DBContextHolder.clearDataSource();
List<DataSource> dataSourceList = dataSourceMapper.get();
for(DataSource source : dataSourceList){
if(source.getDatasourceId().equals(datasourceId)){
System.out.println("需要使用的數(shù)據(jù)源已經(jīng)找到,datasourceId是:"+source.getDatasourceId());
//創(chuàng)建數(shù)據(jù)源連接&檢查,若存在則不需要重新創(chuàng)建
dynamicDataSource.createDataSourceWithCheck(source);
//切換到該數(shù)據(jù)源
DBContextHolder.setDataSource(source.getDatasourceId());
return true;
}
}
return false;
}
}
實體類User
UserMapper
UserMapper.xml
DemoController
? ? ? ? 通過請求接口,通過控制臺日志打印可以看到數(shù)據(jù)源的切換信息
多數(shù)據(jù)源事務(wù)管理
? ? ? ? 實現(xiàn)動態(tài)數(shù)據(jù)源事務(wù),在類DruidDBConfig配置類中將我們實現(xiàn)的動態(tài)數(shù)據(jù)源加載類DynamicDataSource添加到數(shù)據(jù)事務(wù)管理器中
? ? ? ? 上面配置還是單數(shù)據(jù)源事務(wù)生效,如果是多個數(shù)據(jù)源就不會生效。該句話的意思是,一個方法中,如果涉及到一個數(shù)據(jù)源,則事務(wù)生效,如果涉及到多個數(shù)據(jù)源,則事務(wù)不會在多個數(shù)據(jù)源中生效。
? ? ? ? 如果想要多個數(shù)據(jù)源事務(wù)一起生效,解決方案如下(注意是針對mybatis-plus框架)
? ? ? ? 找到DruidDBConfig配置類,將其中的SqlSessionFactoryBean調(diào)整為MybatisSqlSessionFactoryBean,如下文章來源:http://www.zghlxwxcb.cn/news/detail-792091.html
文章來源地址http://www.zghlxwxcb.cn/news/detail-792091.html
到了這里,關(guān)于SpringBoot從數(shù)據(jù)庫讀取數(shù)據(jù)數(shù)據(jù)源配置信息,動態(tài)切換數(shù)據(jù)源的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!