0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

SpringBoot多數(shù)據(jù)源及事務(wù)解決方案

jf_ro2CN3Fa ? 來源:CSDN ? 2023-04-12 11:22 ? 次閱讀


1. 背景

一個(gè)主庫和N個(gè)應(yīng)用庫的數(shù)據(jù)源,并且會同時(shí)操作主庫和應(yīng)用庫的數(shù)據(jù),需要解決以下兩個(gè)問題:

  • 如何動態(tài)管理多個(gè)數(shù)據(jù)源以及切換?
  • 如何保證多數(shù)據(jù)源場景下的數(shù)據(jù)一致性(事務(wù))?

本文主要探討這兩個(gè)問題的解決方案,希望能對讀者有一定的啟發(fā)。

基于 Spring Boot + MyBatis Plus + Vue & Element 實(shí)現(xiàn)的后臺管理系統(tǒng) + 用戶小程序,支持 RBAC 動態(tài)權(quán)限、多租戶、數(shù)據(jù)權(quán)限、工作流、三方登錄、支付、短信、商城等功能

  • 項(xiàng)目地址:https://github.com/YunaiV/ruoyi-vue-pro
  • 視頻教程:https://doc.iocoder.cn/video/

2. 數(shù)據(jù)源切換原理

通過擴(kuò)展Spring提供的抽象類AbstractRoutingDataSource,可以實(shí)現(xiàn)切換數(shù)據(jù)源。其類結(jié)構(gòu)如下圖所示:

32fe5be6-d8d8-11ed-bfe3-dac502259ad0.png
  • targetDataSources&defaultTargetDataSource

項(xiàng)目上需要使用的所有數(shù)據(jù)源和默認(rèn)數(shù)據(jù)源。

  • resolvedDataSources&resolvedDefaultDataSource

當(dāng)Spring容器創(chuàng)建AbstractRoutingDataSource對象時(shí),通過調(diào)用afterPropertiesSet復(fù)制上述目標(biāo)數(shù)據(jù)源。由此可見,一旦數(shù)據(jù)源實(shí)例對象創(chuàng)建完畢,業(yè)務(wù)無法再添加新的數(shù)據(jù)源。

  • determineCurrentLookupKey

此方法為抽象方法,通過擴(kuò)展這個(gè)方法來實(shí)現(xiàn)數(shù)據(jù)源的切換。目標(biāo)數(shù)據(jù)源的結(jié)構(gòu)為:Map其key為lookup key。

我們來看官方對這個(gè)方法的注釋:

3317a344-d8d8-11ed-bfe3-dac502259ad0.png

lookup key通常是綁定在線程上下文中,根據(jù)這個(gè)key去resolvedDataSources中取出DataSource。

根據(jù)目標(biāo)數(shù)據(jù)源的管理方式不同,可以使用基于配置文件和數(shù)據(jù)庫表兩種方式?;谂渲梦募芾矸桨笩o法后續(xù)添加新的數(shù)據(jù)源,而基于數(shù)據(jù)庫表方案管理,則更加靈活。

基于 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 實(shí)現(xiàn)的后臺管理系統(tǒng) + 用戶小程序,支持 RBAC 動態(tài)權(quán)限、多租戶、數(shù)據(jù)權(quán)限、工作流、三方登錄、支付、短信、商城等功能

  • 項(xiàng)目地址:https://github.com/YunaiV/yudao-cloud
  • 視頻教程:https://doc.iocoder.cn/video/

3. 配置文件解決方案

根據(jù)上面的分析,我們可以按照下面的步驟去實(shí)現(xiàn):

  • 定義DynamicDataSource類繼承AbstractRoutingDataSource,重寫determineCurrentLookupKey()方法。
  • 配置多個(gè)數(shù)據(jù)源注入targetDataSourcesdefaultTargetDataSource,通過afterPropertiesSet()方法將數(shù)據(jù)源寫入resolvedDataSourcesresolvedDefaultDataSource。
  • 調(diào)用AbstractRoutingDataSourcegetConnection()方法時(shí),determineTargetDataSource()方法返回DataSource執(zhí)行底層的getConnection()。

