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

親手教你SpringBoot中的多數(shù)據(jù)源集成問題

 更新時間:2022年03月08日 10:59:57   作者:java不簡單  
本文主要是介紹基于springboot的多數(shù)據(jù)源切換,輕量級的一種集成方案,對于小型的應(yīng)用可以采用這種方案,我之前在項目中用到是因為簡單,便于擴展以及優(yōu)化,對SpringBoot多數(shù)據(jù)源集成問題感興趣的朋友一起看看吧

引言

其實對于分庫分表這塊的場景,目前市場上有很多成熟的開源中間件,eg:MyCAT,Cobar,sharding-JDBC等。

本文主要是介紹基于springboot的多數(shù)據(jù)源切換,輕量級的一種集成方案,對于小型的應(yīng)用可以采用這種方案,我之前在項目中用到是因為簡單,便于擴展以及優(yōu)化。

應(yīng)用場景

假設(shè)目前我們有以下幾種數(shù)據(jù)訪問的場景:
1.一個業(yè)務(wù)邏輯中對不同的庫進(jìn)行數(shù)據(jù)的操作(可能你們系統(tǒng)不存在這種場景,目前都時微服務(wù)的架構(gòu),每個微服務(wù)基本上是對應(yīng)一個數(shù)據(jù)庫比較多,其他的需要通過服務(wù)來方案。),
2.訪問分庫分表的場景;后面的文章會單獨介紹下分庫分表的集成。

假設(shè)這里,我們以6個庫,每個庫1000張表的例子來介紹),因為隨著業(yè)務(wù)量的增長,一個庫很難抗住這么大的訪問量。比如說訂單表,我們可以根據(jù)userid進(jìn)行分庫分表。
分庫策略:userId%6[表的數(shù)量];
分庫分表策略:庫路由[userId/(6*1000)/1000],表路由[userId/(6*1000)%1000].

集成方案

方案總覽:
方案是基于springjdbc中提供的api實現(xiàn),看下下面兩段代碼,是我們的切入點,我們都是圍繞著這2個核心方法進(jìn)行集成的。

第一段代碼是注冊邏輯,會將defaultTargetDataSource和targetDataSources這兩個數(shù)據(jù)源對象注冊到resolvedDataSources中。

第二段是具體切換邏輯: 如果數(shù)據(jù)源為空,那么他就會找我們的默認(rèn)數(shù)據(jù)源defaultTargetDataSource,如果設(shè)置了數(shù)據(jù)源,那么他就去讀這個值Object lookupKey = determineCurrentLookupKey();我們后面會重寫這個方法。

我們會在配置文件中配置主數(shù)據(jù)源(默認(rèn)數(shù)據(jù)源)和其他數(shù)據(jù)源,然后在應(yīng)用啟動的時候注冊到spring容器中,分別給defaultTargetDataSource和targetDataSources進(jìn)行賦值,進(jìn)而進(jìn)行Bean注冊。

真正的使用過程中,我們定義注解,通過切面,定位到當(dāng)前線程是由訪問哪個數(shù)據(jù)源(維護(hù)著一個ThreadLocal的對象),進(jìn)而調(diào)用determineCurrentLookupKey方法,進(jìn)行數(shù)據(jù)源的切換。

