最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

spring實(shí)現(xiàn)動(dòng)態(tài)切換、添加數(shù)據(jù)源及源碼分析

 更新時(shí)間:2018年09月12日 14:51:29   作者:殷天文  
這篇文章主要給大家介紹了關(guān)于spring實(shí)現(xiàn)動(dòng)態(tài)切換、添加數(shù)據(jù)源及源碼分析的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧

前言

對(duì)于數(shù)據(jù)量在1千萬(wàn),單個(gè)mysql數(shù)據(jù)庫(kù)就可以支持,但是如果數(shù)據(jù)量大于這個(gè)數(shù)的時(shí)候,例如1億,那么查詢的性能就會(huì)很低。此時(shí)需要對(duì)數(shù)據(jù)庫(kù)做水平切分,常見的做法是按照用戶的賬號(hào)進(jìn)行hash,然后選擇對(duì)應(yīng)的數(shù)據(jù)庫(kù)。

最近公司項(xiàng)目需求,由于要兼容老系統(tǒng)的數(shù)據(jù)庫(kù)結(jié)構(gòu),需要搭建一個(gè) 可以動(dòng)態(tài)切換、添加數(shù)據(jù)源的后端服務(wù)。

參考了過(guò)去的項(xiàng)目,通過(guò)配置多個(gè)SqlSessionFactory 來(lái)實(shí)現(xiàn)多數(shù)據(jù)源,這么做的話,未免過(guò)于笨重,而且無(wú)法實(shí)現(xiàn)動(dòng)態(tài)添加數(shù)據(jù)源這個(gè)需求

通過(guò) spring AbstractRoutingDataSource 為我們抽象了一個(gè) DynamicDataSource 解決這一問(wèn)題

簡(jiǎn)單分析下 AbstractRoutingDataSource 的源碼

targetDataSources 就是我們的多個(gè)數(shù)據(jù)源,在初始化的時(shí)候會(huì)調(diào)用afterPropertiesSet(),去解析我們的數(shù)據(jù)源 然后 put 到 resolvedDataSources


實(shí)現(xiàn)了 DataSource 的 getConnection(); 我們看看 determineTargetDataSource(); 做了什么


通過(guò)下面的 determineCurrentLookupKey();(這個(gè)方法需要我們實(shí)現(xiàn)) 返回一個(gè)key,然后從 resolvedDataSources (其實(shí)也就是 targetDataSources) 中 get 一個(gè)數(shù)據(jù)源,實(shí)現(xiàn)了每次調(diào)用 getConnection(); 打開連接 切換數(shù)據(jù)源,如果想動(dòng)態(tài)添加的話 只需要重新 set targetDataSources 再調(diào)用 afterPropertiesSet() 即可

Talk is cheap. Show me the code

我使用的springboot版本為 1.5.x,下面是核心代碼

完整代碼:https://gitee.com/yintianwen7/spring-dynamic-datasource (本地下載)

/**
 * 多數(shù)據(jù)源配置
 * 
 * @author Taven
 *
 */
@Configuration
@MapperScan("com.gitee.taven.mapper")
public class DataSourceConfigurer {

 /**
  * DataSource 自動(dòng)配置并注冊(cè)
  *
  * @return data source
  */
 @Bean("db0")
 @Primary
 @ConfigurationProperties(prefix = "datasource.db0")
 public DataSource dataSource0() {
  return DruidDataSourceBuilder.create().build();
 }

 /**
  * DataSource 自動(dòng)配置并注冊(cè)
  *
  * @return data source
  */
 @Bean("db1")
 @ConfigurationProperties(prefix = "datasource.db1")
 public DataSource dataSource1() {
  return DruidDataSourceBuilder.create().build();
 }

 /**
  * 注冊(cè)動(dòng)態(tài)數(shù)據(jù)源
  * 
  * @return
  */
 @Bean("dynamicDataSource")
 public DataSource dynamicDataSource() {
  DynamicRoutingDataSource dynamicRoutingDataSource = new DynamicRoutingDataSource();
  Map<Object, Object> dataSourceMap = new HashMap<>();
  dataSourceMap.put("dynamic_db0", dataSource0());
  dataSourceMap.put("dynamic_db1", dataSource1());
  dynamicRoutingDataSource.setDefaultTargetDataSource(dataSource0());// 設(shè)置默認(rèn)數(shù)據(jù)源
  dynamicRoutingDataSource.setTargetDataSources(dataSourceMap);
  return dynamicRoutingDataSource;
 }