其流程如下圖所示:

33237bd8-d8d8-11ed-bfe3-dac502259ad0.png

3.1 創(chuàng)建數(shù)據(jù)源

DynamicDataSource數(shù)據(jù)源的注入,目前業(yè)界主流實(shí)現(xiàn)步驟如下:

在配置文件中定義數(shù)據(jù)源

spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.driverClassName=com.mysql.jdbc.Driver
#主數(shù)據(jù)源
spring.datasource.druid.master.url=jdbcUrl
spring.datasource.druid.master.username=***
spring.datasource.druid.master.password=***
#其他數(shù)據(jù)源
spring.datasource.druid.second.url=jdbcUrl
spring.datasource.druid.second.username=***
spring.datasource.druid.second.password=***

在代碼中配置Bean

@Configuration
publicclassDynamicDataSourceConfig{
@Bean
@ConfigurationProperties("spring.datasource.druid.master")
publicDataSourcefirstDataSource(){
returnDruidDataSourceBuilder.create().build();
}

@Bean
@ConfigurationProperties("spring.datasource.druid.second")
publicDataSourcesecondDataSource(){
returnDruidDataSourceBuilder.create().build();
}

@Bean
@Primary
publicDynamicDataSourcedataSource(DataSourcefirstDataSource,DataSourcesecondDataSource){
MaptargetDataSources=newHashMap<>(5);
targetDataSources.put(DataSourceNames.FIRST,firstDataSource);
targetDataSources.put(DataSourceNames.SECOND,secondDataSource);
returnnewDynamicDataSource(firstDataSource,targetDataSources);
}
}

3.2 AOP處理

通過DataSourceAspect切面技術(shù)來簡化業(yè)務(wù)上的使用,只需要在業(yè)務(wù)方法添加@SwitchDataSource注解即可完成動態(tài)切換:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public@interfaceSwitchDataSource{
Stringvalue();
}

DataSourceAspect攔截業(yè)務(wù)方法,更新當(dāng)前線程上下文DataSourceContextHolder中存儲的key,即可實(shí)現(xiàn)數(shù)據(jù)源切換。

3.3 方案不足

基于AbstractRoutingDataSource的多數(shù)據(jù)源動態(tài)切換,有個(gè)明顯的缺點(diǎn),無法動態(tài)添加和刪除數(shù)據(jù)源。在我們的產(chǎn)品中,不能把應(yīng)用數(shù)據(jù)源寫死在配置文件。接下來分享一下基于數(shù)據(jù)庫表的實(shí)現(xiàn)方案。

4. 數(shù)據(jù)庫表解決方案

我們需要實(shí)現(xiàn)可視化的數(shù)據(jù)源管理,并實(shí)時(shí)查看數(shù)據(jù)源的運(yùn)行狀態(tài)。所以我們不能把數(shù)據(jù)源全部配置在文件中,應(yīng)該將數(shù)據(jù)源定義保存到數(shù)據(jù)庫表。參考AbstractRoutingDataSource的設(shè)計(jì)思路,實(shí)現(xiàn)自定義數(shù)據(jù)源管理。

4.1 設(shè)計(jì)數(shù)據(jù)源表

主庫的數(shù)據(jù)源信息仍然配置在項(xiàng)目配置文件中,應(yīng)用庫數(shù)據(jù)源配置參數(shù),則設(shè)計(jì)對應(yīng)的數(shù)據(jù)表。表結(jié)構(gòu)如下所示:

333864e4-d8d8-11ed-bfe3-dac502259ad0.png

這個(gè)表主要就是DataSource的相關(guān)配置參數(shù),其相應(yīng)的ORM操作代碼在此不再贅述,主要是實(shí)現(xiàn)數(shù)據(jù)源的增刪改查操作。

4.2 自定義數(shù)據(jù)源管理

4.2.1 定義管理接口

