日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
SpringBoot整合多數(shù)據(jù)源,這才叫優(yōu)雅

其實(shí)在配置數(shù)據(jù)源的時(shí)候就已經(jīng)很明確這個(gè)定義了,如以下代碼:

創(chuàng)新互聯(lián)自2013年起,是專業(yè)互聯(lián)網(wǎng)技術(shù)服務(wù)公司,擁有項(xiàng)目成都做網(wǎng)站、成都網(wǎng)站制作網(wǎng)站策劃,項(xiàng)目實(shí)施與項(xiàng)目整合能力。我們以讓每一個(gè)夢想脫穎而出為使命,1280元新民做網(wǎng)站,已為上家服務(wù),為新民各地企業(yè)和個(gè)人服務(wù),聯(lián)系電話:18982081108

@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這三個(gè)屬性已經(jīng)唯一確定了一個(gè)數(shù)據(jù)庫了,DataSource則是依賴這三個(gè)創(chuàng)建出來的。則多數(shù)據(jù)源即是配置多個(gè)DataSource(暫且這么理解)。

”何時(shí)用到多數(shù)據(jù)源?

正如前言介紹到的一個(gè)場景,相信大多數(shù)做過醫(yī)療系統(tǒng)的都會和HIS打交道,為了簡化護(hù)士以及醫(yī)生的操作流程,必須要將必要的信息從HIS系統(tǒng)對接過來,據(jù)我了解的大致有兩種方案如下:

HIS提供視圖,比如醫(yī)護(hù)視圖、患者視圖等,而此時(shí)其他系統(tǒng)只需要定時(shí)的從HIS視圖中讀取數(shù)據(jù)同步到自己數(shù)據(jù)庫中即可。

  • HIS提供接口,無論是webService還是HTTP形式都是可行的,此時(shí)其他系統(tǒng)只需要按照要求調(diào)接口即可。
  • 很明顯第一種方案涉及到了至少兩個(gè)數(shù)據(jù)庫了,一個(gè)是HIS數(shù)據(jù)庫,一個(gè)自己系統(tǒng)的數(shù)據(jù)庫,在單一應(yīng)用中必然需要用到多數(shù)據(jù)源的切換才能達(dá)到目的。

當(dāng)然多數(shù)據(jù)源的使用場景還是有很多的,以上只是簡單的一個(gè)場景。

整合單一的數(shù)據(jù)源

本文使用阿里的數(shù)據(jù)庫連接池druid,添加依賴如下:



com.alibaba
druid-spring-boot-starter
1.1.9

阿里的數(shù)據(jù)庫連接池非常強(qiáng)大,比如數(shù)據(jù)監(jiān)控、數(shù)據(jù)庫加密等等內(nèi)容,本文僅僅演示與Spring Boot整合的過程,一些其他的功能后續(xù)可以自己研究添加。

Druid連接池的starter的自動配置類是DruidDataSourceAutoConfigure,類上標(biāo)注如下一行注解:

@EnableConfigurationProperties({DruidStatProperties.class, DataSourceProperties.class})

@EnableConfigurationProperties這個(gè)注解使得配置文件中的配置生效并且映射到指定類的屬性。

”DruidStatProperties中指定的前綴是spring.datasource.druid,這個(gè)配置主要是用來設(shè)置連接池的一些參數(shù)。

DataSourceProperties中指定的前綴是spring.datasource,這個(gè)主要是用來設(shè)置數(shù)據(jù)庫的url、username、password等信息。

