MybatisPlus多數(shù)據(jù)源及事務(wù)解決思路
關(guān)于多數(shù)據(jù)源解決方案
目前在SpringBoot框架基礎(chǔ)上多數(shù)據(jù)源的解決方案大多手動創(chuàng)建多個DataSource,后續(xù)方案有三:
- 繼承
org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource,使用AOP切面注入相應(yīng)的數(shù)據(jù)源 ,但是這種做法僅僅適用單Service方法使用一個數(shù)據(jù)源可行,如果單Service方法有多個數(shù)據(jù)源執(zhí)行會造成誤讀。 - 通過
DataSource配置JdbcTemplateBean,直接使用JdbcTemplate操控數(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,有自己對ThreadLocal的處理。通過注解切換數(shù)據(jù)源。也就是說,MybatisPlus只支持在單Service方法內(nèi)操作一個數(shù)據(jù)源,畢竟官網(wǎng)都指明——“強烈建議只注解在service實現(xiàn)上”。
而后,注意看com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder,也就是MybatisPlus是如何切換數(shù)據(jù)源的。
重點看:
/**
* 為什么要用鏈表存儲(準(zhǔn)確的是棧)
* <pre>
* 為了支持嵌套切換,如ABC三個service都是不同的數(shù)據(jù)源
* 其中A的某個業(yè)務(wù)要調(diào)B的方法,B的方法需要調(diào)用C的方法。一級一級調(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ù)據(jù)源”。那么,在MYSQL中,有語法為schemaName+. +tableName,如此一來就不會誤走數(shù)據(jù)源了。
我繼續(xù)看MybatisPlus是如何利用mybatis本身的ORM機制將實體類自動映射以及生成SQL語句的(這里插一句,MybatisPlus的源碼易讀懂,寫的很不錯)。無意看到了注解com.baomidou.mybatisplus.annotation.TableName中的schema,如果在類上加schema,在生成SQL語句時就會生成schemaName+. +tableName格式。
MybatisPlus多數(shù)據(jù)源事務(wù)(JTA)
簡單說明一下JTA
JTA包括事務(wù)管理器(Transaction Manager)和一個或多個支持 XA 協(xié)議的資源管理器 ( Resource Manager ) 兩部分, 可以將資源管理器看做任意類型的持久化數(shù)據(jù)存儲;事務(wù)管理器則承擔(dān)著所有事務(wù)參與單元的協(xié)調(diào)與控制。
JTA只是提供了一個接口,并沒有提供具體的實現(xiàn)。
不過Atomikos對其進(jìn)行了實現(xiàn),而后SpringBoot將其進(jìn)行了整合,對其進(jìn)行了托管,很方便開發(fā)者拿來即用。
其中事務(wù)管理器的主要部分為UserTransaction 接口,開發(fā)人員通過此接口在信息系統(tǒng)中實現(xiàn)分布式事務(wù);而資源管理器則用來規(guī)范提供商(如數(shù)據(jù)庫連接提供商)所提供的事務(wù)服務(wù),它約定了事務(wù)的資源管理功能,使得 JTA 可以在異構(gòu)事務(wù)資源之間執(zhí)行協(xié)同溝通。
通常接入JTA步驟(目的就是讓JTA的UserTransaction接管驅(qū)動為分布式的數(shù)據(jù)源,通常為AtomikosDataSourceBean):
- 配置好
AtomikosDataSourceBean。 - 把
AtomikosDataSourceBean交給SqlSessionFactory。 - 配置
UserTransaction事務(wù)管理。
但是我們用的是MybatisPlus,我們需要做的是接管MybatisPlus每一個數(shù)據(jù)源的配置,然后再把數(shù)據(jù)源依次交給MybatisPlus進(jìn)行管理。
看看MybatisPlus是怎么進(jìn)行多數(shù)據(jù)源配置的,源碼里有這幾個地方需要重點看一下:
com.baomidou.dynamic.datasource.provider.AbstractDataSourceProvider,這個就是MybatisPlus多數(shù)據(jù)源配置的方式,利用HashMap來裝載。com.baomidou.dynamic.datasource.DynamicDataSourceCreator,這個是每個數(shù)據(jù)源的配置方式。
其中com.baomidou.dynamic.datasource.provider.AbstractDataSourceProvider實現(xiàn)了接口com.baomidou.dynamic.datasource.provider.DynamicDataSourceProvider,是該接口的默認(rèn)的實現(xiàn)。也就是說我們只需要實現(xiàn)該接口,自己配置多數(shù)據(jù)源以及每個數(shù)據(jù)源的驅(qū)動,成為該接口的默認(rèn)實現(xiàn)就OK。
實現(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ū)動數(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ū)動數(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)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
java圖片色階調(diào)整和亮度調(diào)整代碼示例
這篇文章主要介紹了java圖片色階調(diào)整和亮度調(diào)整代碼示例,具有一定參考價值,需要的朋友可以了解下。2017-11-11
Spring MVC---數(shù)據(jù)綁定和表單標(biāo)簽詳解
本篇文章主要介紹了Spring MVC---數(shù)據(jù)綁定和表單標(biāo)簽詳解,具有一定的參考價值,有興趣的可以了解一下。2017-01-01
SpringBoot RESTful風(fēng)格入門講解
RESTful是一種web軟件風(fēng)格,它不是標(biāo)準(zhǔn)也不是協(xié)議,它不一定要采用,只是一種風(fēng)格,它倡導(dǎo)的是一個資源定位(url)及資源操作的風(fēng)格,這篇文章主要介紹了SpringBoot使用RESTful接口2022-11-11
Spring AOP定義Before增加實戰(zhàn)案例詳解
這篇文章主要介紹了Spring AOP定義Before增加,結(jié)合實例形式詳細(xì)分析了Spring面向切面AOP定義Before增加相關(guān)定義與使用技巧,需要的朋友可以參考下2020-01-01
java中g(shù)radle項目報錯org.gradle?.api.plugins.MavenPlugin解決辦法
在使用Gradle時開發(fā)者可能會遇到org.gradle?.api.plugins.MavenPlugin報錯提醒,這篇文章主要給大家介紹了關(guān)于java中g(shù)radle項目報錯org.gradle?.api.plugins.MavenPlugin的解決辦法,需要的朋友可以參考下2023-12-12

