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

通過Spring Boot配置動態(tài)數(shù)據(jù)源訪問多個數(shù)據(jù)庫的實現(xiàn)代碼

 更新時間:2018年03月01日 08:35:40   作者:Elon.Yang  
這篇文章主要介紹了通過Spring Boot配置動態(tài)數(shù)據(jù)源訪問多個數(shù)據(jù)庫的實現(xiàn)代碼,需要的朋友可以參考下

之前寫過一篇博客《Spring+Mybatis+Mysql搭建分布式數(shù)據(jù)庫訪問框架》描述如何通過Spring+Mybatis配置動態(tài)數(shù)據(jù)源訪問多個數(shù)據(jù)庫。但是之前的方案有一些限制(原博客中也描述了):只適用于數(shù)據(jù)庫數(shù)量不多且固定的情況。針對數(shù)據(jù)庫動態(tài)增加的情況無能為力。

下面講的方案能支持?jǐn)?shù)據(jù)庫動態(tài)增刪,數(shù)量不限。

數(shù)據(jù)庫環(huán)境準(zhǔn)備

下面一Mysql為例,先在本地建3個數(shù)據(jù)庫用于測試。需要說明的是本方案不限數(shù)據(jù)庫數(shù)量,支持不同的數(shù)據(jù)庫部署在不同的服務(wù)器上。如圖所示db_project_001、db_project_002、db_project_003。

搭建Java后臺微服務(wù)項目

創(chuàng)建一個Spring Boot的maven項目:

config:數(shù)據(jù)源配置管理類。

datasource:自己實現(xiàn)的數(shù)據(jù)源管理邏輯。

dbmgr:管理了項目編碼與數(shù)據(jù)庫IP、名稱的映射關(guān)系(實際項目中這部分?jǐn)?shù)據(jù)保存在redis緩存中,可動態(tài)增刪)。

mapper:數(shù)據(jù)庫訪問接口。

model:映射模型。

rest:微服務(wù)對外發(fā)布的restful接口,這里用來測試。

application.yml:配置了數(shù)據(jù)庫的JDBC參數(shù)。

詳細(xì)的代碼實現(xiàn)

1. 添加數(shù)據(jù)源配置

package com.elon.dds.config;
import javax.sql.DataSource;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.elon.dds.datasource.DynamicDataSource;
/**
 * 數(shù)據(jù)源配置管理。
 *
 * @author elon
 * @version 2018年2月26日
 */
@Configuration
@MapperScan(basePackages="com.elon.dds.mapper", value="sqlSessionFactory")
public class DataSourceConfig {
 /**
 * 根據(jù)配置參數(shù)創(chuàng)建數(shù)據(jù)源。使用派生的子類。
 *
 * @return 數(shù)據(jù)源
 */
 @Bean(name="dataSource")
 @ConfigurationProperties(prefix="spring.datasource")
 public DataSource getDataSource() {
 DataSourceBuilder builder = DataSourceBuilder.create();
 builder.type(DynamicDataSource.class);
 return builder.build();
 }
 /**
 * 創(chuàng)建會話工廠。
 *
 * @param dataSource 數(shù)據(jù)源
 * @return 會話工廠
 */
 @Bean(name="sqlSessionFactory")
 public SqlSessionFactory getSqlSessionFactory(@Qualifier("dataSource") DataSource dataSource) {
 SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
 bean.setDataSource(dataSource);
 try {
  return bean.getObject();
 } catch (Exception e) {
  e.printStackTrace();
  return null;
 }
 }
}

2.定義動態(tài)數(shù)據(jù)源

1)  首先增加一個數(shù)據(jù)庫標(biāo)識類,用于區(qū)分不同的數(shù)據(jù)庫訪問。

由于我們?yōu)椴煌膒roject創(chuàng)建了單獨(dú)的數(shù)據(jù)庫,所以使用項目編碼作為數(shù)據(jù)庫的索引。而微服務(wù)支持多線程并發(fā)的,采用線程變量。

package com.elon.dds.datasource;
/**
 * 數(shù)據(jù)庫標(biāo)識管理類。用于區(qū)分?jǐn)?shù)據(jù)源連接的不同數(shù)據(jù)庫。
 *
 * @author elon
 * @version 2018-02-25
 */
public class DBIdentifier {
 /**
 * 用不同的工程編碼來區(qū)分?jǐn)?shù)據(jù)庫
 */
 private static ThreadLocal<String> projectCode = new ThreadLocal<String>();
 public static String getProjectCode() {
 return projectCode.get();
 }
 public static void setProjectCode(String code) {
 projectCode.set(code);
 }
}