 /**
  * Sql session factory bean.
  * Here to config datasource for SqlSessionFactory
  * <p>
  * You need to add @{@code @ConfigurationProperties(prefix = "mybatis")}, if you are using *.xml file,
  * the {@code 'mybatis.type-aliases-package'} and {@code 'mybatis.mapper-locations'} should be set in
  * {@code 'application.properties'} file, or there will appear invalid bond statement exception
  *
  * @return the sql session factory bean
  */
 @Bean
 @ConfigurationProperties(prefix = "mybatis")
 public SqlSessionFactoryBean sqlSessionFactoryBean() {
  SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
  // 必須將動(dòng)態(tài)數(shù)據(jù)源添加到 sqlSessionFactoryBean
  sqlSessionFactoryBean.setDataSource(dynamicDataSource());
  return sqlSessionFactoryBean;
 }

 /**
  * 事務(wù)管理器
  *
  * @return the platform transaction manager
  */
 @Bean
 public PlatformTransactionManager transactionManager() {
  return new DataSourceTransactionManager(dynamicDataSource());
 }
}

通過(guò) ThreadLocal 獲取線程安全的數(shù)據(jù)源 key

package com.gitee.taven.config;

public class DynamicDataSourceContextHolder {

 private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>() {
  @Override
  protected String initialValue() {
   return "dynamic_db0";
  }
 };

 /**
  * To switch DataSource
  *
  * @param key the key
  */
 public static void setDataSourceKey(String key) {
  contextHolder.set(key);
 }

 /**
  * Get current DataSource
  *
  * @return data source key
  */
 public static String getDataSourceKey() {
  return contextHolder.get();
 }

 /**
  * To set DataSource as default
  */
 public static void clearDataSourceKey() {
  contextHolder.remove();
 }
}

動(dòng)態(tài) 添加、切換數(shù)據(jù)源

/**
 * 動(dòng)態(tài)數(shù)據(jù)源
 * 
 * @author Taven
 *
 */
public class DynamicRoutingDataSource extends AbstractRoutingDataSource {

 private final Logger logger = LoggerFactory.getLogger(getClass());

 private static Map<Object, Object> targetDataSources = new HashMap<>();
 
 /**
  * 設(shè)置當(dāng)前數(shù)據(jù)源
  *
  * @return
  */
 @Override
 protected Object determineCurrentLookupKey() {
  logger.info("Current DataSource is [{}]", DynamicDataSourceContextHolder.getDataSourceKey());
  return DynamicDataSourceContextHolder.getDataSourceKey();
 }
 
 @Override
 public void setTargetDataSources(Map<Object, Object> targetDataSources) {
  super.setTargetDataSources(targetDataSources);
  DynamicRoutingDataSource.targetDataSources = targetDataSources;
 }
 
 /**
  * 是否存在當(dāng)前key的 DataSource
  * 
  * @param key
  * @return 存在返回 true, 不存在返回 false
  */
 public static boolean isExistDataSource(String key) {
  return targetDataSources.containsKey(key);
 }
 
 /**
  * 動(dòng)態(tài)增加數(shù)據(jù)源
  * 
  * @param map 數(shù)據(jù)源屬性
  * @return
  */
 public synchronized boolean addDataSource(Map<String, String> map) {
  try {
   Connection connection = null;
   // 排除連接不上的錯(cuò)誤
   try { 
    Class.forName(map.get(DruidDataSourceFactory.PROP_DRIVERCLASSNAME));
    connection = DriverManager.getConnection(
      map.get(DruidDataSourceFactory.PROP_URL), 
      map.get(DruidDataSourceFactory.PROP_USERNAME),
      map.get(DruidDataSourceFactory.PROP_PASSWORD));
    System.out.println(connection.isClosed());
   } catch (Exception e) {
    return false;
   } finally {
    if (connection != null && !connection.isClosed()) 
     connection.close();
   }
   String database = map.get("database");//獲取要添加的數(shù)據(jù)庫(kù)名
   if (StringUtils.isBlank(database)) return false;
   if (DynamicRoutingDataSource.isExistDataSource(database)) return true; 
   DruidDataSource druidDataSource = (DruidDataSource) DruidDataSourceFactory.createDataSource(map);
   druidDataSource.init();
   Map<Object, Object> targetMap = DynamicRoutingDataSource.targetDataSources;
   targetMap.put(database, druidDataSource);
   // 當(dāng)前 targetDataSources 與 父類 targetDataSources 為同一對(duì)象 所以不需要set
//   this.setTargetDataSources(targetMap);
   this.afterPropertiesSet();
   logger.info("dataSource {} has been added", database);
  } catch (Exception e) {
   logger.error(e.getMessage());
   return false;
  }
  return true;
 } 
}

可以通過(guò) AOP 或者 手動(dòng) DynamicDataSourceContextHolder.setDataSourceKey(String key) 切換數(shù)據(jù)源

需要注意的:當(dāng)我們開啟了事務(wù)之后,是無(wú)法在去切換數(shù)據(jù)源的