通過繼承AbstractDataSource即可實(shí)現(xiàn)DynamicDataSource。為了方便對數(shù)據(jù)源進(jìn)行操作,我們定義一個(gè)接口DataSourceManager,為業(yè)務(wù)提供操作數(shù)據(jù)源的統(tǒng)一接口。

publicinterfaceDataSourceManager{
voidput(Stringvar1,DataSourcevar2);

DataSourceget(Stringvar1);

BooleanhasDataSource(Stringvar1);

voidremove(Stringvar1);

voidcloseDataSource(Stringvar1);

Collectionall();
}

該接口主要是對數(shù)據(jù)表中定義的數(shù)據(jù)源,提供基礎(chǔ)管理功能。

4.2.2 自定義數(shù)據(jù)源

DynamicDataSource的實(shí)現(xiàn)如下圖所示:

335837ce-d8d8-11ed-bfe3-dac502259ad0.png

根據(jù)前面的分析,AbstractRoutingDataSource是在容器啟動的時(shí)候,執(zhí)行afterPropertiesSet注入數(shù)據(jù)源對象,完成之后無法對數(shù)據(jù)源進(jìn)行修改。DynamicDataSource則實(shí)現(xiàn)DataSourceManager接口,可以將數(shù)據(jù)表中的數(shù)據(jù)源加載到dataSources。

4.2.3 切面處理

這一塊的處理跟配置文件數(shù)據(jù)源方案處理方式相同,都是通過AOP技術(shù)切換lookup key。

publicDataSourcedetermineTargetDataSource(){
StringlookupKey=DataSourceContextHolder.getKey();
DataSourcedataSource=Optional.ofNullable(lookupKey)
.map(dataSources::get)
.orElse(defaultDataSource);
if(dataSource==null){
thrownewIllegalStateException("CannotdetermineDataSourceforlookupkey["+lookupKey+"]");
}
returndataSource;
}

4.2.4 管理數(shù)據(jù)源狀態(tài)

在項(xiàng)目啟動的時(shí)候,加載數(shù)據(jù)表中的所有數(shù)據(jù)源,并執(zhí)行初始化。初始化操作主要是使用SpringBoot提供的DataSourceBuilder類,根據(jù)數(shù)據(jù)源表的定義創(chuàng)建DataSource。在項(xiàng)目運(yùn)行過程中,可以使用定時(shí)任務(wù)對數(shù)據(jù)源進(jìn)行?;睿瑸榱颂嵘阅茉偬砑右粚泳彺?。

336da2da-d8d8-11ed-bfe3-dac502259ad0.png

AbstractRoutingDataSource 只支持單庫事務(wù),切換數(shù)據(jù)源是在開啟事務(wù)之前執(zhí)行。 Spring使用 DataSourceTransactionManager進(jìn)行事務(wù)管理。開啟事務(wù),會將數(shù)據(jù)源緩存到DataSourceTransactionObject對象中,后續(xù)的commit和 rollback事務(wù)操作實(shí)際上是使用的同一個(gè)數(shù)據(jù)源。

如何解決切庫事務(wù)問題?借助Spring的聲明式事務(wù)處理,我們可以在多次切庫操作時(shí)強(qiáng)制開啟新的事務(wù):

@SwitchDataSource
@Transactional(rollbackFor=Exception.class,propagation=Propagation.REQUIRES_NEW)

這樣的話,執(zhí)行切庫操作的時(shí)候強(qiáng)制啟動新事務(wù),便可實(shí)現(xiàn)多次切庫而且事務(wù)能夠生效。但是這種事務(wù)方式,存在數(shù)據(jù)一致性問題:

337b63a2-d8d8-11ed-bfe3-dac502259ad0.png

假若ServiceB正常執(zhí)行提交事務(wù),接著返回ServiceA執(zhí)行并且發(fā)生異常。因?yàn)閮纱翁幚硎遣煌氖聞?wù),ServiceA這個(gè)事務(wù)執(zhí)行回滾,而ServiceA事務(wù)已經(jīng)提交。這樣的話,數(shù)據(jù)就不一致了。接下來,我們主要討論如何解決多庫的事務(wù)問題。