public void afterPropertiesSet() {
        if (this.targetDataSources == null) {
            throw new IllegalArgumentException("Property 'targetDataSources' is required");
        }
        this.resolvedDataSources = new HashMap<Object, DataSource>(this.targetDataSources.size());
        for (Map.Entry<Object, Object> entry : this.targetDataSources.entrySet()) {
            Object lookupKey = resolveSpecifiedLookupKey(entry.getKey());
            DataSource dataSource = resolveSpecifiedDataSource(entry.getValue());
            this.resolvedDataSources.put(lookupKey, dataSource);
        if (this.defaultTargetDataSource != null) {
            this.resolvedDefaultDataSource = resolveSpecifiedDataSource(this.defaultTargetDataSource);
    }

@Override
public Connection getConnection() throws SQLException {
        return determineTargetDataSource().getConnection();
}
protected DataSource determineTargetDataSource() {
        Assert.notNull(this.resolvedDataSources, "DataSource router not initialized");
        Object lookupKey = determineCurrentLookupKey();
        DataSource dataSource = this.resolvedDataSources.get(lookupKey);
        if (dataSource == null && (this.lenientFallback || lookupKey == null)) {
            dataSource = this.resolvedDefaultDataSource;
        if (dataSource == null) {
            throw new IllegalStateException("Cannot determine target DataSource for lookup key [" + lookupKey + "]");
        return dataSource;

1.動態(tài)數(shù)據(jù)源注冊注冊默認(rèn)數(shù)據(jù)源以及其他額外的數(shù)據(jù)源; 這里只是復(fù)制了核心的幾個方法,具體的大家下載git看代碼。

// 創(chuàng)建DynamicDataSource
        GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
        beanDefinition.setBeanClass(DyncRouteDataSource.class);
        beanDefinition.setSynthetic(true);
        MutablePropertyValues mpv = beanDefinition.getPropertyValues();
        //默認(rèn)數(shù)據(jù)源
        mpv.addPropertyValue("defaultTargetDataSource", defaultDataSource);
        //其他數(shù)據(jù)源
        mpv.addPropertyValue("targetDataSources", targetDataSources);
        //注冊到spring bean容器中
        registry.registerBeanDefinition("dataSource", beanDefinition);

2.在Application中加載動態(tài)數(shù)據(jù)源,這樣spring容器啟動的時候就會將數(shù)據(jù)源加載到內(nèi)存中。

@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class, DataSourceTransactionManagerAutoConfiguration.class, MybatisAutoConfiguration.class })
@Import(DyncDataSourceRegister.class)
@ServletComponentScan
@ComponentScan(basePackages = { "com.happy.springboot.api","com.happy.springboot.service"})
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

3.數(shù)據(jù)源切換核心邏輯:創(chuàng)建一個數(shù)據(jù)源切換的注解,并且基于該注解實現(xiàn)切面邏輯,這里我們通過threadLocal來實現(xiàn)線程間的數(shù)據(jù)源切換;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TargetDataSource {
    String value();
    // 是否分庫
    boolean isSharding() default false;
    // 獲取分庫策略
    String strategy() default "";
}
// 動態(tài)數(shù)據(jù)源上下文
public class DyncDataSourceContextHolder {
    private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();
    public static List<String> dataSourceNames = new ArrayList<String>();
    public static void setDataSource(String dataSourceName) {
        contextHolder.set(dataSourceName);
    }
    public static String getDataSource() {
        return contextHolder.get();
    public static void clearDataSource() {
        contextHolder.remove();
    public static boolean containsDataSource(String dataSourceName) {
        return dataSourceNames.contains(dataSourceName);
/**
 *
 * @author jasoHsu
 * 動態(tài)數(shù)據(jù)源在切換,將數(shù)據(jù)源設(shè)置到ThreadLocal對象中
 */
@Component
@Order(-10)
@Aspect
public class DyncDataSourceInterceptor {
    @Before("@annotation(targetDataSource)")
    public void changeDataSource(JoinPoint point, TargetDataSource targetDataSource) throws Exception {
        String dbIndx = null;
        String targetDataSourceName = targetDataSource.value() + (dbIndx == null ? "" : dbIndx);
        if (DyncDataSourceContextHolder.containsDataSource(targetDataSourceName)) {
            DyncDataSourceContextHolder.setDataSource(targetDataSourceName);
        }
    @After("@annotation(targetDataSource)")
    public void resetDataSource(JoinPoint point, TargetDataSource targetDataSource) {
        DyncDataSourceContextHolder.clearDataSource();
//
public class DyncRouteDataSource extends AbstractRoutingDataSource {
@Override
    protected Object determineCurrentLookupKey() {
        return DyncDataSourceContextHolder.getDataSource();
    public DataSource findTargetDataSource() {
        return this.determineTargetDataSource();

4.與mybatis集成,其實這一步與前面的基本一致。
只是將在sessionFactory以及數(shù)據(jù)管理器中,將動態(tài)數(shù)據(jù)源注冊進(jìn)去【dynamicRouteDataSource】,而非之前的靜態(tài)數(shù)據(jù)源【getMasterDataSource()】

@Resource
    DyncRouteDataSource dynamicRouteDataSource;

    @Bean(name = "sqlSessionFactory")
    public SqlSessionFactory getinvestListingSqlSessionFactory() throws Exception {
        String configLocation = "classpath:/conf/mybatis/configuration.xml";
        String mapperLocation = "classpath*:/com/happy/springboot/service/dao/*/*Mapper.xml";
        SqlSessionFactory sqlSessionFactory = createDefaultSqlSessionFactory(dynamicRouteDataSource, configLocation,
                mapperLocation);
        return sqlSessionFactory;
    }
    @Bean(name = "txManager")
    public PlatformTransactionManager txManager() {
        return new DataSourceTransactionManager(dynamicRouteDataSource);

5.核心配置參數(shù):

spring:
  dataSource:
    happyboot:
      driverClassName: com.mysql.jdbc.Driver
      url: jdbc:mysql://localhost:3306/happy_springboot?characterEncoding=utf8&useSSL=true
      username: root
      password: admin
    extdsnames: happyboot01,happyboot02
    happyboot01:
      driverClassName: com.mysql.jdbc.Driver
      url: jdbc:mysql://localhost:3306/happyboot01?characterEncoding=utf8&useSSL=true
      username: root
      password: admin
    happyboot02:
      driverClassName: com.mysql.jdbc.Driver
      url: jdbc:mysql://localhost:3306/happyboot02?characterEncoding=utf8&useSSL=true
      username: root
      password: admin

6.應(yīng)用代碼

@Service
public class UserExtServiceImpl implements IUserExtService {
    @Autowired
    private UserInfoMapper userInfoMapper;
    @TargetDataSource(value="happyboot01")
    @Override
    public UserInfo getUserBy(Long id) {
        return userInfoMapper.selectByPrimaryKey(id);
    }
}

到此這篇關(guān)于關(guān)于SpringBoot中的多數(shù)據(jù)源集成的文章就介紹到這了,更多相關(guān)SpringBoot多數(shù)據(jù)源集成內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java8中List轉(zhuǎn)Map(Collectors.toMap) 的技巧分享

    Java8中List轉(zhuǎn)Map(Collectors.toMap) 的技巧分享

    在最近的工作開發(fā)之中,慢慢習(xí)慣了很多Java8中的Stream的用法,很方便而且也可以并行的去執(zhí)行這個流,這篇文章主要給大家介紹了關(guān)于Java8中List轉(zhuǎn)Map(Collectors.toMap) 的相關(guān)資料,需要的朋友可以參考下
    2021-07-07
  • Java類加載器ClassLoader源碼層面分析講解

    Java類加載器ClassLoader源碼層面分析講解

    ClassLoader翻譯過來就是類加載器,普通的java開發(fā)者其實用到的不多,但對于某些框架開發(fā)者來說卻非常常見。理解ClassLoader的加載機制,也有利于我們編寫出更高效的代碼。ClassLoader的具體作用就是將class文件加載到j(luò)vm虛擬機中去,程序就可以正確運行了
    2022-09-09
  • JAVA新手學(xué)習(xí)篇之類和對象詳解

    JAVA新手學(xué)習(xí)篇之類和對象詳解

    這篇文章主要給大家介紹了關(guān)于JAVA新手學(xué)習(xí)篇之類和對象的相關(guān)資料,Java是面向?qū)ο蟮木幊陶Z言,主旨在于通過對象封裝屬性和方法實現(xiàn)功能,面向?qū)ο笈c面向過程的區(qū)別在于關(guān)注點的不同,需要的朋友可以參考下
    2024-10-10
  • java后端解決跨域的幾種問題解決

    java后端解決跨域的幾種問題解決

    這篇文章主要介紹了java后端解決跨域的幾種問題解決,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-03-03
  • Java開發(fā)環(huán)境配置JDK超詳細(xì)整理(適合新手入門)

    Java開發(fā)環(huán)境配置JDK超詳細(xì)整理(適合新手入門)

    這篇文章主要給大家介紹了關(guān)于Java開發(fā)環(huán)境配置JDK超詳細(xì)整理的相關(guān)資料,非常適合新手入門,JDK是Java語言的軟件開發(fā)工具包,主要用于移動設(shè)備、嵌入式設(shè)備上的java應(yīng)用程序,需要的朋友可以參考下
    2023-11-11
  • Java中StringBuilder類常用方法總結(jié)

    Java中StringBuilder類常用方法總結(jié)

    這篇文章主要介紹了Java中StringBuilder類常用方法的相關(guān)資料,StringBuilder類是Java中用于頻繁修改字符串的可變字符串緩沖區(qū)類,它提供了多種方法進(jìn)行字符串操作,如添加、插入、刪除、替換字符等,需要的朋友可以參考下
    2024-12-12
  • java-spark中各種常用算子的寫法示例

    java-spark中各種常用算子的寫法示例

    這篇文章主要給大家介紹了關(guān)于java-spark中各種常用算子的寫法的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2018-06-06
  • 解決運行jar包出錯:ClassNotFoundException問題

    解決運行jar包出錯:ClassNotFoundException問題

    這篇文章主要介紹了解決運行jar包出錯:ClassNotFoundException問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-12-12
  • spring boot實現(xiàn)文件上傳

    spring boot實現(xiàn)文件上傳

    這篇文章主要為大家詳細(xì)介紹了spring boot實現(xiàn)文件上傳,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-08-08
  • Spring中利用IOC實現(xiàn)注入的方式

    Spring中利用IOC實現(xiàn)注入的方式

    Spring IOC(控制反轉(zhuǎn))實現(xiàn)依賴注入,將對象創(chuàng)建和依賴關(guān)系的管理交由Spring容器處理,通過注解或XML配置,實現(xiàn)對象之間的松耦合,提高代碼復(fù)用性和可維護(hù)性
    2023-04-04

最新評論

凭祥市| 河西区| 辉南县| 斗六市| 甘谷县| 佛教| 肥城市| 富顺县| 廉江市| 万山特区| 张家口市| 来凤县| 宜州市| 灵武市| 乌苏市| 华坪县| 柳江县| 年辖:市辖区| 石城县| 潞城市| 天峨县| 花莲市| 武汉市| 东明县| 游戏| 招远市| 曲松县| 繁峙县| 本溪| 常山县| 义马市| 康马县| 西吉县| 新营市| 峨山| 文登市| 株洲市| 靖远县| 湘乡市| 佳木斯市| 清流县|