因此我們只需要在全局配置文件中指定數(shù)據(jù)庫的一些配置以及連接池的一些配置信息即可,前綴分別是spring.datasource.druid、spring.datasource,以下是個(gè)人隨便配置的(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
#連接池最大使用連接數(shù)量
spring.datasource.druid.max-active=20
#連接池最小空閑
spring.datasource.druid.min-idle=0
#獲取連接最大等待時(shí)間
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
#配置間隔多久才進(jìn)行一次檢測,檢測需要關(guān)閉的空閑連接,單位是毫秒
spring.datasource.druid.time-between-eviction-runs-millis=60000
#置一個(gè)連接在池中最小生存的時(shí)間,單位是毫秒
spring.datasource.druid.min-evictable-idle-time-millis=25200000
#spring.datasource.druid.max-evictable-idle-time-millis=
#打開removeAbandoned功能,多少時(shí)間內(nèi)必須關(guān)閉連接
spring.datasource.druid.removeAbandoned=true
#1800秒,也就是30分鐘
spring.datasource.druid.remove-abandoned-timeout=1800
#
spring.datasource.druid.log-abandoned=true
spring.datasource.druid.filters=mergeStat

在全局配置文件application.properties文件中配置以上的信息即可注入一個(gè)數(shù)據(jù)源到Spring Boot中。其實(shí)這僅僅是一種方式,下面介紹另外一種方式。

”在自動配置類中DruidDataSourceAutoConfigure中有如下一段代碼:

  @Bean(initMethod = "init")
@ConditionalOnMissingBean
public DataSource dataSource() {
LOGGER.info("Init DruidDataSource");
return new DruidDataSourceWrapper();
}

@ConditionalOnMissingBean和@Bean這兩個(gè)注解的結(jié)合,意味著我們可以覆蓋,只需要提前在IOC中注入一個(gè)DataSource類型的Bean即可。

”因此我們在自定義的配置類中定義如下配置即可:

/**
* @Bean:向IOC容器中注入一個(gè)Bean
* @ConfigurationProperties:使得配置文件中以spring.datasource為前綴的屬性映射到Bean的屬性中
* @return
*/
@ConfigurationProperties(prefix = "spring.datasource")
@Bean
public DataSource dataSource(){
//做一些其他的自定義配置,比如密碼加密等......
return new DruidDataSource();
}

以上介紹了兩種數(shù)據(jù)源的配置方式,第一種比較簡單,第二種適合擴(kuò)展,按需選擇。

整合Mybatis

Spring Boot 整合Mybatis其實(shí)很簡單,簡單的幾步就搞定,首先添加依賴:


org.mybatis.spring.boot
mybatis-spring-boot-starter
2.0.0

第二步找到自動配置類MybatisAutoConfiguration,有如下一行代碼:

@EnableConfigurationProperties(MybatisProperties.class)

老套路了,全局配置文件中配置前綴為mybatis的配置將會映射到該類中的屬性。

”可配置的東西很多,比如XML文件的位置、類型處理器等等,如下簡單的配置:

mybatis.type-handlers-package=com.demo.typehandler
mybatis.configuration.map-underscore-to-camel-case=true

如果需要通過包掃描的方式注入Mapper,則需要在配置類上加入一個(gè)注解:@MapperScan,其中的value屬性指定需要掃描的包。

直接在全局配置文件配置各種屬性是一種比較簡單的方式,其實(shí)的任何組件的整合都有不少于兩種的配置方式,下面來介紹下配置類如何配置。

”MybatisAutoConfiguration自動配置類有如下一斷代碼:

@Bean
@ConditionalOnMissingBean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {}

@ConditionalOnMissingBean和@Bean真是老搭檔了,意味著我們又可以覆蓋,只需要在IOC容器中注入SqlSessionFactory(Mybatis六劍客之一生產(chǎn)者)。

在自定義配置類中注入即可,如下:

 /**
* 注入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();
// 自動將數(shù)據(jù)庫中的下劃線轉(zhuǎn)換為駝峰格式
configuration.setMapUnderscoreToCamelCase(true);
configuration.setDefaultFetchSize(100);
configuration.setDefaultStatementTimeout(30);
sqlSessionFactoryBean.setConfiguration(configuration);
return sqlSessionFactoryBean.getObject();
}

以上介紹了配置Mybatis的兩種方式,其實(shí)在大多數(shù)場景中使用第一種已經(jīng)夠用了,至于為什么介紹第二種呢?當(dāng)然是為了多數(shù)據(jù)源的整合而做準(zhǔn)備了。

”在MybatisAutoConfiguration中有一行很重要的代碼,如下:

@ConditionalOnSingleCandidate(DataSource.class)

@ConditionalOnSingleCandidate這個(gè)注解的意思是當(dāng)IOC容器中只有一個(gè)候選Bean的實(shí)例才會生效。

這行代碼標(biāo)注在Mybatis的自動配置類中有何含義呢?下面介紹,哈哈哈~

多數(shù)據(jù)源如何整合?

上文留下的問題:為什么的Mybatis自動配置上標(biāo)注如下一行代碼:

@ConditionalOnSingleCandidate(DataSource.class)

以上這行代碼的言外之意:當(dāng)IOC容器中只有一個(gè)數(shù)據(jù)源DataSource,這個(gè)自動配置類才會生效。

”哦?照這樣搞,多數(shù)據(jù)源是不能用Mybatis嗎?

可能大家會有一個(gè)誤解,認(rèn)為多數(shù)據(jù)源就是多個(gè)的DataSource并存的,當(dāng)然這樣說也不是不正確。

多數(shù)據(jù)源的情況下并不是多個(gè)數(shù)據(jù)源并存的,Spring提供了AbstractRoutingDataSource這樣一個(gè)抽象類,使得能夠在多數(shù)據(jù)源的情況下任意切換,相當(dāng)于一個(gè)動態(tài)路由的作用,作者稱之為動態(tài)數(shù)據(jù)源。因此Mybatis只需要配置這個(gè)動態(tài)數(shù)據(jù)源即可。

什么是動態(tài)數(shù)據(jù)源?

動態(tài)數(shù)據(jù)源簡單的說就是能夠自由切換的數(shù)據(jù)源,類似于一個(gè)動態(tài)路由的感覺,Spring 提供了一個(gè)抽象類AbstractRoutingDataSource,這個(gè)抽象類中喲一個(gè)屬性,如下:

private Map targetDataSources;

targetDataSources是一個(gè)Map結(jié)構(gòu),所有需要切換的數(shù)據(jù)源都存放在其中,根據(jù)指定的KEY進(jìn)行切換。當(dāng)然還有一個(gè)默認(rèn)的數(shù)據(jù)源。

AbstractRoutingDataSource這個(gè)抽象類中有一個(gè)抽象方法需要子類實(shí)現(xiàn),如下:

protected abstract Object determineCurrentLookupKey();

determineCurrentLookupKey()這個(gè)方法的返回值決定了需要切換的數(shù)據(jù)源的KEY,就是根據(jù)這個(gè)KEY從targetDataSources取值(數(shù)據(jù)源)。

數(shù)據(jù)源切換如何保證線程隔離?

數(shù)據(jù)源屬于一個(gè)公共的資源,在多線程的情況下如何保證線程隔離呢?不能我這邊切換了影響其他線程的執(zhí)行。

說到線程隔離,自然會想到ThreadLocal了,將切換數(shù)據(jù)源的KEY(用于從targetDataSources中取值)存儲在ThreadLocal中,執(zhí)行結(jié)束之后清除即可。

”單獨(dú)封裝了一個(gè)DataSourceHolder,內(nèi)部使用ThreadLocal隔離線程,代碼如下:

/**
* 使用ThreadLocal存儲切換數(shù)據(jù)源后的KEY
*/
public class DataSourceHolder {

//線程 本地環(huán)境
private static final ThreadLocal dataSources = new InheritableThreadLocal();

//設(shè)置數(shù)據(jù)源
public static void setDataSource(String datasource) {
dataSources.set(datasource);
}

//獲取數(shù)據(jù)源
public static String getDataSource() {
return dataSources.get();
}

//清除數(shù)據(jù)源
public static void clearDataSource() {
dataSources.remove();
}
}

如何構(gòu)造一個(gè)動態(tài)數(shù)據(jù)源?

上文說過只需繼承一個(gè)抽象類AbstractRoutingDataSource,重寫其中的一個(gè)方法determineCurrentLookupKey()即可。代碼如下:

/**
* 動態(tài)數(shù)據(jù)源,繼承AbstractRoutingDataSource
*/
public class DynamicDataSource extends AbstractRoutingDataSource {

/**
* 返回需要使用的數(shù)據(jù)源的key,將會按照這個(gè)KEY從Map獲取對應(yīng)的數(shù)據(jù)源(切換)
* @return
*/
@Override
protected Object determineCurrentLookupKey() {
//從ThreadLocal中取出KEY
return DataSourceHolder.getDataSource();
}

/**
* 構(gòu)造方法填充Map,構(gòu)建多數(shù)據(jù)源
*/
public DynamicDataSource(DataSource defaultTargetDataSource, Map targetDataSources) {
//默認(rèn)的數(shù)據(jù)源,可以作為主數(shù)據(jù)源
super.setDefaultTargetDataSource(defaultTargetDataSource);
//目標(biāo)數(shù)據(jù)源
super.setTargetDataSources(targetDataSources);
//執(zhí)行afterPropertiesSet方法,完成屬性的設(shè)置
super.afterPropertiesSet();
}
}

上述代碼很簡單,分析如下:

  • 一個(gè)多參的構(gòu)造方法,指定了默認(rèn)的數(shù)據(jù)源和目標(biāo)數(shù)據(jù)源。
  • 重寫determineCurrentLookupKey()方法,返回?cái)?shù)據(jù)源對應(yīng)的KEY,這里是直接從ThreadLocal中取值,就是上文封裝的DataSourceHolder。