2)  從DataSource派生了一個DynamicDataSource,在其中實現(xiàn)數(shù)據(jù)庫連接的動態(tài)切換

import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.SQLException;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.tomcat.jdbc.pool.DataSource;
import org.apache.tomcat.jdbc.pool.PoolProperties;
import com.elon.dds.dbmgr.ProjectDBMgr;
/**
 * 定義動態(tài)數(shù)據(jù)源派生類。從基礎(chǔ)的DataSource派生,動態(tài)性自己實現(xiàn)。
 *
 * @author elon
 * @version 2018-02-25
 */
public class DynamicDataSource extends DataSource {
 private static Logger log = LogManager.getLogger(DynamicDataSource.class);
 /**
 * 改寫本方法是為了在請求不同工程的數(shù)據(jù)時去連接不同的數(shù)據(jù)庫。
 */
 @Override
 public Connection getConnection(){
 String projectCode = DBIdentifier.getProjectCode();
 //1、獲取數(shù)據(jù)源
 DataSource dds = DDSHolder.instance().getDDS(projectCode);
 //2、如果數(shù)據(jù)源不存在則創(chuàng)建
 if (dds == null) {
  try {
  DataSource newDDS = initDDS(projectCode);
  DDSHolder.instance().addDDS(projectCode, newDDS);
  } catch (IllegalArgumentException | IllegalAccessException e) {
  log.error("Init data source fail. projectCode:" + projectCode);
  return null;
  }
 }
 dds = DDSHolder.instance().getDDS(projectCode);
 try {
  return dds.getConnection();
 } catch (SQLException e) {
  e.printStackTrace();
  return null;
 }
 }
 /**
 * 以當(dāng)前數(shù)據(jù)對象作為模板復(fù)制一份。
 *
 * @return dds
 * @throws IllegalAccessException
 * @throws IllegalArgumentException
 */
 private DataSource initDDS(String projectCode) throws IllegalArgumentException, IllegalAccessException {
 DataSource dds = new DataSource();
 // 2、復(fù)制PoolConfiguration的屬性
 PoolProperties property = new PoolProperties();
 Field[] pfields = PoolProperties.class.getDeclaredFields();
 for (Field f : pfields) {
  f.setAccessible(true);
  Object value = f.get(this.getPoolProperties());
  try
  {
  f.set(property, value);  
  }
  catch (Exception e)
  {
  log.info("Set value fail. attr name:" + f.getName());
  continue;
  }
 }
 dds.setPoolProperties(property);
 // 3、設(shè)置數(shù)據(jù)庫名稱和IP(一般來說,端口和用戶名、密碼都是統(tǒng)一固定的)
 String urlFormat = this.getUrl();
 String url = String.format(urlFormat, ProjectDBMgr.instance().getDBIP(projectCode),
  ProjectDBMgr.instance().getDBName(projectCode));
 dds.setUrl(url);
 return dds;
 }
}

3)  通過DDSTimer控制數(shù)據(jù)連接釋放(超過指定時間未使用的數(shù)據(jù)源釋放)

package com.elon.dds.datasource;
import org.apache.tomcat.jdbc.pool.DataSource;
/**
 * 動態(tài)數(shù)據(jù)源定時器管理。長時間無訪問的數(shù)據(jù)庫連接關(guān)閉。
 *
 * @author elon
 * @version 2018年2月25日
 */
public class DDSTimer {
 /**
 * 空閑時間周期。超過這個時長沒有訪問的數(shù)據(jù)庫連接將被釋放。默認(rèn)為10分鐘。
 */
 private static long idlePeriodTime = 10 * 60 * 1000;
 /**
 * 動態(tài)數(shù)據(jù)源
 */
 private DataSource dds;
 /**
 * 上一次訪問的時間
 */
 private long lastUseTime;
 public DDSTimer(DataSource dds) {
 this.dds = dds;
 this.lastUseTime = System.currentTimeMillis();
 }
 /**
 * 更新最近訪問時間
 */
 public void refreshTime() {
 lastUseTime = System.currentTimeMillis();
 }
 /**
 * 檢測數(shù)據(jù)連接是否超時關(guān)閉。
 *
 * @return true-已超時關(guān)閉; false-未超時
 */
 public boolean checkAndClose() {
 if (System.currentTimeMillis() - lastUseTime > idlePeriodTime)
 {
  dds.close();
  return true;
 }
 return false;
 }
 public DataSource getDds() {
 return dds;
 }
}