本文項(xiàng)目源碼:https://gitee.com/yintianwen7/spring-dynamic-datasource (本地下載)

參考文獻(xiàn):https://github.com/helloworlde/SpringBoot-DynamicDataSource

總結(jié)

以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問(wèn)大家可以留言交流,謝謝大家對(duì)腳本之家的支持。

相關(guān)文章

  • SpringBoot使用hutool-captcha實(shí)現(xiàn)驗(yàn)證碼生成與驗(yàn)證

    SpringBoot使用hutool-captcha實(shí)現(xiàn)驗(yàn)證碼生成與驗(yàn)證

    在springboot的登陸頁(yè)面中為了防止機(jī)器大規(guī)模注冊(cè),機(jī)器暴力破解數(shù)據(jù)密碼等危害,需要驗(yàn)證隨機(jī)生成的驗(yàn)證碼,本文主要介紹了SpringBoot使用hutool-captcha實(shí)現(xiàn)驗(yàn)證碼生成與驗(yàn)證,感興趣的可以了解一下
    2023-12-12
  • SpringBoot中使用SpringSecurity進(jìn)行權(quán)限控制的示例代碼

    SpringBoot中使用SpringSecurity進(jìn)行權(quán)限控制的示例代碼

    本文將詳細(xì)介紹如何在Spring Boot應(yīng)用程序中使用Spring Security進(jìn)行權(quán)限控制,我們將探討Spring Security的基本概念,以及如何使用Spring Security實(shí)現(xiàn)認(rèn)證和授權(quán),需要的朋友可以參考下
    2024-02-02
  • SpringBoot多環(huán)境日志配置方式

    SpringBoot多環(huán)境日志配置方式

    SpringBoot?默認(rèn)使用LogBack日志系統(tǒng),默認(rèn)情況下,SpringBoot項(xiàng)目的日志只會(huì)在控制臺(tái)輸入,本文給大家介紹SpringBoot多環(huán)境日志配置方式,需要的朋友可以參考下
    2024-08-08
  • Java中的CopyOnWriteArrayList你了解嗎

    Java中的CopyOnWriteArrayList你了解嗎

    CopyOnWriteArrayList是Java集合框架中的一種線程安全的List實(shí)現(xiàn),這篇文章主要來(lái)和大家聊聊CopyOnWriteArrayList的簡(jiǎn)單使用,需要的可以參考一下
    2023-06-06
  • java 對(duì)象參數(shù)去空格方式代碼實(shí)例

    java 對(duì)象參數(shù)去空格方式代碼實(shí)例

    這篇文章主要介紹了java 對(duì)象參數(shù)去空格方式代碼實(shí)例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-10-10
  • 100-200之間所有素?cái)?shù)求和程序代碼(二個(gè)版本)

    100-200之間所有素?cái)?shù)求和程序代碼(二個(gè)版本)

    寫一個(gè)求100-200之間素?cái)?shù),并求和的程序,大家參考使用吧
    2013-11-11
  • Springcloud Config支持本地配置文件的方法示例

    Springcloud Config支持本地配置文件的方法示例

    這篇文章主要介紹了Springcloud Config支持本地配置文件的方法示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-02-02
  • java線程池使用場(chǎng)景及一些建議

    java線程池使用場(chǎng)景及一些建議

    本文主要介紹了java線程池使用場(chǎng)景及一些建議,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-06-06
  • Java中數(shù)據(jù)庫(kù)常用的兩把鎖之樂(lè)觀鎖和悲觀鎖

    Java中數(shù)據(jù)庫(kù)常用的兩把鎖之樂(lè)觀鎖和悲觀鎖

    這篇文章主要介紹了數(shù)據(jù)庫(kù)常用的兩把鎖之樂(lè)觀鎖和悲觀鎖,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-07-07
  • Java判斷用戶名和密碼是否符合要求過(guò)程詳解

    Java判斷用戶名和密碼是否符合要求過(guò)程詳解

    這篇文章主要介紹了Java判斷用戶名和密碼過(guò)程詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-02-02

最新評(píng)論

华池县| 达州市| 冷水江市| 措勤县| 涞源县| 蓬莱市| 高密市| 离岛区| 南陵县| 清远市| 新田县| 轮台县| 高淳县| 民权县| 都江堰市| 西宁市| 屏东县| 丰县| 滁州市| 枣庄市| 辰溪县| 肃南| 宁安市| 衡南县| 西昌市| 普定县| 郑州市| 济南市| 永仁县| 乌审旗| 崇义县| 烟台市| 宕昌县| 南川市| 咸丰县| 张家界市| 永清县| 宁德市| 唐河县| 鸡西市| 义乌市|