定義一個(gè)注解

為了操作方便且低耦合,不能每次需要切換的數(shù)據(jù)源的時(shí)候都要手動調(diào)一下接口吧,可以定義一個(gè)切換數(shù)據(jù)源的注解,如下:

/**
* 切換數(shù)據(jù)源的注解
*/
@Target(value = ElementType.METHOD)
@Retention(value = RetentionPolicy.RUNTIME)
@Documented
public @interface SwitchSource {

/**
* 默認(rèn)切換的數(shù)據(jù)源KEY
*/
String DEFAULT_NAME = "hisDataSource";

/**
* 需要切換到數(shù)據(jù)的KEY
*/
String value() default DEFAULT_NAME;
}

注解中只有一個(gè)value屬性,指定了需要切換數(shù)據(jù)源的KEY。

有注解還不行,當(dāng)然還要有切面,代碼如下:

@Aspect
//優(yōu)先級要設(shè)置在事務(wù)切面執(zhí)行之前
@Order(1)
@Component
@Slf4j
public class DataSourceAspect {


@Pointcut("@annotation(SwitchSource)")
public void pointcut() {
}

/**
* 在方法執(zhí)行之前切換到指定的數(shù)據(jù)源
* @param joinPoint
*/
@Before(value = "pointcut()")
public void beforeOpt(JoinPoint joinPoint) {
/*因?yàn)槭菍ψ⒔膺M(jìn)行切面,所以這邊無需做過多判定,直接獲取注解的值,進(jìn)行環(huán)繞,將數(shù)據(jù)源設(shè)置成遠(yuǎn)方,然后結(jié)束后,清楚當(dāng)前線程數(shù)據(jù)源*/
Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
SwitchSource switchSource = method.getAnnotation(SwitchSource.class);
log.info("[Switch DataSource]:" + switchSource.value());
DataSourceHolder.setDataSource(switchSource.value());
}

/**
* 方法執(zhí)行之后清除掉ThreadLocal中存儲的KEY,這樣動態(tài)數(shù)據(jù)源會使用默認(rèn)的數(shù)據(jù)源
*/
@After(value = "pointcut()")
public void afterOpt() {
DataSourceHolder.clearDataSource();
log.info("[Switch Default DataSource]");
}
}

