springboot+druid+mybatis plus的多數(shù)據(jù)源配置

記得面試時(shí)候,有面試官會(huì)問道挺份,你們多數(shù)據(jù)源是怎么實(shí)現(xiàn)的呀褒翰。.......,一陣蒙蔽中,然后說(shuō)道我們之前項(xiàng)目中优训,沒有用到多數(shù)據(jù)源朵你。

所幸,目前做得項(xiàng)目中有一個(gè)業(yè)務(wù)邏輯中揣非,用到多個(gè)數(shù)據(jù)庫(kù)數(shù)據(jù)情況抡医,多數(shù)據(jù)源華麗上線。

一. mybatis plus

?因?yàn)槲覀冺?xiàng)目是springboot+mybatis plus早敬,有些人一看魂拦,mybatis還知道對(duì)吧,mybatis plus是什么鬼搁嗓,其實(shí)字面意思可以理解芯勘,就是對(duì)mybatis進(jìn)行一些功能改造,一些封裝升級(jí)腺逛,然后用起來(lái)特別方便荷愕。

? ? ?核心功能的升級(jí)主要是以下三點(diǎn):

? ? ?支持通用的 CRUD、代碼生成器與條件構(gòu)造器棍矛。

通用 CRUD:定義好 Mapper 接口后安疗,只需要繼承 BaseMapper 接口即可獲得通用的增刪改查功能,無(wú)需編寫任何接口方法與配置文件

條件構(gòu)造器:通過 EntityWrapper (實(shí)體包裝類)够委,可以用于拼接 SQL 語(yǔ)句荐类,并且支持排序、分組查詢等復(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配置相對(duì)立季蚂。是兩種加載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()

? ? ? ? //添加分頁(yè)功能

? ? ? ? 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ù)源切換

* aop切點(diǎn)礁蔗,實(shí)現(xiàn)mapper類找尋觉义,找到所屬大本營(yíng)以后,如db1Aspect(),則會(huì)調(diào)用

* db1()前面之前的操作浴井,進(jìn)行數(shù)據(jù)源的切換晒骇。 */@Component@Order(value = -100)@Slf4j@Aspectpublic 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測(cè)試一下

@Service

public class DictServiceImpl implements IDictService {

? ? @Resource

? ? private PtDictMapper ptDictMapper; //來(lái)自db1

? ? @Resource

? ? private SysDictMapper sysDictMapper; // 來(lái)自db2

? ? @Override

? ? public void getById(String id) {

? ? ? ? PtDict dict = ptDictMapper.selectById("2bf6257fc8fe483c84c1ad7e89d632f6");

? ? ? ? SysDict sysDict = sysDictMapper.getById("49");

? ? ? ? System.out.println("123");

? ? }

}

9洪囤、測(cè)試結(jié)果

總結(jié): 其實(shí)整個(gè)過程可以理解成,配置多數(shù)據(jù)源 xml中 ?-------> 然后通過加載多數(shù)源到spring工廠中-------->然后創(chuàng)建多線程撕氧,每個(gè)數(shù)據(jù)源對(duì)應(yīng)一個(gè)數(shù)據(jù)源--------->然后實(shí)際調(diào)用時(shí)候瘤缩,會(huì)先通過aop匹配到某一具體數(shù)據(jù)源------------->然后實(shí)例化當(dāng)前數(shù)據(jù)源

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市伦泥,隨后出現(xiàn)的幾起案子剥啤,更是在濱河造成了極大的恐慌,老刑警劉巖不脯,帶你破解...
    沈念sama閱讀 206,482評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件府怯,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡防楷,警方通過查閱死者的電腦和手機(jī)牺丙,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)复局,“玉大人冲簿,你說(shuō)我怎么就攤上這事⌒ごВ” “怎么了民假?”我有些...
    開封第一講書人閱讀 152,762評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)龙优。 經(jīng)常有香客問我,道長(zhǎng)事秀,這世上最難降的妖魔是什么彤断? 我笑而不...
    開封第一講書人閱讀 55,273評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮易迹,結(jié)果婚禮上宰衙,老公的妹妹穿的比我還像新娘。我一直安慰自己睹欲,他們只是感情好供炼,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,289評(píng)論 5 373
  • 文/花漫 我一把揭開白布一屋。 她就那樣靜靜地躺著,像睡著了一般袋哼。 火紅的嫁衣襯著肌膚如雪冀墨。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,046評(píng)論 1 285
  • 那天涛贯,我揣著相機(jī)與錄音诽嘉,去河邊找鬼。 笑死弟翘,一個(gè)胖子當(dāng)著我的面吹牛虫腋,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播稀余,決...
    沈念sama閱讀 38,351評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼悦冀,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了睛琳?” 一聲冷哼從身側(cè)響起盒蟆,我...
    開封第一講書人閱讀 36,988評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎掸掏,沒想到半個(gè)月后茁影,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,476評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡丧凤,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,948評(píng)論 2 324
  • 正文 我和宋清朗相戀三年募闲,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片愿待。...
    茶點(diǎn)故事閱讀 38,064評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡浩螺,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出仍侥,到底是詐尸還是另有隱情要出,我是刑警寧澤,帶...
    沈念sama閱讀 33,712評(píng)論 4 323
  • 正文 年R本政府宣布农渊,位于F島的核電站患蹂,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏砸紊。R本人自食惡果不足惜传于,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,261評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望醉顽。 院中可真熱鬧沼溜,春花似錦、人聲如沸游添。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,264評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至找都,卻和暖如春唇辨,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背檐嚣。 一陣腳步聲響...
    開封第一講書人閱讀 31,486評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工助泽, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人嚎京。 一個(gè)月前我還...
    沈念sama閱讀 45,511評(píng)論 2 354
  • 正文 我出身青樓嗡贺,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親鞍帝。 傳聞我的和親對(duì)象是個(gè)殘疾皇子诫睬,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,802評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容