詳細(xì)聊聊SpringBoot中動態(tài)切換數(shù)據(jù)源的方法
其實(shí)這個表示有點(diǎn)不太對,應(yīng)該是 Druid 動態(tài)切換數(shù)據(jù)源的方法,只是應(yīng)用在了 springboot 框架中,準(zhǔn)備代碼準(zhǔn)備了半天,之前在一次數(shù)據(jù)庫遷移中使用了,發(fā)現(xiàn) Druid 還是很強(qiáng)大的,用來做動態(tài)數(shù)據(jù)源切換很方便。
首先這里的場景跟我原來用的有點(diǎn)點(diǎn)區(qū)別,在項(xiàng)目中使用的是通過配置中心控制數(shù)據(jù)源切換,統(tǒng)一切換,而這里的例子多加了個可以根據(jù)接口注解配置
第一部分是最核心的,如何基于 Spring JDBC 和 Druid 來實(shí)現(xiàn)數(shù)據(jù)源切換,是繼承了org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource 這個類,他的determineCurrentLookupKey方法會被調(diào)用來獲得用來決定選擇那個數(shù)據(jù)源的對象,也就是 lookupKey,也可以通過這個類看到就是通過這個 lookupKey 來路由找到數(shù)據(jù)源。
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
if (DatabaseContextHolder.getDatabaseType() != null) {
return DatabaseContextHolder.getDatabaseType().getName();
}
return DatabaseType.MASTER1.getName();
}
}
而如何使用這個 lookupKey 呢,就涉及到我們的 DataSource 配置了,原來就是我們可以直接通過spring 的 jdbc 配置數(shù)據(jù)源,像這樣

現(xiàn)在我們要使用 Druid 作為數(shù)據(jù)源了,然后配置 DynamicDataSource的參數(shù),通過 key 來選擇對應(yīng)的 DataSource,也就是下面配的 master1 和 master2
<bean id="master1" class="com.alibaba.druid.pool.DruidDataSource" init-method="init"
destroy-method="close"
p:driverClassName="com.mysql.cj.jdbc.Driver"
p:url="${master1.demo.datasource.url}"
p:username="${master1.demo.datasource.username}"
p:password="${master1.demo.datasource.password}"
p:initialSize="5"
p:minIdle="1"
p:maxActive="10"
p:maxWait="60000"
p:timeBetweenEvictionRunsMillis="60000"
p:minEvictableIdleTimeMillis="300000"
p:validationQuery="SELECT 'x'"
p:testWhileIdle="true"
p:testOnBorrow="false"
p:testOnReturn="false"
p:poolPreparedStatements="false"
p:maxPoolPreparedStatementPerConnectionSize="20"
p:connectionProperties="config.decrypt=true"
p:filters="stat,config"/>
<bean id="master2" class="com.alibaba.druid.pool.DruidDataSource" init-method="init"
destroy-method="close"
p:driverClassName="com.mysql.cj.jdbc.Driver"
p:url="${master2.demo.datasource.url}"
p:username="${master2.demo.datasource.username}"
p:password="${master2.demo.datasource.password}"
p:initialSize="5"
p:minIdle="1"
p:maxActive="10"
p:maxWait="60000"
p:timeBetweenEvictionRunsMillis="60000"
p:minEvictableIdleTimeMillis="300000"
p:validationQuery="SELECT 'x'"
p:testWhileIdle="true"
p:testOnBorrow="false"
p:testOnReturn="false"
p:poolPreparedStatements="false"
p:maxPoolPreparedStatementPerConnectionSize="20"
p:connectionProperties="config.decrypt=true"
p:filters="stat,config"/>
<bean id="dataSource" class="com.nicksxs.springdemo.config.DynamicDataSource">
<property name="targetDataSources">
<map key-type="java.lang.String">
<!-- master -->
<entry key="master1" value-ref="master1"/>
<!-- slave -->
<entry key="master2" value-ref="master2"/>
</map>
</property>
<property name="defaultTargetDataSource" ref="master1"/>
</bean>
現(xiàn)在就要回到頭上,介紹下這個DatabaseContextHolder,這里使用了 ThreadLocal 存放這個 DatabaseType,為啥要用這個是因?yàn)榍懊嬲f的我們想要讓接口層面去配置不同的數(shù)據(jù)源,要把持相互隔離不受影響,就使用了 ThreadLocal,關(guān)于它也可以看我前面寫的一篇文章聊聊傳說中的 ThreadLocal,而 DatabaseType 就是個簡單的枚舉
public class DatabaseContextHolder {
public static final ThreadLocal<DatabaseType> databaseTypeThreadLocal = new ThreadLocal<>();
public static DatabaseType getDatabaseType() {
return databaseTypeThreadLocal.get();
}
public static void putDatabaseType(DatabaseType databaseType) {
databaseTypeThreadLocal.set(databaseType);
}
public static void clearDatabaseType() {
databaseTypeThreadLocal.remove();
}
}
public enum DatabaseType {
MASTER1("master1", "1"),
MASTER2("master2", "2");
private final String name;
private final String value;
DatabaseType(String name, String value) {
this.name = name;
this.value = value;
}
public String getName() {
return name;
}
public String getValue() {
return value;
}
public static DatabaseType getDatabaseType(String name) {
if (MASTER2.name.equals(name)) {
return MASTER2;
}
return MASTER1;
}
}
這邊可以看到就是通過動態(tài)地通過putDatabaseType設(shè)置lookupKey來進(jìn)行數(shù)據(jù)源切換,要通過接口注解配置來進(jìn)行設(shè)置的話,我們就需要一個注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface DataSource {
String value();
}
這個注解可以配置在我的接口方法上,比如這樣
public interface StudentService {
@DataSource("master1")
public Student queryOne();
@DataSource("master2")
public Student queryAnother();
}
通過切面來進(jìn)行數(shù)據(jù)源的設(shè)置
@Aspect
@Component
@Order(-1)
public class DataSourceAspect {
@Pointcut("execution(* com.nicksxs.springdemo.service..*.*(..))")
public void pointCut() {
}
@Before("pointCut()")
public void before(JoinPoint point)
{
Object target = point.getTarget();
System.out.println(target.toString());
String method = point.getSignature().getName();
System.out.println(method);
Class<?>[] classz = target.getClass().getInterfaces();
Class<?>[] parameterTypes = ((MethodSignature) point.getSignature())
.getMethod().getParameterTypes();
try {
Method m = classz[0].getMethod(method, parameterTypes);
System.out.println("method"+ m.getName());
if (m.isAnnotationPresent(DataSource.class)) {
DataSource data = m.getAnnotation(DataSource.class);
System.out.println("dataSource:"+data.value());
DatabaseContextHolder.putDatabaseType(DatabaseType.getDatabaseType(data.value()));
}
} catch (Exception e) {
e.printStackTrace();
}
}
@After("pointCut()")
public void after() {
DatabaseContextHolder.clearDatabaseType();
}
}
通過接口判斷是否帶有注解跟是注解的值,DatabaseType 的配置不太好,不過先忽略了,然后在切點(diǎn)后進(jìn)行清理
這是我 master1 的數(shù)據(jù),