4)      增加DDSHolder來管理不同的數(shù)據(jù)源,提供數(shù)據(jù)源的添加、查詢功能

package com.elon.dds.datasource;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Timer;
import org.apache.tomcat.jdbc.pool.DataSource;
/**
 * 動態(tài)數(shù)據(jù)源管理器。
 *
 * @author elon
 * @version 2018年2月25日
 */
public class DDSHolder {
 /**
 * 管理動態(tài)數(shù)據(jù)源列表。<工程編碼,數(shù)據(jù)源>
 */
 private Map<String, DDSTimer> ddsMap = new HashMap<String, DDSTimer>();
 /**
 * 通過定時任務(wù)周期性清除不使用的數(shù)據(jù)源
 */
 private static Timer clearIdleTask = new Timer();
 static {
 clearIdleTask.schedule(new ClearIdleTimerTask(), 5000, 60 * 1000);
 };
 private DDSHolder() {
 }
 /*
 * 獲取單例對象
 */
 public static DDSHolder instance() {
 return DDSHolderBuilder.instance;
 }
 /**
 * 添加動態(tài)數(shù)據(jù)源。
 *
 * @param projectCode 項目編碼
 * @param dds dds
 */
 public synchronized void addDDS(String projectCode, DataSource dds) {
 DDSTimer ddst = new DDSTimer(dds);
 ddsMap.put(projectCode, ddst);
 }
 /**
 * 查詢動態(tài)數(shù)據(jù)源
 *
 * @param projectCode 項目編碼
 * @return dds
 */
 public synchronized DataSource getDDS(String projectCode) {
 if (ddsMap.containsKey(projectCode)) {
  DDSTimer ddst = ddsMap.get(projectCode);
  ddst.refreshTime();
  return ddst.getDds();
 }
 return null;
 }
 /**
 * 清除超時無人使用的數(shù)據(jù)源。
 */
 public synchronized void clearIdleDDS() {
 Iterator<Entry<String, DDSTimer>> iter = ddsMap.entrySet().iterator();
 for (; iter.hasNext(); ) {
  Entry<String, DDSTimer> entry = iter.next();
  if (entry.getValue().checkAndClose())
  {
  iter.remove();
  }
 }
 }
 /**
 * 單例構(gòu)件類
 * @author elon
 * @version 2018年2月26日
 */
 private static class DDSHolderBuilder {
 private static DDSHolder instance = new DDSHolder();
 }
}

5)      定時器任務(wù)ClearIdleTimerTask用于定時清除空閑的數(shù)據(jù)源

package com.elon.dds.datasource;
import java.util.TimerTask;
/**
 * 清除空閑連接任務(wù)。
 *
 * @author elon
 * @version 2018年2月26日
 */
public class ClearIdleTimerTask extends TimerTask {
 @Override
 public void run() {
 DDSHolder.instance().clearIdleDDS();
 }
}

3.       管理項目編碼與數(shù)據(jù)庫IP和名稱的映射關(guān)系

package com.elon.dds.dbmgr;
import java.util.HashMap;
import java.util.Map;
/**
 * 項目數(shù)據(jù)庫管理。提供根據(jù)項目編碼查詢數(shù)據(jù)庫名稱和IP的接口。
 * @author elon
 * @version 2018年2月25日
 */
public class ProjectDBMgr {
 /**
 * 保存項目編碼與數(shù)據(jù)名稱的映射關(guān)系。這里是硬編碼,實際開發(fā)中這個關(guān)系數(shù)據(jù)可以保存到redis緩存中;
 * 新增一個項目或者刪除一個項目只需要更新緩存。到時這個類的接口只需要修改為從緩存拿數(shù)據(jù)。
 */
 private Map<String, String> dbNameMap = new HashMap<String, String>();
 /**
 * 保存項目編碼與數(shù)據(jù)庫IP的映射關(guān)系。
 */
 private Map<String, String> dbIPMap = new HashMap<String, String>();
 private ProjectDBMgr() {
 dbNameMap.put("project_001", "db_project_001");
 dbNameMap.put("project_002", "db_project_002");
 dbNameMap.put("project_003", "db_project_003");
 dbIPMap.put("project_001", "127.0.0.1");
 dbIPMap.put("project_002", "127.0.0.1");
 dbIPMap.put("project_003", "127.0.0.1");
 }
 public static ProjectDBMgr instance() {
 return ProjectDBMgrBuilder.instance;
 }
 // 實際開發(fā)中改為從緩存獲取
 public String getDBName(String projectCode) {
 if (dbNameMap.containsKey(projectCode)) {
  return dbNameMap.get(projectCode);
 }
 return "";
 }
 //實際開發(fā)中改為從緩存中獲取
 public String getDBIP(String projectCode) {
 if (dbIPMap.containsKey(projectCode)) {
  return dbIPMap.get(projectCode);
 }
 return "";
 }
 private static class ProjectDBMgrBuilder {
 private static ProjectDBMgr instance = new ProjectDBMgr();
 }
}