6. 多庫事務(wù)處理

6.1 關(guān)于事務(wù)的理解

首先有必要理解事務(wù)的本質(zhì)。

1.提到Spring事務(wù),就離不開事務(wù)的四大特性和隔離級別、七大傳播特性。

事務(wù)特性和離級別是屬于數(shù)據(jù)庫范疇。Spring事務(wù)的七大傳播特性是什么呢?它是Spring在當(dāng)前線程內(nèi),處理多個(gè)事務(wù)操作時(shí)的事務(wù)應(yīng)用策略,數(shù)據(jù)庫事務(wù)本身并不存在傳播特性。

3390ad70-d8d8-11ed-bfe3-dac502259ad0.png

2.Spring事務(wù)的定義包括:begin、commit、rollback、close、suspend、resume等動作。

  • begin(事務(wù)開始): 可以認(rèn)為存在于數(shù)據(jù)庫的命令中,比如Mysql的start transaction命令,但是在JDBC編程方式中不存在。
  • close(事務(wù)關(guān)閉): Spring事務(wù)的close()方法,是把Connection對象歸還給數(shù)據(jù)庫連接池,與事務(wù)無關(guān)。
  • suspend(事務(wù)掛起): Spring中事務(wù)掛起的語義是:需要新事務(wù)時(shí),將現(xiàn)有的Connection保存起來(還有尚未提交的事務(wù)),然后創(chuàng)建新的Connection2,Connection2提交、回滾、關(guān)閉完畢后,再把Connection1取出來繼續(xù)執(zhí)行。
  • resume(事務(wù)恢復(fù)): 嵌套事務(wù)執(zhí)行完畢,返回上層事務(wù)重新綁定連接對象到事務(wù)管理器的過程。

實(shí)際上,只有commit、rollback、close是在JDBC真實(shí)存在的,而其他動作都是應(yīng)用的語意,而非JDBC事務(wù)的真實(shí)命令。因此,事務(wù)真實(shí)存在的方法是:setAutoCommit()commit()、rollback()。

close()語義為:

  • 關(guān)閉一個(gè)數(shù)據(jù)庫連接,這已經(jīng)不再是事務(wù)的方法了。

使用DataSource并不會執(zhí)行物理關(guān)閉,只是歸還給連接池。

6.2 自定義管理事務(wù)

為了保證在多個(gè)數(shù)據(jù)源中事務(wù)的一致性,我們可以手動管理Connetion的事務(wù)提交和回滾??紤]到不同ORM框架的事務(wù)管理實(shí)現(xiàn)差異,要求實(shí)現(xiàn)自定義事務(wù)管理不影響框架層的事務(wù)。

這可以通過使用裝飾器設(shè)計(jì)模式,對Connection進(jìn)行包裝重寫commit和rolllback屏蔽其默認(rèn)行為,這樣就不會影響到原生Connection和ORM框架的默認(rèn)事務(wù)行為。其整體思路如下圖所示:

33a2cf3c-d8d8-11ed-bfe3-dac502259ad0.png

這里并沒有使用前面提到的@SwitchDataSource,這是因?yàn)槲覀冊?code style="font-size:14px;padding:2px 4px;margin-right:2px;margin-left:2px;color:rgb(30,107,184);background-color:rgba(27,31,35,.05);font-family:'Operator Mono', Consolas, Monaco, Menlo, monospace;">TransactionAop中已經(jīng)執(zhí)行了lookupKey的切換。

