Mybatis plus 配置多數(shù)據(jù)源的實(shí)現(xiàn)示例
記得面試時(shí)候,有面試官會(huì)問道,你們多數(shù)據(jù)源是怎么實(shí)現(xiàn)的呀。.......,一陣蒙蔽中,然后說道我們之前項(xiàng)目中,沒有用到多數(shù)據(jù)源。
所幸,目前做得項(xiàng)目中有一個(gè)業(yè)務(wù)邏輯中,用到多個(gè)數(shù)據(jù)庫數(shù)據(jù)情況,多數(shù)據(jù)源華麗上線。
一. mybatis plus
因?yàn)槲覀冺?xiàng)目是springboot+mybatis plus,有些人一看,mybatis還知道對吧,mybatis plus是什么鬼,其實(shí)字面意思可以理解,就是對mybatis進(jìn)行一些功能改造,一些封裝升級(jí),然后用起來特別方便。
核心功能的升級(jí)主要是以下三點(diǎn):
支持通用的 CRUD、代碼生成器與條件構(gòu)造器。
通用 CRUD:定義好 Mapper 接口后,只需要繼承 BaseMapper<T> 接口即可獲得通用的增刪改查功能,無需編寫任何接口方法與配置文件
條件構(gòu)造器:通過 EntityWrapper<T> (實(shí)體包裝類),可以用于拼接 SQL 語句,并且支持排序、分組查詢等復(fù)雜的 SQL
代碼生成器:支持一系列的策略配置與全局配置,比 MyBatis 的代碼生成更好用
二.多數(shù)據(jù)源配置開始
思路:
1、yml中配置多個(gè)數(shù)據(jù)源信息
2、通過AOP切換不同數(shù)據(jù)源
3、配合mybatis plus使用
1、yml配置
spring:
aop:
proxy-target-class: true
auto: true
datasource:
druid:
db1:
url: jdbc:mysql://localhost:3306/eboot
username: root
password: root
driver-class-name: com.mysql.jdbc.Driver
initialSize: 5
minIdle: 5
maxActive: 20
db2:
url: jdbc:oracle:thin:@192.168.136.222:ORCL
username: sa
password: sa123456
driver-class-name: oracle.jdbc.OracleDriver
initialSize: 5
minIdle: 5
maxActive: 20
2、啟動(dòng)多個(gè)數(shù)據(jù)源
@EnableTransactionManagement //開啟事務(wù)
@Configuration //spring中常用到注解,與xml配置相對立。是兩種加載bean方式
@MapperScan("com.df.openapi.**.mapper.db*") // 掃描mapperdao的地址
public class MybatisPlusConfig {
@Bean
public PaginationInterceptor paginationInterceptor() {
PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
// paginationInterceptor.setLocalPage(true); // 由于版本問題,有些類可能招不到這個(gè)方法,需要升級(jí)jar包
return paginationInterceptor;
}
@Bean(name = "db1")
@ConfigurationProperties(prefix = "spring.datasource.druid.db1")
public DataSource db1() {
return DruidDataSourceBuilder.create().build();
}
@Bean(name = "db2")
@ConfigurationProperties(prefix = "spring.datasource.druid.db2")
public DataSource db2() {
return DruidDataSourceBuilder.create().build();
}
/**
* 動(dòng)態(tài)數(shù)據(jù)源配置
*
* @return
*/
@Bean
@Primary
public DataSource multipleDataSource(@Qualifier("db1") DataSource db1,
@Qualifier("db2") DataSource db2) {
DynamicDataSource dynamicDataSource = new DynamicDataSource();
Map<Object, Object> targetDataSources = new HashMap<>();
targetDataSources.put(DBTypeEnum.db1.getValue(), db1);
targetDataSources.put(DBTypeEnum.db2.getValue(), db2);
dynamicDataSource.setTargetDataSources(targetDataSources);
dynamicDataSource.setDefaultTargetDataSource(db2); // 程序默認(rèn)數(shù)據(jù)源,這個(gè)要根據(jù)程序調(diào)用數(shù)據(jù)源頻次,經(jīng)常把常調(diào)用的數(shù)據(jù)源作為默認(rèn)
return dynamicDataSource;
}
@Bean("sqlSessionFactory")
public SqlSessionFactory sqlSessionFactory() throws Exception {
MybatisSqlSessionFactoryBean sqlSessionFactory = new MybatisSqlSessionFactoryBean();
sqlSessionFactory.setDataSource(multipleDataSource(db1(), db2()));
MybatisConfiguration configuration = new MybatisConfiguration();
configuration.setJdbcTypeForNull(JdbcType.NULL);
configuration.setMapUnderscoreToCamelCase(true);
configuration.setCacheEnabled(false);
sqlSessionFactory.setConfiguration(configuration);
//PerformanceInterceptor(),OptimisticLockerInterceptor()
//添加分頁功能
sqlSessionFactory.setPlugins(new Interceptor[]{
paginationInterceptor()
});
// sqlSessionFactory.setGlobalConfig(globalConfiguration()); //注釋掉全局配置,因?yàn)樵趚ml中讀取就是全局配置
return sqlSessionFactory.getObject();
}
/* @Bean
public GlobalConfiguration globalConfiguration() {
GlobalConfiguration conf = new GlobalConfiguration(new LogicSqlInjector());
conf.setLogicDeleteValue("-1");
conf.setLogicNotDeleteValue("1");
conf.setIdType(0);
conf.setMetaObjectHandler(new MyMetaObjectHandler());
conf.setDbColumnUnderline(true);
conf.setRefresh(true);
return conf;
}*/
}
3、DBType枚舉類
package com.df.openapi.config.db;
public enum DBTypeEnum {
db1("db1"), db2("db2");
private String value;
DBTypeEnum(String value) {
this.value = value;
}
public String getValue() {
return value;
}
}
4、動(dòng)態(tài)數(shù)據(jù)源決策
package com.df.openapi.config.db;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
public class DynamicDataSource extends AbstractRoutingDataSource {
private static final Logger LOGGER = LoggerFactory.getLogger(DynamicDataSource.class);
@Override
protected Object determineCurrentLookupKey() {
String datasource = DataSourceContextHolder.getDbType();
LOGGER.debug("使用數(shù)據(jù)源 {}", datasource);
return datasource;
}
}
5、設(shè)置、獲取數(shù)據(jù)源
public class DataSourceContextHolder {
private static final Logger LOGGER = LoggerFactory.getLogger(DataSourceContextHolder.class);
private static final ThreadLocal contextHolder = new ThreadLocal<>(); //實(shí)際上就是開啟多個(gè)線程,每個(gè)線程進(jìn)行初始化一個(gè)數(shù)據(jù)源
/**
* 設(shè)置數(shù)據(jù)源
* @param dbTypeEnum
*/
public static void setDbType(DBTypeEnum dbTypeEnum) {
contextHolder.set(dbTypeEnum.getValue());
}
/**
* 取得當(dāng)前數(shù)據(jù)源
* @return
*/
public static String getDbType() {
return (String) contextHolder.get();
}
/**
* 清除上下文數(shù)據(jù)
*/
public static void clearDbType() {
contextHolder.remove();
}
}
6、AOP實(shí)現(xiàn)的數(shù)據(jù)源切換
@Order設(shè)置的足夠小是為了讓他先執(zhí)行
/**
* aop的實(shí)現(xiàn)的數(shù)據(jù)源切換<br> * aop切點(diǎn),實(shí)現(xiàn)mapper類找尋,找到所屬大本營以后,如db1Aspect(),則會(huì)調(diào)用<br> * db1()前面之前的操作,進(jìn)行數(shù)據(jù)源的切換。
*/
@Component
@Order(value = -100)
@Slf4j
@Aspect
public class DataSourceAspect {
@Pointcut("execution(* com.zwyl.bazhong.dao.mapper.db1..*.*(..))")
private void db1Aspect() {
}
@Pointcut("execution(* com.zwyl.bazhong.dao.mapper.db2..*.*(..))")
private void db2Aspect() {
}
@Before("db1Aspect()")
public void db1() {
log.info("切換到db1 數(shù)據(jù)源...");
DataSourceContextHolder.setDbType(DBTypeEnum.db1);
}
@Before("db2Aspect()")
public void db2() {
log.info("切換到db2 數(shù)據(jù)源...");
DataSourceContextHolder.setDbType(DBTypeEnum.db2);
}
}
7、mapper層結(jié)構(gòu)