4.       定義數(shù)據(jù)庫訪問的mapper

package com.elon.dds.mapper;
import java.util.List;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Result;
import org.apache.ibatis.annotations.Results;
import org.apache.ibatis.annotations.Select;
import com.elon.dds.model.User;
/**
 * Mybatis映射接口定義。
 *
 * @author elon
 * @version 2018年2月26日
 */
@Mapper
public interface UserMapper
{
 /**
 * 查詢所有用戶數(shù)據(jù)
 * @return 用戶數(shù)據(jù)列表
 */
 @Results(value= {
  @Result(property="userId", column="id"),
  @Result(property="name", column="name"),
  @Result(property="age", column="age")
 })
 @Select("select id, name, age from tbl_user")
 List<User> getUsers();
}

5.       定義查詢對象模型

package com.elon.dds.model;
public class User
{
 private int userId = -1;
 private String name = "";
 private int age = -1;
 @Override
 public String toString()
 {
 return "name:" + name + "|age:" + age;
 }
 public int getUserId()
 {
 return userId;
 }
 public void setUserId(int userId)
 {
 this.userId = userId;
 }
 public String getName()
 {
 return name;
 }
 public void setName(String name)
 {
 this.name = name;
 }
 public int getAge()
 {
 return age;
 }
 public void setAge(int age)
 {
 this.age = age;
 }
}

6.       定義查詢用戶數(shù)據(jù)的restful接口

package com.elon.dds.rest;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.elon.dds.datasource.DBIdentifier;
import com.elon.dds.mapper.UserMapper;
import com.elon.dds.model.User;
/**
 * 用戶數(shù)據(jù)訪問接口。
 *
 * @author elon
 * @version 2018年2月26日
 */
@RestController
@RequestMapping(value="/user")
public class WSUser {
 @Autowired
 private UserMapper userMapper;
 /**
 * 查詢項目中所有用戶信息
 *
 * @param projectCode 項目編碼
 * @return 用戶列表
 */
 @RequestMapping(value="/v1/users", method=RequestMethod.GET)
 public List<User> queryUser(@RequestParam(value="projectCode", required=true) String projectCode)
 {
 DBIdentifier.setProjectCode(projectCode);
 return userMapper.getUsers();
 }
}

要求每次查詢都要帶上projectCode參數(shù)。

 7.       編寫Spring Boot App的啟動代碼

package com.elon.dds;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
 * Hello world!
 *
 */
@SpringBootApplication
public class App
{
 public static void main( String[] args )
 {
 System.out.println( "Hello World!" );
 SpringApplication.run(App.class, args);
 }
}

8.       在application.yml中配置數(shù)據(jù)源

其中的數(shù)據(jù)庫IP和數(shù)據(jù)庫名稱使用%s。在查詢用戶數(shù)據(jù)中動態(tài)切換。

spring:
 datasource:
 url: jdbc:mysql://%s:3306/%s?useUnicode=true&characterEncoding=utf-8
 username: root
 password:
 driver-class-name: com.mysql.jdbc.Driver
logging:
 config: classpath:log4j2.xml

測試方案

1.       查詢project_001的數(shù)據(jù),正常返回 

2.       查詢project_002的數(shù)據(jù),正常返回

總結(jié)

以上所述是小編給大家介紹的通過Spring Boot配置動態(tài)數(shù)據(jù)源訪問多個數(shù)據(jù)庫的實現(xiàn)代碼,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復(fù)大家的。在此也非常感謝大家對腳本之家網(wǎng)站的支持!