這個(gè)ASPECT很容易理解,beforeOpt()在方法之前執(zhí)行,取值@SwitchSource中value屬性設(shè)置到ThreadLocal中;afterOpt()方法在方法執(zhí)行之后執(zhí)行,清除掉ThreadLocal中的KEY,保證了如果不切換數(shù)據(jù)源,則用默認(rèn)的數(shù)據(jù)源。

如何與Mybatis整合?

單一數(shù)據(jù)源與Mybatis整合上文已經(jīng)詳細(xì)講解了,數(shù)據(jù)源DataSource作為參數(shù)構(gòu)建了SqlSessionFactory,同樣的思想,只需要把這個(gè)數(shù)據(jù)源換成動態(tài)數(shù)據(jù)源即可。注入的代碼如下:

/**
* 創(chuàng)建動態(tài)數(shù)據(jù)源的SqlSessionFactory,傳入的是動態(tài)數(shù)據(jù)源
* @Primary這個(gè)注解很重要,如果項(xiàng)目中存在多個(gè)SqlSessionFactory,這個(gè)注解一定要加上
*/
@Primary
@Bean("sqlSessionFactory2")
public SqlSessionFactory sqlSessionFactoryBean(DynamicDataSource dynamicDataSource) throws Exception {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dynamicDataSource);
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整合很簡單,只需要把數(shù)據(jù)源替換成自定義的動態(tài)數(shù)據(jù)源DynamicDataSource。

