Spring Boot與多資料來源那點事兒~
持續原創輸出,點選上方藍字關注我
目錄
前言 寫這篇文章的目的 什麼是多資料來源? 何時用到多資料來源? 整合單一的資料來源 整合Mybatis 多資料來源如何整合? 什麼是動態資料來源? 資料來源切換如何保證執行緒隔離? 如何構造一個動態資料來源? 定義一個註解 如何與Mybatis整合? 演示
總結
前言
大約在19年的這個時候,老同事公司在做醫療系統,需要和HIS
系統對接一些資訊,比如患者、醫護、醫囑、科室等資訊。但是起初並不知道如何與HIS無縫對接,於是向我取經。
最終經過討論採用了檢視對接
的方式,大致就是HIS系統提供檢視,他們進行對接。
寫這篇文章的目的
這篇文章將會涉及到Spring Boot 與Mybatis、資料庫整合,類似於整合Mybatis與資料庫的文章其實網上很多,作者此前也寫過一篇文章詳細的介紹了一些整合的套路:Spring Boot 整合多點套路,少走點彎路~,有興趣的可以看看。
什麼是多資料來源?
最常見的單一應用中最多涉及到一個數據庫,即是一個數據源(Datasource
)。那麼顧名思義,多資料來源就是在一個單一應用中涉及到了兩個及以上的資料庫了。
其實在配置資料來源的時候就已經很明確這個定義了,如以下程式碼:
@Bean(name = "dataSource") public DataSource dataSource() { DruidDataSource druidDataSource = new DruidDataSource(); druidDataSource.setUrl(url); druidDataSource.setUsername(username); druidDataSource.setDriverClassName(driverClassName); druidDataSource.setPassword(password); return druidDataSource; }
url
、username
、password
這三個屬性已經唯一確定了一個數據庫了,DataSource
則是依賴這三個創建出來的。則多資料來源即是配置多個DataSource
(暫且這麼理解)。
何時用到多資料來源?
正如前言介紹到的一個場景,相信大多數做過醫療系統的都會和HIS
打交道,為了簡化護士以及醫生的操作流程,必須要將必要的資訊從HIS
系統對接過來,據我瞭解的大致有兩種方案如下:
HIS
提供檢視,比如醫護檢視、患者檢視等,而此時其他系統只需要定時的從HIS
檢視中讀取資料同步到自己資料庫中即可。HIS
提供介面,無論是webService
還是HTTP
形式都是可行的,此時其他系統只需要按照要求調介面即可。
很明顯第一種方案涉及到了至少兩個資料庫了,一個是
HIS
資料庫,一個自己系統的資料庫,在單一應用中必然需要用到多資料來源的切換才能達到目的。
當然多資料來源的使用場景還是有很多的,以上只是簡單的一個場景。
整合單一的資料來源
本文使用阿里的資料庫連線池druid
,新增依賴如下:
<!--druid連線池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.9</version>
</dependency>
阿里的資料庫連線池非常強大,比如資料監控
、資料庫加密
等等內容,本文僅僅演示與Spring Boot整合的過程,一些其他的功能後續可以自己研究新增。
Druid連線池的starter
的自動配置類是DruidDataSourceAutoConfigure
,類上標註如下一行註解:
@EnableConfigurationProperties({DruidStatProperties.class, DataSourceProperties.class})
@EnableConfigurationProperties
這個註解使得配置檔案中的配置生效並且對映到指定類的屬性。
DruidStatProperties
中指定的字首是spring.datasource.druid
,這個配置主要是用來設定連線池的一些引數。
DataSourceProperties
中指定的字首是spring.datasource
,這個主要是用來設定資料庫的url
、username
、password
等資訊。
因此我們只需要在全域性配置檔案中指定資料庫的一些配置以及連線池的一些配置資訊即可,字首分別是spring.datasource.druid
、spring.datasource
,以下是個人隨便配置的(application.properties
):
spring.datasource.url=jdbc\:mysql\://120.26.101.xxx\:3306/xxx?useUnicode\=true&characterEncoding\=UTF-8&zeroDateTimeBehavior\=convertToNull&useSSL\=false&allowMultiQueries\=true&serverTimezone=Asia/Shanghai
spring.datasource.username=root
spring.datasource.password=xxxx
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
#初始化連線大小
spring.datasource.druid.initial-size=0
#連線池最大使用連線數量
spring.datasource.druid.max-active=20
#連線池最小空閒
spring.datasource.druid.min-idle=0
#獲取連線最大等待時間
spring.datasource.druid.max-wait=6000
spring.datasource.druid.validation-query=SELECT 1
#spring.datasource.druid.validation-query-timeout=6000
spring.datasource.druid.test-on-borrow=false
spring.datasource.druid.test-on-return=false
spring.datasource.druid.test-while-idle=true
#配置間隔多久才進行一次檢測,檢測需要關閉的空閒連線,單位是毫秒
spring.datasource.druid.time-between-eviction-runs-millis=60000
#置一個連線在池中最小生存的時間,單位是毫秒
spring.datasource.druid.min-evictable-idle-time-millis=25200000
#spring.datasource.druid.max-evictable-idle-time-millis=
#開啟removeAbandoned功能,多少時間內必須關閉連線
spring.datasource.druid.removeAbandoned=true
#1800秒,也就是30分鐘
spring.datasource.druid.remove-abandoned-timeout=1800
#<!-- 1800秒,也就是30分鐘 -->
spring.datasource.druid.log-abandoned=true
spring.datasource.druid.filters=mergeStat
在全域性配置檔案
application.properties
檔案中配置以上的資訊即可注入一個數據源到Spring Boot中。其實這僅僅是一種方式,下面介紹另外一種方式。
在自動配置類中DruidDataSourceAutoConfigure
中有如下一段程式碼:
@Bean(initMethod = "init")
@ConditionalOnMissingBean
public DataSource dataSource() {
LOGGER.info("Init DruidDataSource");
return new DruidDataSourceWrapper();
}
@ConditionalOnMissingBean
和@Bean
這兩個註解的結合,意味著我們可以覆蓋,只需要提前在IOC
中注入一個DataSource
型別的Bean
即可。
因此我們在自定義的配置類中定義如下配置即可:
/**
* @Bean:向IOC容器中注入一個Bean
* @ConfigurationProperties:使得配置檔案中以spring.datasource為字首的屬性對映到Bean的屬性中
* @return
*/
@ConfigurationProperties(prefix = "spring.datasource")
@Bean
public DataSource dataSource(){
//做一些其他的自定義配置,比如密碼加密等......
return new DruidDataSource();
}
以上介紹了兩種資料來源的配置方式,第一種比較簡單,第二種適合擴充套件,按需選擇。
整合Mybatis
Spring Boot 整合Mybatis其實很簡單,簡單的幾步就搞定,首先新增依賴:
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.0.0</version>
</dependency>
第二步找到自動配置類MybatisAutoConfiguration
,有如下一行程式碼:
@EnableConfigurationProperties(MybatisProperties.class)
老套路了,全域性配置檔案中配置字首為
mybatis
的配置將會對映到該類中的屬性。
可配置的東西很多,比如XML檔案的位置
、型別處理器
等等,如下簡單的配置:
mybatis.type-handlers-package=com.demo.typehandler
mybatis.configuration.map-underscore-to-camel-case=true
如果需要通過包掃描的方式注入Mapper,則需要在配置類上加入一個註解:@MapperScan
,其中的value屬性指定需要掃描的包。
直接在全域性配置檔案配置各種屬性是一種比較簡單的方式,其實的任何元件的整合都有不少於兩種的配置方式,下面來介紹下配置類如何配置。
MybatisAutoConfiguration
自動配置類有如下一斷程式碼:
@Bean
@ConditionalOnMissingBean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {}
@ConditionalOnMissingBean
和@Bean
真是老搭檔了,意味著我們又可以覆蓋,只需要在IOC容器中注入SqlSessionFactory(Mybatis六劍客之一生產者)
。
在自定義配置類中注入即可,如下:
/**
* 注入SqlSessionFactory
*/
@Bean("sqlSessionFactory1")
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSource);
sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:/mapper/**/*.xml"));
org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();
// 自動將資料庫中的下劃線轉換為駝峰格式
configuration.setMapUnderscoreToCamelCase(true);
configuration.setDefaultFetchSize(100);
configuration.setDefaultStatementTimeout(30);
sqlSessionFactoryBean.setConfiguration(configuration);
return sqlSessionFactoryBean.getObject();
}
以上介紹了配置Mybatis的兩種方式,其實在大多數場景中使用第一種已經夠用了,至於為什麼介紹第二種呢?當然是為了多資料來源的整合而做準備了。
在MybatisAutoConfiguration
中有一行很重要的程式碼,如下:
@ConditionalOnSingleCandidate(DataSource.class)
@ConditionalOnSingleCandidate
這個註解的意思是當IOC容器中只有一個候選Bean的例項才會生效。
這行程式碼標註在Mybatis的自動配置類中有何含義呢?下面介紹,哈哈哈~
多資料來源如何整合?
上文留下的問題:為什麼的Mybatis自動配置上標註如下一行程式碼:
@ConditionalOnSingleCandidate(DataSource.class)
以上這行程式碼的言外之意:當IOC容器中只有一個數據源DataSource,這個自動配置類才會生效。
哦?照這樣搞,多資料來源是不能用Mybatis嗎?
可能大家會有一個誤解,認為多資料來源就是多個的DataSource
並存的,當然這樣說也不是不正確。
多資料來源的情況下並不是多個數據源並存的,Spring提供了
AbstractRoutingDataSource
這樣一個抽象類,使得能夠在多資料來源的情況下任意切換,相當於一個動態路由的作用,作者稱之為動態資料來源
。因此Mybatis只需要配置這個動態資料來源即可。
什麼是動態資料來源?
動態資料來源簡單的說就是能夠自由切換的資料來源,類似於一個動態路由的感覺,Spring 提供了一個抽象類AbstractRoutingDataSource
,這個抽象類中喲一個屬性,如下:
private Map<Object, Object> targetDataSources;
targetDataSources
是一個Map
結構,所有需要切換的資料來源都存放在其中,根據指定的KEY
進行切換。當然還有一個預設的資料來源。
AbstractRoutingDataSource
這個抽象類中有一個抽象方法需要子類實現,如下:
protected abstract Object determineCurrentLookupKey();
determineCurrentLookupKey()
這個方法的返回值決定了需要切換的資料來源的KEY
,就是根據這個KEY
從targetDataSources
取值(資料來源)。
資料來源切換如何保證執行緒隔離?
資料來源屬於一個公共的資源,在多執行緒的情況下如何保證執行緒隔離呢?不能我這邊切換了影響其他執行緒的執行。
說到執行緒隔離,自然會想到
ThreadLocal
了,將切換資料來源的KEY
(用於從targetDataSources
中取值)儲存在ThreadLocal
中,執行結束之後清除即可。
單獨封裝了一個DataSourceHolder
,內部使用ThreadLocal
隔離執行緒,程式碼如下:
/**
* 使用ThreadLocal儲存切換資料來源後的KEY
*/
public class DataSourceHolder {
//執行緒 本地環境
private static final ThreadLocal<String> dataSources = new InheritableThreadLocal();
//設定資料來源
public static void setDataSource(String datasource) {
dataSources.set(datasource);
}
//獲取資料來源
public static String getDataSource() {
return dataSources.get();
}
//清除資料來源
public static void clearDataSource() {
dataSources.remove();
}
}
如何構造一個動態資料來源?
上文說過只需繼承一個抽象類AbstractRoutingDataSource
,重寫其中的一個方法determineCurrentLookupKey()
即可。程式碼如下:
/**
* 動態資料來源,繼承AbstractRoutingDataSource
*/
public class DynamicDataSource extends AbstractRoutingDataSource {
/**
* 返回需要使用的資料來源的key,將會按照這個KEY從Map獲取對應的資料來源(切換)
* @return
*/
@Override
protected Object determineCurrentLookupKey() {
//從ThreadLocal中取出KEY
return DataSourceHolder.getDataSource();
}
/**
* 構造方法填充Map,構建多資料來源
*/
public DynamicDataSource(DataSource defaultTargetDataSource, Map<Object, Object> targetDataSources) {
//預設的資料來源,可以作為主資料來源
super.setDefaultTargetDataSource(defaultTargetDataSource);
//目標資料來源
super.setTargetDataSources(targetDataSources);
//執行afterPropertiesSet方法,完成屬性的設定
super.afterPropertiesSet();
}
}
上述程式碼很簡單,分析如下:
一個多參的構造方法,指定了預設的資料來源和目標資料來源。 重寫 determineCurrentLookupKey()
方法,返回資料來源對應的KEY
,這裡是直接從ThreadLocal
中取值,就是上文封裝的DataSourceHolder
。
定義一個註解
為了操作方便且低耦合,不能每次需要切換的資料來源的時候都要手動調一下介面吧,可以定義一個切換資料來源的註解,如下:
/**
* 切換資料來源的註解
*/
@Target(value = ElementType.METHOD)
@Retention(value = RetentionPolicy.RUNTIME)
@Documented
public @interface SwitchSource {
/**
* 預設切換的資料來源KEY
*/
String DEFAULT_NAME = "hisDataSource";
/**
* 需要切換到資料的KEY
*/
String value() default DEFAULT_NAME;
}
註解中只有一個value
屬性,指定了需要切換資料來源的KEY
。
有註解還不行,當然還要有切面,程式碼如下:
@Aspect
//優先順序設定到最高
@Order(Ordered.HIGHEST_PRECEDENCE)
@Component
@Slf4j
public class DataSourceAspect {
@Pointcut("@annotation(SwitchSource)")
public void pointcut() {
}
/**
* 在方法執行之前切換到指定的資料來源
* @param joinPoint
*/
@Before(value = "pointcut()")
public void beforeOpt(JoinPoint joinPoint) {
/*因為是對註解進行切面,所以這邊無需做過多判定,直接獲取註解的值,進行環繞,將資料來源設定成遠方,然後結束後,清楚當前執行緒資料來源*/
Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
SwitchSource switchSource = method.getAnnotation(SwitchSource.class);
log.info("[Switch DataSource]:" + switchSource.value());
DataSourceHolder.setDataSource(switchSource.value());
}
/**
* 方法執行之後清除掉ThreadLocal中儲存的KEY,這樣動態資料來源會使用預設的資料來源
*/
@After(value = "pointcut()")
public void afterOpt() {
DataSourceHolder.clearDataSource();
log.info("[Switch Default DataSource]");
}
}
這個ASPECT
很容易理解,beforeOpt()
在方法之前執行,取值@SwitchSource
中value屬性設定到ThreadLocal
中;afterOpt()
方法在方法執行之後執行,清除掉ThreadLocal
中的KEY
,保證瞭如果不切換資料來源,則用預設的資料來源。
如何與Mybatis整合?
單一資料來源與Mybatis整合上文已經詳細講解了,資料來源DataSource
作為引數構建了SqlSessionFactory
,同樣的思想,只需要把這個資料來源換成動態資料來源即可。注入的程式碼如下:
/**
* 建立動態資料來源的SqlSessionFactory,傳入的是動態資料來源
* @Primary這個註解很重要,如果專案中存在多個SqlSessionFactory,這個註解一定要加上
*/
@Primary
@Bean("sqlSessionFactory2")
public SqlSessionFactory sqlSessionFactoryBean(DynamicDataSource dynamicDataSource) throws Exception {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dynamicDataSource);
org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();
configuration.setMapUnderscoreToCamelCase(true);
configuration.setDefaultFetchSize(100);
configuration.setDefaultStatementTimeout(30);
sqlSessionFactoryBean.setConfiguration(configuration);
return sqlSessionFactoryBean.getObject();
}
與Mybatis整合很簡單,只需要把資料來源替換成自定義的動態資料來源
DynamicDataSource
。
那麼動態資料來源如何注入到IOC容器中呢?看上文自定義的DynamicDataSource
構造方法,肯定需要兩個資料來源了,因此必須先注入兩個或者多個數據源到IOC容器中,如下:
/**
* @Bean:向IOC容器中注入一個Bean
* @ConfigurationProperties:使得配置檔案中以spring.datasource為字首的屬性對映到Bean的屬性中
*/
@ConfigurationProperties(prefix = "spring.datasource")
@Bean("dataSource")
public DataSource dataSource(){
return new DruidDataSource();
}
/**
* 向IOC容器中注入另外一個數據源
* 全域性配置檔案中字首是spring.datasource.his
*/
@Bean(name = SwitchSource.DEFAULT_NAME)
@ConfigurationProperties(prefix = "spring.datasource.his")
public DataSource hisDataSource() {
return DataSourceBuilder.create().build();
}
以上構建的兩個資料來源,一個是預設的資料來源,一個是需要切換到的資料來源(
targetDataSources
),這樣就組成了動態資料來源了。資料來源的一些資訊,比如url
,username
需要自己在全域性配置檔案中根據指定的字首配置即可,程式碼不再貼出。
相關推薦
Spring Boot與多資料來源那點事兒~
持續原創輸出,點選上方藍字關注我 目錄 前言寫這篇文章的目的什麼是多資料來源?何時用到多資料來源?整合單一的資料來源整合Mybatis多資料來源如何整合? 什麼是動態資料來源?資料來源切換如何保證執行緒隔離?如何構造一個動態資料來源?定義一個註解如何與Mybatis整合?演示 總結 前言 大約在19年
spring boot 配置多資料來源
1.application.yml配置 server: port: 8088 spring: http: multipart: max-file-size: 50Mb max-request-size: 50Mb enabl
Spring Boot +Mybatis 多資料來源的配置和使用
1、在application.properties中新增資料庫連線配置 mybatis.type-aliases-package=com.yc.edusys.bean mybatis.mapper-locations=cla
Spring Boot Jpa多資料來源配置
前言隨著業務量發展,我們通常會進行資料庫拆分或是引入其他資料庫,從而我們需要配置多個數據源,如:user一個庫,business一個庫。那麼接下來我們就要考慮怎麼去在spring boot中實現多個數據源的配置。 ××× 實現建表首先是建表語句,我們要建立兩個資料庫,並各庫內新建一張表user表mysql
Spring Boot使用多資料來源配置JdbcTemplate.md
多資料來源配置 建立一個Spring配置類,定義兩個DataSource用來讀取application.properties中的不同配置。如下例子中,主資料來源配置為spring.datasource.primary開頭的配置,第二資
spring-boot-mybatis-多資料來源
sql 語句 DROP TABLE IF EXISTS `users`; CREATE TABLE `users` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主鍵id', `userName` varchar(32) DEFAULT NUL
Spring Boot + Mybatis 多資料來源配置實現讀寫分離
本文來自網易雲社群作者:王超應用場景:專案中有一些報表統計與查詢功能,對資料實時性要求不高,因此考慮對報表的統計與查詢去操作slave db,減少對master的壓力。根據網上多份資料測試發現總是使用master資料來源,無法切換到slave,經過多次除錯修改現已完美通過,現
Spring boot(七):Spring boot+ mybatis 多資料來源最簡解決方案
多資料來源一般解決哪些問題?主從模式或者業務比較複雜需要連線不同的分庫來支援業務。 直接上程式碼。 配置檔案 pom包依賴,該依賴的依賴。主要是資料庫這邊的配置: mybatis.config-locations=classpath:mybatis/mybati
Spring Boot配置多資料來源並實現Druid自動切換
SpringBoot多資料來源切換,先上配置檔案: 1.pom: <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0"
Spring Boot+Mybatis多資料來源讀寫分離
兩天搭建了一個SpringBoot,Mybatis多資料來源讀寫分離,redis實現session共享的例子。記錄一下~~~ 前提條件:Spring Boot,Mybatis 單資料庫能正常執行 一、Spring Boot整合Mybatis實現讀寫分離(後續會做Mysql
spring boot+mybatis 多資料來源報錯 expected single matching bean but found
spring boot 多資料來源配置 No qualifying bean of type [javax.sql.DataSource] is defined: expected single matching bean but found 2: paymen
Objective-C與Runtime的那點事兒訊息機制
最近在找工作,Objective-C中的Runtime是經常被問到的一個問題,幾乎是面試大公司必問的一個問題。當然還有一些其他問題也幾乎必問,例 如:RunLoop,Block,記憶體管理等。其他的問題如果有機會我會在其他文章中介紹。本篇文章主要介紹RunTime。 R
Spring Boot + mybatic + 多資料來源 自動切換
寫在開篇 Spring boot 最大的特點就是簡化開發(去xml配置)。故這篇所實現的也是配置些註解,並無大量的xml配置。 外甥打燈籠--照舊上程式碼 依賴:其他依賴省去。 <dependency> <groupId>com.za
spring boot(多資料來源)+spring batch 解決異常: java.lang.IllegalStateException
專案整合說明: spring boot(配置多資料來源) spring batch 資料來源配置檔案: @Configuration public class DataSourceConfig { @Primary @Bean(n
unity與ios互動那點事兒
unity與ios互動 Unity中呼叫Objective-C/C++方法 1、[DllImport("__Internal")] 特性 在Unity新建專案中,Progect --> Create --> C# Script ,然後使用 [DllIm
【十九】Spring Boot 之多資料來源和分散式事務(JTA、Atomikos、Druid、Mybatis)
1.事務開始 2.A資料來源insert 3.B資料來源insert 4.報錯 5.事務回滾(A、B資料來源都回滾) 專案目錄 pom.xml <?xml version="1.0" encoding="UTF-8"?> <projec
spring boot + hibernate 多資料來源(註解方式)
一)spring boot + hibernate 多資料來源(XML) import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.En
【十七】Spring Boot 之 多資料來源(擴充套件AbstractRoutingDataSource類,實現動態資料來源。使用AOP註解動態切換資料來源)
在一個專案中操作多個不同的資料來源時,需要用到多資料來源的配置。 pom.xml <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0
spring boot mybatis 多資料來源異常
摘要:在做多資料來源專案改造的時候,通過spring boot啟動applicationContext.xml配置檔案,老提示如下異常資訊: [2016-11-30 19:49:29 WARN o.s.c.a.AnnotationConfigApplicationCont
spring boot mybatis多資料來源,後端進行資料庫讀寫分離
2.後端的讀寫分離很簡單,主要是配置資料庫的難題。為啥簡單?只要資料庫配置好,你想哪個資料庫寫,哪個資料庫讀,自己安排 1)看下application.properties #spring.datasource.name= test #spring.datasource.