6.2.1 定義多事務(wù)注解

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public@interfaceMultiTransaction{
StringtransactionManager()default"multiTransactionManager";
//默認(rèn)數(shù)據(jù)隔離級別,隨數(shù)據(jù)庫本身默認(rèn)值
IsolationLevelisolationLevel()defaultIsolationLevel.DEFAULT;
//默認(rèn)為主庫數(shù)據(jù)源
StringdatasourceId()default"default";
//只讀事務(wù),若有更新操作會拋出異常
booleanreadOnly()defaultfalse;

業(yè)務(wù)方法只需使用該注解即可開啟事務(wù),datasourceId指定事務(wù)用到的數(shù)據(jù)源,不指定默認(rèn)為主庫。

6.2.3 包裝Connection

自定義事務(wù)我們使用包裝過的Connection,屏蔽其中的commit&rollback方法。這樣我們就可以在主事務(wù)里進(jìn)行統(tǒng)一的事務(wù)提交和回滾操作。

publicclassConnectionProxyimplementsConnection{

privatefinalConnectionconnection;

publicConnectionProxy(Connectionconnection){
this.connection=connection;
}

@Override
publicvoidcommit()throwsSQLException{
//connection.commit();
}

publicvoidrealCommit()throwsSQLException{
connection.commit();
}

@Override
publicvoidclose()throwsSQLException{
//connection.close();
}

publicvoidrealClose()throwsSQLException{
if(!connection.getAutoCommit()){
connection.setAutoCommit(true);
}
connection.close();
}

@Override
publicvoidrollback()throwsSQLException{
if(!connection.isClosed())
connection.rollback();
}
...
}

這里commit&close方法不執(zhí)行操作,rollback執(zhí)行的前提是連接執(zhí)行close才生效。這樣不管是使用哪個(gè)ORM框架,其自身事務(wù)管理都將失效。事務(wù)的控制就交由MultiTransaction控制了。

6.2.4 事務(wù)上下文管理

publicclassTransactionHolder{
//是否開啟了一個(gè)MultiTransaction
privatebooleanisOpen;
//是否只讀事務(wù)
privatebooleanreadOnly;
//事務(wù)隔離級別
privateIsolationLevelisolationLevel;
//維護(hù)當(dāng)前線程事務(wù)ID和連接關(guān)系
privateConcurrentHashMapconnectionMap;
//事務(wù)執(zhí)行棧
privateStackexecuteStack;
//數(shù)據(jù)源切換棧
privateStackdatasourceKeyStack;
//主事務(wù)ID
privateStringmainTransactionId;
//執(zhí)行次數(shù)
privateAtomicIntegertransCount;

//事務(wù)和數(shù)據(jù)源key關(guān)系
privateConcurrentHashMapexecuteIdDatasourceKeyMap;

}

每開啟一個(gè)事物,生成一個(gè)事務(wù)ID并綁定一個(gè)ConnectionProxy。事務(wù)嵌套調(diào)用,保存事務(wù)ID和lookupKey至棧中,當(dāng)內(nèi)層事務(wù)執(zhí)行完畢執(zhí)行pop。這樣的話,外層事務(wù)只需在棧中執(zhí)行peek即可獲取事務(wù)ID和lookupKey。

6.2.5 數(shù)據(jù)源兼容處理

為了不影響原生事務(wù)的使用,需要重寫getConnection方法。當(dāng)前線程沒有啟動自定義事務(wù),則直接從數(shù)據(jù)源中返回連接。

@Override
publicConnectiongetConnection()throwsSQLException{
TransactionHoldertransactionHolder=MultiTransactionManager.TRANSACTION_HOLDER_THREAD_LOCAL.get();
if(Objects.isNull(transactionHolder)){
returndetermineTargetDataSource().getConnection();
}
ConnectionProxyConnectionProxy=transactionHolder.getConnectionMap()
.get(transactionHolder.getExecuteStack().peek());
if(ConnectionProxy==null){
//沒開跨庫事務(wù),直接返回
returndetermineTargetDataSource().getConnection();
}else{
transactionHolder.addCount();
//開了跨庫事務(wù),從當(dāng)前線程中拿包裝過的Connection
returnConnectionProxy;
}
}

6.2.6 切面處理

切面處理的核心邏輯是:維護(hù)一個(gè)嵌套事務(wù)棧,當(dāng)業(yè)務(wù)方法執(zhí)行結(jié)束,或者發(fā)生異常時(shí),判斷當(dāng)前棧頂事務(wù)ID是否為主事務(wù)ID。如果是的話這時(shí)候已經(jīng)到了最外層事務(wù),這時(shí)才執(zhí)行提交和回滾。詳細(xì)流程如下圖所示:

33b5335c-d8d8-11ed-bfe3-dac502259ad0.png
packagecom.github.mtxn.transaction.aop;

importcom.github.mtxn.application.Application;
importcom.github.mtxn.transaction.MultiTransactionManager;
importcom.github.mtxn.transaction.annotation.MultiTransaction;
importcom.github.mtxn.transaction.context.DataSourceContextHolder;
importcom.github.mtxn.transaction.support.IsolationLevel;
importcom.github.mtxn.transaction.support.TransactionHolder;
importcom.github.mtxn.utils.ExceptionUtils;
importlombok.extern.slf4j.Slf4j;
importorg.aspectj.lang.ProceedingJoinPoint;
importorg.aspectj.lang.annotation.Around;
importorg.aspectj.lang.annotation.Aspect;
importorg.aspectj.lang.annotation.Pointcut;
importorg.aspectj.lang.reflect.MethodSignature;
importorg.springframework.core.annotation.Order;
importorg.springframework.stereotype.Component;

importjava.lang.reflect.Method;


@Aspect
@Component
@Slf4j
@Order(99999)
publicclassMultiTransactionAop{

@Pointcut("@annotation(com.github.mtxn.transaction.annotation.MultiTransaction)")
publicvoidpointcut(){
if(log.isDebugEnabled()){
log.debug("startintransactionpointcut...");
}
}


@Around("pointcut()")
publicObjectaroundTransaction(ProceedingJoinPointpoint)throwsThrowable{
MethodSignaturesignature=(MethodSignature)point.getSignature();
//從切面中獲取當(dāng)前方法
Methodmethod=signature.getMethod();
MultiTransactionmultiTransaction=method.getAnnotation(MultiTransaction.class);
if(multiTransaction==null){
returnpoint.proceed();
}
IsolationLevelisolationLevel=multiTransaction.isolationLevel();
booleanreadOnly=multiTransaction.readOnly();
StringprevKey=DataSourceContextHolder.getKey();
MultiTransactionManagermultiTransactionManager=Application.resolve(multiTransaction.transactionManager());
//切數(shù)據(jù)源,如果失敗使用默認(rèn)庫
if(multiTransactionManager.switchDataSource(point,signature,multiTransaction))returnpoint.proceed();
//開啟事務(wù)棧
TransactionHoldertransactionHolder=multiTransactionManager.startTransaction(prevKey,isolationLevel,readOnly,multiTransactionManager);
Objectproceed;

try{
proceed=point.proceed();
multiTransactionManager.commit();
}catch(Throwableex){
log.error("executemethod:{}#{},err:",method.getDeclaringClass(),method.getName(),ex);
multiTransactionManager.rollback();
throwExceptionUtils.api(ex,"系統(tǒng)異常:%s",ex.getMessage());
}finally{
//當(dāng)前事務(wù)結(jié)束出棧
StringtransId=multiTransactionManager.getTrans().getExecuteStack().pop();
transactionHolder.getDatasourceKeyStack().pop();
//恢復(fù)上一層事務(wù)
DataSourceContextHolder.setKey(transactionHolder.getDatasourceKeyStack().peek());
//最后回到主事務(wù),關(guān)閉此次事務(wù)
multiTransactionManager.close(transId);
}
returnproceed;

}


}

7.總結(jié)

本文主要介紹了多數(shù)據(jù)源管理的解決方案(應(yīng)用層事務(wù),而非XA二段提交保證),以及對多個(gè)庫同時(shí)操作的事務(wù)管理。

需要注意的是,這種方式只適用于單體架構(gòu)的應(yīng)用。因?yàn)槎鄠€(gè)庫的事務(wù)參與者都是運(yùn)行在同一個(gè)JVM進(jìn)行。如果是在微服務(wù)架構(gòu)的應(yīng)用中,則需要使用分布式事務(wù)管理(譬如:Seata)。