”那么動態(tài)數(shù)據(jù)源如何注入到IOC容器中呢?看上文自定義的DynamicDataSource構(gòu)造方法,肯定需要兩個(gè)數(shù)據(jù)源了,因此必須先注入兩個(gè)或者多個(gè)數(shù)據(jù)源到IOC容器中,如下:

 /**
* @Bean:向IOC容器中注入一個(gè)Bean
* @ConfigurationProperties:使得配置文件中以spring.datasource為前綴的屬性映射到Bean的屬性中
*/
@ConfigurationProperties(prefix = "spring.datasource")
@Bean("dataSource")
public DataSource dataSource(){
return new DruidDataSource();
}

/**
* 向IOC容器中注入另外一個(gè)數(shù)據(jù)源
* 全局配置文件中前綴是spring.datasource.his
*/
@Bean(name = SwitchSource.DEFAULT_NAME)
@ConfigurationProperties(prefix = "spring.datasource.his")
public DataSource hisDataSource() {
return DataSourceBuilder.create().build();
}

以上構(gòu)建的兩個(gè)數(shù)據(jù)源,一個(gè)是默認(rèn)的數(shù)據(jù)源,一個(gè)是需要切換到的數(shù)據(jù)源(targetDataSources),這樣就組成了動態(tài)數(shù)據(jù)源了。數(shù)據(jù)源的一些信息,比如url,username需要自己在全局配置文件中根據(jù)指定的前綴配置即可,代碼不再貼出。

”動態(tài)數(shù)據(jù)源的注入代碼如下:

/**
* 創(chuàng)建動態(tài)數(shù)據(jù)源的SqlSessionFactory,傳入的是動態(tài)數(shù)據(jù)源
* @Primary這個(gè)注解很重要,如果項(xiàng)目中存在多個(gè)SqlSessionFactory,這個(gè)注解一定要加上
*/
@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();
}

這里還有一個(gè)問題:IOC中存在多個(gè)數(shù)據(jù)源了,那么事務(wù)管理器怎么辦呢?它也懵逼了,到底選擇哪個(gè)數(shù)據(jù)源呢?因此事務(wù)管理器肯定還是要重新配置的。

”事務(wù)管理器此時(shí)管理的數(shù)據(jù)源將是動態(tài)數(shù)據(jù)源DynamicDataSource,配置如下:

   /**
* 重寫事務(wù)管理器,管理動態(tài)數(shù)據(jù)源
*/
@Primary
@Bean(value = "transactionManager2")
public PlatformTransactionManager annotationDrivenTransactionManager(DynamicDataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}

至此,Mybatis與多數(shù)據(jù)源的整合就完成了。

演示

使用也是很簡單,在需要切換數(shù)據(jù)源的方法上方標(biāo)注@SwitchSource切換到指定的數(shù)據(jù)源即可,如下:

   //不開啟事務(wù)
@Transactional(propagation = Propagation.NOT_SUPPORTED)
//切換到HIS的數(shù)據(jù)源
@SwitchSource
@Override
public List list() {
return hisDeptInfoMapper.listDept();
}

這樣只要執(zhí)行到這方法將會切換到HIS的數(shù)據(jù)源,方法執(zhí)行結(jié)束之后將會清除,執(zhí)行默認(rèn)的數(shù)據(jù)源。

總結(jié)

本篇文章講了Spring Boot與單數(shù)據(jù)源、Mybatis、多數(shù)據(jù)源之間的整合,希望這篇文章能夠幫助讀者理解多數(shù)據(jù)源的整合,雖說用的不多,但是在有些領(lǐng)域仍然是比較重要的。


當(dāng)前標(biāo)題:SpringBoot整合多數(shù)據(jù)源,這才叫優(yōu)雅
分享路徑:http://m.5511xx.com/article/cdhghcp.html