欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

親手教你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ù)邏輯中對不同的庫進行數(shù)據(jù)的操作(可能你們系統(tǒng)不存在這種場景,目前都時微服務(wù)的架構(gòu),每個微服務(wù)基本上是對應(yīng)一個數(shù)據(jù)庫比較多,其他的需要通過服務(wù)來方案。),
2.訪問分庫分表的場景;后面的文章會單獨介紹下分庫分表的集成。

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

集成方案

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

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

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

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

真正的使用過程中,我們定義注解,通過切面,定位到當前線程是由訪問哪個數(shù)據(jù)源(維護著一個ThreadLocal的對象),進而調(diào)用determineCurrentLookupKey方法,進行數(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ù)源注冊注冊默認數(shù)據(jù)源以及其他額外的數(shù)據(jù)源; 這里只是復(fù)制了核心的幾個方法,具體的大家下載git看代碼。

// 創(chuàng)建DynamicDataSource
        GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
        beanDefinition.setBeanClass(DyncRouteDataSource.class);
        beanDefinition.setSynthetic(true);
        MutablePropertyValues mpv = beanDefinition.getPropertyValues();
        //默認數(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ù)源注冊進去【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)文章

  • Java回調(diào)機制解讀

    Java回調(diào)機制解讀

    本文主要介紹了Java回調(diào)機制的相關(guān)知識,具有很好的參考價值,下面跟著小編一起來看下吧
    2017-02-02
  • Java 冒泡排序、快速排序?qū)嵗a

    Java 冒泡排序、快速排序?qū)嵗a

    這篇文章主要介紹了Java 冒泡排序、快速排序?qū)嵗a,需要的朋友可以參考下
    2017-05-05
  • Java中controller層如何接收帶參數(shù)的查詢

    Java中controller層如何接收帶參數(shù)的查詢

    本文主要介紹了Java中controller層如何接收帶參數(shù)的查詢,在控制器層接收帶參數(shù)的查詢可以通過多種方式實現(xiàn),下面就詳細的介紹一下,感興趣的可以了解一下
    2023-08-08
  • Java繼承的問題引導(dǎo)和測試代碼

    Java繼承的問題引導(dǎo)和測試代碼

    這篇文章主要介紹了Java繼承的問題引導(dǎo)和測試代碼,涉及繼承的概念,合成的語法等相關(guān)內(nèi)容,具有一定借鑒價值,需要的朋友可以參考下。
    2021-08-08
  • SpringBoot整合RabbitMQ的5種模式實戰(zhàn)

    SpringBoot整合RabbitMQ的5種模式實戰(zhàn)

    本文主要介紹了SpringBoot整合RabbitMQ的5種模式實戰(zhàn),文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-08-08
  • Mybatis-plus插入后返回元素id的問題

    Mybatis-plus插入后返回元素id的問題

    這篇文章主要介紹了Mybatis-plus插入后返回元素id的問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-03-03
  • 使用SpringBoot中整合Redis

    使用SpringBoot中整合Redis

    這篇文章主要介紹了使用SpringBoot中整合Redis,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-06-06
  • Java?SpringBoot整合shiro-spring-boot-starterqi項目報錯解決

    Java?SpringBoot整合shiro-spring-boot-starterqi項目報錯解決

    這篇文章主要介紹了Java?SpringBoot整合shiro-spring-boot-starterqi項目報錯解決,文章圍繞主題展開詳細的內(nèi)容介紹,具有一定的參考一下
    2022-08-08
  • Java自定義映射resultMap定義及用法

    Java自定義映射resultMap定義及用法

    MyBatis的每一個查詢映射的返回類型都是ResultMap,當我們提供返回類型屬性是resultType時,MyBatis會自動給我們把對應(yīng)值賦給resultType所指定對象的屬性,當我們提供返回類型是resultMap時,將數(shù)據(jù)庫中列數(shù)據(jù)復(fù)制到對象的相應(yīng)屬性上,可以用于復(fù)制查詢,兩者不能同時用
    2022-11-11
  • MyBatis一二級緩存

    MyBatis一二級緩存

    這篇文章主要介紹了MyBatis一二級緩存的相關(guān)知識,非常不錯,具有參考借鑒價值,需要的朋友可以參考下
    2017-06-06

最新評論