相關(guān)文章

  • IKAnalyzer使用不同版本中文分詞的切詞方式實現(xiàn)相同功能效果

    IKAnalyzer使用不同版本中文分詞的切詞方式實現(xiàn)相同功能效果

    今天小編就為大家分享一篇關(guān)于IKAnalyzer使用不同版本中文分詞的切詞方式實現(xiàn)相同功能效果,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧
    2018-12-12
  • Java實戰(zhàn)之在線租房系統(tǒng)的實現(xiàn)

    Java實戰(zhàn)之在線租房系統(tǒng)的實現(xiàn)

    這篇文章主要介紹了利用Java實現(xiàn)的在線租房系統(tǒng),文中用到了SpringBoot、Redis、MySQL、Vue等技術(shù),文中示例代碼講解詳細(xì),需要的可以參考一下
    2022-02-02
  • SpringBoot啟動時加載指定方法的方式小結(jié)

    SpringBoot啟動時加載指定方法的方式小結(jié)

    本文主要給大家介紹了Spring Boot項目啟動時加載指定方法都有哪些方式的,文中給大家介紹了五種常用的方式,有詳細(xì)的代碼示例,具有一定的參考價值,需要的朋友可以參考下
    2023-08-08
  • Java實現(xiàn)五子棋單機(jī)版

    Java實現(xiàn)五子棋單機(jī)版

    這篇文章主要為大家詳細(xì)介紹了Java實現(xiàn)五子棋單機(jī)版,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-05-05
  • 解析ConcurrentHashMap: 預(yù)熱(內(nèi)部一些小方法分析)

    解析ConcurrentHashMap: 預(yù)熱(內(nèi)部一些小方法分析)

    ConcurrentHashMap是由Segment數(shù)組結(jié)構(gòu)和HashEntry數(shù)組結(jié)構(gòu)組成。Segment的結(jié)構(gòu)和HashMap類似,是一種數(shù)組和鏈表結(jié)構(gòu),今天給大家普及java面試常見問題---ConcurrentHashMap知識,一起看看吧
    2021-06-06
  • ElasticSearch的完整安裝教程

    ElasticSearch的完整安裝教程

    這篇文章主要給大家分享介紹了ElasticSearch的完整安裝教程,文中通過示例代碼介紹的非常詳細(xì),對大家學(xué)習(xí)或者使用ElasticSearch具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-04-04
  • ConcurrentMap.putIfAbsent(key,value)用法實例

    ConcurrentMap.putIfAbsent(key,value)用法實例

    這篇文章主要介紹了ConcurrentMap.putIfAbsent(key,value)用法實例,分享了相關(guān)代碼示例,小編覺得還是挺不錯的,具有一定借鑒價值,需要的朋友可以參考下
    2018-02-02
  • Curator實現(xiàn)zookeeper的節(jié)點(diǎn)監(jiān)聽詳解

    Curator實現(xiàn)zookeeper的節(jié)點(diǎn)監(jiān)聽詳解

    這篇文章主要介紹了Curator實現(xiàn)zookeeper的節(jié)點(diǎn)監(jiān)聽詳解,Curtor框架中一共有三個實現(xiàn)監(jiān)聽的方式,一種是NodeCache監(jiān)聽指定節(jié)點(diǎn),一種是pathChildrenCache監(jiān)聽子節(jié)點(diǎn),一種是TreeCache可以監(jiān)控所有節(jié)點(diǎn) 相當(dāng)于以上兩種的合集,需要的朋友可以參考下
    2023-12-12
  • 使用springboot logback動態(tài)獲取application的配置項

    使用springboot logback動態(tài)獲取application的配置項

    這篇文章主要介紹了使用springboot logback動態(tài)獲取application的配置項,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-08-08
  • jasypt對配置文件的數(shù)據(jù)加密與解密方式

    jasypt對配置文件的數(shù)據(jù)加密與解密方式

    這篇文章主要介紹了jasypt對配置文件的數(shù)據(jù)加密與解密方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-01-01

最新評論

元氏县| 旺苍县| 甘谷县| 河曲县| 永康市| 安义县| 扶余县| 甘洛县| 乌拉特后旗| 临潭县| 项城市| 陆丰市| 克东县| 阳泉市| 莆田市| 道孚县| 旌德县| 稷山县| 洛南县| 韶山市| 西乡县| 恩施市| 阜宁县| 建瓯市| 兰考县| 阿拉尔市| 石棉县| 荃湾区| 大名县| 富源县| 广西| 香港| 鄂托克旗| 游戏| 玛沁县| 内乡县| 芦溪县| 万山特区| 白山市| 惠州市| 石林|