master2 的數(shù)據(jù)

然后跑一下簡單的 demo,
@Override
public void run(String...args) {
LOGGER.info("run here");
System.out.println(studentService.queryOne());
System.out.println(studentService.queryAnother());
}
看一下運(yùn)行結(jié)果

其實(shí)這個方法應(yīng)用場景不止可以用來遷移數(shù)據(jù)庫,還能實(shí)現(xiàn)精細(xì)化的讀寫數(shù)據(jù)源分離之類的,算是做個簡單記錄和分享。
總結(jié)
到此這篇關(guān)于SpringBoot中動態(tài)切換數(shù)據(jù)源的文章就介紹到這了,更多相關(guān)SpringBoot動態(tài)切換數(shù)據(jù)源內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- springboot集成@DS注解實(shí)現(xiàn)數(shù)據(jù)源切換的方法示例
- springboot+dynamicDataSource動態(tài)添加切換數(shù)據(jù)源方式
- SpringBoot?+DynamicDataSource切換多數(shù)據(jù)源的全過程
- Springboot實(shí)現(xiàn)根據(jù)用戶ID切換動態(tài)數(shù)據(jù)源
- Springboot動態(tài)切換數(shù)據(jù)源的具體實(shí)現(xiàn)與原理分析
- Spring配置多數(shù)據(jù)源切換
- SpringBoot AOP方式實(shí)現(xiàn)多數(shù)據(jù)源切換的方法
- Spring配置多個數(shù)據(jù)源并實(shí)現(xiàn)數(shù)據(jù)源的動態(tài)切換功能
相關(guān)文章
Java實(shí)現(xiàn)線程的暫停和恢復(fù)的示例詳解
這幾天的項(xiàng)目中,客戶給了個需求,希望我可以開啟一個任務(wù),想什么時候暫停就什么時候暫停,想什么時候開始就什么時候開始,所以本文小編給大家介紹了Java實(shí)現(xiàn)線程的暫停和恢復(fù)的示例,需要的朋友可以參考下2023-11-11
java對象序列化與反序列化的默認(rèn)格式和json格式使用示例
這篇文章主要介紹了java對象序列化與反序列化的默認(rèn)格式和json格式使用示例,需要的朋友可以參考下2014-02-02