審核編輯 :李倩


聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報(bào)投訴
  • spring
    +關(guān)注

    關(guān)注

    0

    文章

    335

    瀏覽量

    14277
  • 數(shù)據(jù)源
    +關(guān)注

    關(guān)注

    1

    文章

    61

    瀏覽量

    9655
  • 微服務(wù)
    +關(guān)注

    關(guān)注

    0

    文章

    129

    瀏覽量

    7314
  • SpringBoot
    +關(guān)注

    關(guān)注

    0

    文章

    173

    瀏覽量

    153

原文標(biāo)題:SpringBoot 多數(shù)據(jù)源及事務(wù)解決方案

文章出處:【微信號:芋道源碼,微信公眾號:芋道源碼】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。

收藏 人收藏

    評論

    相關(guān)推薦

    LabView動態(tài)創(chuàng)建數(shù)據(jù)源的方法

    在LabView中LabSQL操作數(shù)據(jù)庫是通過ODBC連接,用戶需要在ODBC中指定數(shù)據(jù)源名稱和驅(qū)動程序。因此在使用LabSQL之前,首先需要在Windows操作系統(tǒng)中的ODBC數(shù)據(jù)源中創(chuàng)建一個(gè)
    發(fā)表于 09-23 01:53

    ODBC數(shù)據(jù)源的建立

    我找不到數(shù)據(jù)源這個(gè)選項(xiàng)啊,怎么辦?
    發(fā)表于 04-21 11:39

    SpringBoot中的Druid介紹

    SpringBoot中Druid數(shù)據(jù)源配置
    發(fā)表于 05-07 09:21

    SpringBoot項(xiàng)目多數(shù)據(jù)源配置數(shù)據(jù)

    SpringBoot項(xiàng)目多數(shù)據(jù)源配置
    發(fā)表于 06-05 09:51

    基于“本體”的異構(gòu)數(shù)據(jù)源的集成方法研究

             本文給出了一種基于本體的異構(gòu)數(shù)據(jù)源的集成方案,該方案從根本上解決了企業(yè)內(nèi)部數(shù)據(jù)源的語法
    發(fā)表于 09-04 09:24 ?13次下載

    數(shù)據(jù)源配置工具

    odbc_for_access.exe1、可直接配置數(shù)據(jù)源。2、可直接創(chuàng)建Access數(shù)據(jù)庫文件。3、為數(shù)據(jù)庫配置密碼。
    發(fā)表于 07-01 16:22 ?4次下載

    基于元組水平對數(shù)據(jù)源進(jìn)行分層抽樣

    深網(wǎng)查詢在Web上眾多的應(yīng)用,需要查詢大量的數(shù)據(jù)源才能獲得足夠的數(shù)據(jù),如多媒體數(shù)據(jù)搜索、團(tuán)購網(wǎng)站信息聚合等.應(yīng)用的成功,取決于查詢多數(shù)據(jù)源的效率和效果,當(dāng)前研究側(cè)重查詢與
    發(fā)表于 12-29 10:55 ?0次下載
    基于元組水平對<b class='flag-5'>數(shù)據(jù)源</b>進(jìn)行分層抽樣

    基于LDA主題模型進(jìn)行數(shù)據(jù)源選擇方法

    聯(lián)邦搜索是從大規(guī)模深層網(wǎng)上獲取信息的一種重要技術(shù)。給定一個(gè)用戶查詢,聯(lián)邦搜索系統(tǒng)需要解決的一個(gè)主要問題是數(shù)據(jù)源選擇問題,即從海量數(shù)據(jù)源中選出一組最有可能返回相關(guān)結(jié)果的數(shù)據(jù)源?,F(xiàn)有的數(shù)據(jù)源
    發(fā)表于 01-04 15:00 ?0次下載
    基于LDA主題模型進(jìn)行<b class='flag-5'>數(shù)據(jù)源</b>選擇方法

    Deep Web數(shù)據(jù)源選擇和集成方法

    針對基于數(shù)據(jù)源質(zhì)量選擇方法的數(shù)據(jù)源數(shù)據(jù)爬取時(shí)存在代價(jià)大、重復(fù)率高的問題,提出一種結(jié)合兩層選擇模型的Deep Web數(shù)據(jù)源選擇和集成方法。該方法根據(jù)
    發(fā)表于 02-09 15:24 ?0次下載
    Deep Web<b class='flag-5'>數(shù)據(jù)源</b>選擇和集成方法

    數(shù)據(jù)倉庫入門之創(chuàng)建數(shù)據(jù)源

    首先需要創(chuàng)建一個(gè)數(shù)據(jù)源,SSAS(分析服務(wù))將利用數(shù)據(jù)源來連接數(shù)據(jù)庫。一、準(zhǔn)備環(huán)境二、啟動SSDT,新建項(xiàng)目三、創(chuàng)建數(shù)據(jù)源
    發(fā)表于 02-24 14:48 ?2494次閱讀
    <b class='flag-5'>數(shù)據(jù)</b>倉庫入門之創(chuàng)建<b class='flag-5'>數(shù)據(jù)源</b>

    Veritas收購Globanet,新功能將允許用戶獲得和使用更多數(shù)據(jù)源

    新功能將允許用戶獲得和使用更多數(shù)據(jù)源 北京 2020 年 11月11日 近日,全球數(shù)據(jù)保護(hù)及數(shù)據(jù)管理領(lǐng)域的領(lǐng)導(dǎo)者Veritas Technologies宣布已完成對Globanet的收購
    的頭像 發(fā)表于 11-12 16:31 ?1413次閱讀

    SpringBoot分布式事務(wù)解決方案(JTA+Atomic+多數(shù)據(jù)源

    首先,到底啥是分布式事務(wù)呢,比如我們在執(zhí)行一個(gè)業(yè)務(wù)邏輯的時(shí)候有兩步分別操作A數(shù)據(jù)源和B數(shù)據(jù)源,當(dāng)我們在A數(shù)據(jù)源執(zhí)行數(shù)據(jù)更改后,在B
    的頭像 發(fā)表于 04-11 11:05 ?1533次閱讀

    多數(shù)據(jù)源數(shù)據(jù)轉(zhuǎn)換和同步的ETL工具推薦

    有許多支持多數(shù)據(jù)源數(shù)據(jù)轉(zhuǎn)換和同步的ETL工具可供選擇。以下是一些常見的ETL工具和它們支持多數(shù)據(jù)源數(shù)據(jù)轉(zhuǎn)換和同步的特點(diǎn): Apache NiFi:Apache NiFi是一個(gè)開源的ET
    的頭像 發(fā)表于 07-28 16:32 ?993次閱讀

    weblogic修改數(shù)據(jù)源需要重啟嗎

    WebLogic是一款支持Java EE(Java Enterprise Edition)規(guī)范的應(yīng)用服務(wù)器,通過WebLogic可進(jìn)行數(shù)據(jù)源的配置和管理。在WebLogic中修改數(shù)據(jù)源是否需要重啟
    的頭像 發(fā)表于 12-05 16:09 ?1184次閱讀

    SpringBoot實(shí)現(xiàn)動態(tài)切換數(shù)據(jù)源

    最近在做業(yè)務(wù)需求時(shí),需要從不同的數(shù)據(jù)庫中獲取數(shù)據(jù)然后寫入到當(dāng)前數(shù)據(jù)庫中,因此涉及到切換數(shù)據(jù)源問題。本來想著使用Mybatis-plus中提供的動態(tài)數(shù)
    的頭像 發(fā)表于 12-08 10:53 ?923次閱讀
    <b class='flag-5'>SpringBoot</b>實(shí)現(xiàn)動態(tài)切換<b class='flag-5'>數(shù)據(jù)源</b>