MybatisPlus多數(shù)據(jù)源及事務(wù)解決思路
關(guān)于多數(shù)據(jù)源解決方案
目前在SpringBoot
框架基礎(chǔ)上多數(shù)據(jù)源的解決方案大多手動(dòng)創(chuàng)建多個(gè)DataSource
,后續(xù)方案有三:
- 繼承
org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource
,使用AOP
切面注入相應(yīng)的數(shù)據(jù)源 ,但是這種做法僅僅適用單Service
方法使用一個(gè)數(shù)據(jù)源可行,如果單Service
方法有多個(gè)數(shù)據(jù)源執(zhí)行會(huì)造成誤讀。 - 通過
DataSource
配置JdbcTemplate
Bean,直接使用JdbcTemplate
操控?cái)?shù)據(jù)源。 - 分別通過
DataSource
創(chuàng)建SqlSessionFactory
并掃描相應(yīng)的Mapper
文件和Mapper
接口。
MybatisPlus
的多數(shù)據(jù)源
我通過閱讀源碼,發(fā)現(xiàn)MybatisPlus
的多數(shù)據(jù)源解決方案正是AOP
,繼承了org.springframework.jdbc.datasource.AbstractDataSource
,有自己對(duì)ThreadLocal
的處理。通過注解切換數(shù)據(jù)源。也就是說,MybatisPlus
只支持在單Service
方法內(nèi)操作一個(gè)數(shù)據(jù)源,畢竟官網(wǎng)都指明——“強(qiáng)烈建議只注解在service實(shí)現(xiàn)上”。
而后,注意看com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder
,也就是MybatisPlus
是如何切換數(shù)據(jù)源的。
重點(diǎn)看:
/** * 為什么要用鏈表存儲(chǔ)(準(zhǔn)確的是棧) * <pre> * 為了支持嵌套切換,如ABC三個(gè)service都是不同的數(shù)據(jù)源 * 其中A的某個(gè)業(yè)務(wù)要調(diào)B的方法,B的方法需要調(diào)用C的方法。一級(jí)一級(jí)調(diào)用切換,形成了鏈。 * 傳統(tǒng)的只設(shè)置當(dāng)前線程的方式不能滿足此業(yè)務(wù)需求,必須模擬棧,后進(jìn)先出。 * </pre> */ private static final ThreadLocal<Deque<String>> LOOKUP_KEY_HOLDER = new ThreadLocal() { @Override protected Object initialValue() { return new ArrayDeque(); } };
這段話翻譯為大家都能懂得的意思就是“可以同時(shí)操控多個(gè)數(shù)據(jù)源”。那么,在MYSQL
中,有語法為schemaName
+.
+tableName
,如此一來就不會(huì)誤走數(shù)據(jù)源了。
我繼續(xù)看MybatisPlus
是如何利用mybatis
本身的ORM
機(jī)制將實(shí)體類自動(dòng)映射以及生成SQL
語句的(這里插一句,MybatisPlus
的源碼易讀懂,寫的很不錯(cuò))。無意看到了注解com.baomidou.mybatisplus.annotation.TableName
中的schema
,如果在類上加schema
,在生成SQL
語句時(shí)就會(huì)生成schemaName
+.
+tableName
格式。
MybatisPlus
多數(shù)據(jù)源事務(wù)(JTA
)
簡單說明一下JTA
JTA
包括事務(wù)管理器(Transaction Manager)和一個(gè)或多個(gè)支持 XA 協(xié)議的資源管理器 ( Resource Manager ) 兩部分, 可以將資源管理器看做任意類型的持久化數(shù)據(jù)存儲(chǔ);事務(wù)管理器則承擔(dān)著所有事務(wù)參與單元的協(xié)調(diào)與控制。
JTA
只是提供了一個(gè)接口,并沒有提供具體的實(shí)現(xiàn)。
不過Atomikos
對(duì)其進(jìn)行了實(shí)現(xiàn),而后SpringBoot
將其進(jìn)行了整合,對(duì)其進(jìn)行了托管,很方便開發(fā)者拿來即用。
其中事務(wù)管理器的主要部分為UserTransaction
接口,開發(fā)人員通過此接口在信息系統(tǒng)中實(shí)現(xiàn)分布式事務(wù);而資源管理器則用來規(guī)范提供商(如數(shù)據(jù)庫連接提供商)所提供的事務(wù)服務(wù),它約定了事務(wù)的資源管理功能,使得 JTA
可以在異構(gòu)事務(wù)資源之間執(zhí)行協(xié)同溝通。
通常接入JTA
步驟(目的就是讓JTA
的UserTransaction
接管驅(qū)動(dòng)為分布式的數(shù)據(jù)源,通常為AtomikosDataSourceBean
):
- 配置好
AtomikosDataSourceBean
。 - 把
AtomikosDataSourceBean
交給SqlSessionFactory
。 - 配置
UserTransaction
事務(wù)管理。
但是我們用的是MybatisPlus
,我們需要做的是接管MybatisPlus
每一個(gè)數(shù)據(jù)源的配置,然后再把數(shù)據(jù)源依次交給MybatisPlus
進(jìn)行管理。
看看MybatisPlus
是怎么進(jìn)行多數(shù)據(jù)源配置的,源碼里有這幾個(gè)地方需要重點(diǎn)看一下:
com.baomidou.dynamic.datasource.provider.AbstractDataSourceProvider
,這個(gè)就是MybatisPlus
多數(shù)據(jù)源配置的方式,利用HashMap
來裝載。com.baomidou.dynamic.datasource.DynamicDataSourceCreator
,這個(gè)是每個(gè)數(shù)據(jù)源的配置方式。
其中com.baomidou.dynamic.datasource.provider.AbstractDataSourceProvider
實(shí)現(xiàn)了接口com.baomidou.dynamic.datasource.provider.DynamicDataSourceProvider
,是該接口的默認(rèn)的實(shí)現(xiàn)。也就是說我們只需要實(shí)現(xiàn)該接口,自己配置多數(shù)據(jù)源以及每個(gè)數(shù)據(jù)源的驅(qū)動(dòng),成為該接口的默認(rèn)實(shí)現(xiàn)就OK。
實(shí)現(xiàn)該接口,配置多數(shù)據(jù)源:
package xxx.xxx.xxx.config; import com.baomidou.dynamic.datasource.provider.DynamicDataSourceProvider; import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DataSourceProperty; import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceProperties; import org.springframework.context.annotation.Primary; import org.springframework.stereotype.Service; import javax.sql.DataSource; import java.util.HashMap; import java.util.Map; /** * @author : zuoyu * @description : 接管MybatisPlus多數(shù)據(jù)源至Atomikos管理 * @date : 2020-06-01 16:36 **/ @Service @Primary public class DynamicDataSourceProviderImpl implements DynamicDataSourceProvider { /** * 配置文件數(shù)據(jù)的松散綁定 */ private final DynamicDataSourceProperties properties; /** * Atomikos驅(qū)動(dòng)數(shù)據(jù)源創(chuàng)建 */ private final AtomikosDataSourceCreator atomikosDataSourceCreator; public DynamicDataSourceProviderImpl(DynamicDataSourceProperties properties, AtomikosDataSourceCreator atomikosDataSourceCreator) { this.properties = properties; this.atomikosDataSourceCreator = atomikosDataSourceCreator; } @Override public Map<String, DataSource> loadDataSources() { Map<String, DataSourceProperty> dataSourcePropertiesMap = properties.getDatasource(); Map<String, DataSource> dataSourceMap = new HashMap<>(dataSourcePropertiesMap.size() * 2); for (Map.Entry<String, DataSourceProperty> item : dataSourcePropertiesMap.entrySet()) { String pollName = item.getKey(); DataSourceProperty dataSourceProperty = item.getValue(); dataSourceProperty.setPollName(pollName); dataSourceMap.put(pollName, atomikosDataSourceCreator.createDataSource(dataSourceProperty)); } return dataSourceMap; } }
Atomikos
驅(qū)動(dòng)數(shù)據(jù)源創(chuàng)建:
package xxx.xxx.xxx.config; import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DataSourceProperty; import com.mysql.jdbc.jdbc2.optional.MysqlXADataSource; import org.springframework.boot.jta.atomikos.AtomikosDataSourceBean; import org.springframework.stereotype.Component; import javax.sql.DataSource; /** * @author : zuoyu * @description : 事務(wù)數(shù)據(jù)源 * @date : 2020-06-01 17:30 **/ @Component public class AtomikosDataSourceCreator { /** * 創(chuàng)建數(shù)據(jù)源 * * @param dataSourceProperty 數(shù)據(jù)源信息 * @return 數(shù)據(jù)源 */ public DataSource createDataSource(DataSourceProperty dataSourceProperty) { MysqlXADataSource mysqlXaDataSource = new MysqlXADataSource(); mysqlXaDataSource.setUrl(dataSourceProperty.getUrl()); mysqlXaDataSource.setPassword(dataSourceProperty.getPassword()); mysqlXaDataSource.setUser(dataSourceProperty.getUsername()); AtomikosDataSourceBean xaDataSource = new AtomikosDataSourceBean(); xaDataSource.setXaDataSource(mysqlXaDataSource); xaDataSource.setMinPoolSize(5); xaDataSource.setBorrowConnectionTimeout(60); xaDataSource.setMaxPoolSize(20); xaDataSource.setXaDataSourceClassName(dataSourceProperty.getDriverClassName()); xaDataSource.setTestQuery("SELECT 1 FROM DUAL"); xaDataSource.setUniqueResourceName(dataSourceProperty.getPollName()); return xaDataSource; } }
配置JTA
事務(wù)管理器:
package xxx.xxx.xxx.config; import com.atomikos.icatch.jta.UserTransactionImp; import com.atomikos.icatch.jta.UserTransactionManager; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.DependsOn; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.annotation.EnableTransactionManagement; import org.springframework.transaction.jta.JtaTransactionManager; import javax.transaction.TransactionManager; import javax.transaction.UserTransaction; /** * @author : zuoyu * @description : 分布式事務(wù)配置 * @date : 2020-06-01 17:55 **/ @Configuration @EnableTransactionManagement public class TransactionManagerConfig { @Bean(name = "userTransaction") public UserTransaction userTransaction() throws Throwable { UserTransactionImp userTransactionImp = new UserTransactionImp(); userTransactionImp.setTransactionTimeout(10000); return userTransactionImp; } @Bean(name = "atomikosTransactionManager") public TransactionManager atomikosTransactionManager() throws Throwable { UserTransactionManager userTransactionManager = new UserTransactionManager(); userTransactionManager.setForceShutdown(false); return userTransactionManager; } @Bean(name = "transactionManager") @DependsOn({"userTransaction", "atomikosTransactionManager"}) public PlatformTransactionManager transactionManager() throws Throwable { return new JtaTransactionManager(userTransaction(), atomikosTransactionManager()); } }
如此,即可
這樣一來便可解決MybatisPlus
多數(shù)據(jù)源的誤走,且支持多數(shù)據(jù)源下的事務(wù)問題。
做任何事情,重要的是思路,而不是搬磚。
到此這篇關(guān)于MybatisPlus多數(shù)據(jù)源及事務(wù)解決思路的文章就介紹到這了,更多相關(guān)MybatisPlus多數(shù)據(jù)源事務(wù)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
java圖片色階調(diào)整和亮度調(diào)整代碼示例
這篇文章主要介紹了java圖片色階調(diào)整和亮度調(diào)整代碼示例,具有一定參考價(jià)值,需要的朋友可以了解下。2017-11-11Spring MVC---數(shù)據(jù)綁定和表單標(biāo)簽詳解
本篇文章主要介紹了Spring MVC---數(shù)據(jù)綁定和表單標(biāo)簽詳解,具有一定的參考價(jià)值,有興趣的可以了解一下。2017-01-01SpringBoot RESTful風(fēng)格入門講解
RESTful是一種web軟件風(fēng)格,它不是標(biāo)準(zhǔn)也不是協(xié)議,它不一定要采用,只是一種風(fēng)格,它倡導(dǎo)的是一個(gè)資源定位(url)及資源操作的風(fēng)格,這篇文章主要介紹了SpringBoot使用RESTful接口2022-11-11Spring AOP定義Before增加實(shí)戰(zhàn)案例詳解
這篇文章主要介紹了Spring AOP定義Before增加,結(jié)合實(shí)例形式詳細(xì)分析了Spring面向切面AOP定義Before增加相關(guān)定義與使用技巧,需要的朋友可以參考下2020-01-01java中g(shù)radle項(xiàng)目報(bào)錯(cuò)org.gradle?.api.plugins.MavenPlugin解決辦法
在使用Gradle時(shí)開發(fā)者可能會(huì)遇到org.gradle?.api.plugins.MavenPlugin報(bào)錯(cuò)提醒,這篇文章主要給大家介紹了關(guān)于java中g(shù)radle項(xiàng)目報(bào)錯(cuò)org.gradle?.api.plugins.MavenPlugin的解決辦法,需要的朋友可以參考下2023-12-12Myeclipse工程發(fā)布時(shí)端口占用問題的解決方法
這篇文章主要介紹了Myeclipse工程發(fā)布時(shí)端口占用問題的解決方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-12-12