8、寫一個(gè)service測試一下
@Service
public class DictServiceImpl implements IDictService {
@Resource
private PtDictMapper ptDictMapper; //來自db1
@Resource
private SysDictMapper sysDictMapper; // 來自db2
@Override
public void getById(String id) {
PtDict dict = ptDictMapper.selectById("2bf6257fc8fe483c84c1ad7e89d632f6");
SysDict sysDict = sysDictMapper.getById("49");
System.out.println("123");
}
}
9、測試結(jié)果
總結(jié): 其實(shí)整個(gè)過程可以理解成,配置多數(shù)據(jù)源 xml中 -------> 然后通過加載多數(shù)源到spring工廠中-------->然后創(chuàng)建多線程,每個(gè)數(shù)據(jù)源對應(yīng)一個(gè)數(shù)據(jù)源--------->然后實(shí)際調(diào)用時(shí)候,會(huì)先通過aop匹配到某一具體數(shù)據(jù)源------------->然后實(shí)例化當(dāng)前數(shù)據(jù)源
到此這篇關(guān)于Mybatis plus 配置多數(shù)據(jù)源的實(shí)現(xiàn)示例的文章就介紹到這了,更多相關(guān)Mybatis plus 多數(shù)據(jù)源內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
java用靜態(tài)工廠代替構(gòu)造函數(shù)使用方法和優(yōu)缺點(diǎn)
這篇文章主要介紹了java用靜態(tài)工廠代替構(gòu)造函數(shù)使用方法和優(yōu)缺點(diǎn),需要的朋友可以參考下2014-02-02
springboot項(xiàng)目mysql-connector-java默認(rèn)版本如何查看
這篇文章主要介紹了springboot項(xiàng)目mysql-connector-java默認(rèn)版本如何查看問題,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-11-11
Java回調(diào)函數(shù)原理實(shí)例與代理模式的區(qū)別講解
今天小編就為大家分享一篇關(guān)于Java回調(diào)函數(shù)原理實(shí)例與代理模式的區(qū)別講解,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧2019-02-02
Springboot 2.x集成kafka 2.2.0的示例代碼
kafka近幾年更新非???,也可以看出kafka在企業(yè)中是用的頻率越來越高。本文主要為大家介紹了Springboot 2.x集成kafka 2.2.0的示例代碼,需要的可以參考一下2022-04-04
SpringBoot集成itext實(shí)現(xiàn)html轉(zhuǎn)PDF
iText是著名的開放源碼的站點(diǎn)sourceforge一個(gè)項(xiàng)目,是用于生成PDF文檔的一個(gè)java類庫,本文主要介紹了如何利用itext實(shí)現(xiàn)html轉(zhuǎn)PDF,需要的可以參考下2024-03-03
Spring Security如何使用URL地址進(jìn)行權(quán)限控制
這篇文章主要介紹了Spring Security如何使用URL地址進(jìn)行權(quán)限控制,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-12-12
Elasticsearch查詢之Match Query示例詳解
這篇文章主要為大家介紹了Elasticsearch查詢之Match查詢示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-04-04
java虛擬機(jī)參數(shù)-D、-X和-XX的區(qū)別小結(jié)
本文主要介紹了java虛擬機(jī)參數(shù)-D、-X和-XX的區(qū)別小結(jié),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-06-06

