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ù)需求,必須模擬棧,后進先出。
* </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對其進行了實現(xiàn),而后SpringBoot將其進行了整合,對其進行了托管,很方便開發(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進行管理。
看看MybatisPlus是怎么進行多數(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常用JDBC連接數(shù)據(jù)庫的方法總結(jié)
本篇文章是對在JAVA中常用JDBC連接數(shù)據(jù)庫的方法進行了詳細(xì)的總結(jié)分析,需要的朋友參考下2013-07-07
MybatisPlus中QueryWrapper常用方法總結(jié)
MyBatis-Plus是一個Mybatis增強版工具,在MyBatis上擴充了其他功能沒有改變其基本功能,為了簡化開發(fā)提交效率而存在,queryWrapper是mybatis plus中實現(xiàn)查詢的對象封裝操作類,本文就給大家總結(jié)了MybatisPlus中QueryWrapper的常用方法,需要的朋友可以參考下2023-07-07
Spring Boot+Drools規(guī)則引擎整合詳解
本篇文章主要介紹了Spring Boot+Drools規(guī)則引擎整合,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-09-09
IDEA中Java出現(xiàn)無效的源發(fā)行版錯誤的解決辦法
這篇文章主要給大家介紹了關(guān)于IDEA中Java出現(xiàn)無效的源發(fā)行版錯誤的解決辦法,IDEA中Java出現(xiàn)?效的源發(fā)?版解決辦法出現(xiàn)該問題的原因是項?Project當(dāng)中的jdk與電腦當(dāng)中的jdk版本不?致造成的,需要的朋友可以參考下2023-10-10
MyBatis的collection和association的使用解讀
這篇文章主要介紹了MyBatis的collection和association的使用解讀2023-12-12
Mybatis注解實現(xiàn)多數(shù)據(jù)源讀寫分離詳解
這篇文章主要給大家介紹了關(guān)于Mybatis注解實現(xiàn)多數(shù)據(jù)源讀寫分離的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家學(xué)習(xí)或者使用Mybatis具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧2019-09-09

