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

Mybatis-Plus集成Sharding-JDBC與Flyway實(shí)現(xiàn)多租戶(hù)分庫(kù)分表實(shí)戰(zhàn)

 更新時(shí)間:2023年11月21日 10:17:13   作者:逆流的小魚(yú)168  
這篇文章主要為大家介紹了Mybatis-Plus集成Sharding-JDBC與Flyway實(shí)現(xiàn)多租戶(hù)分庫(kù)分表實(shí)戰(zhàn),有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

背景

公司產(chǎn)品部收到了一些重要客戶(hù)的需求,他們希望能夠依賴(lài)獨(dú)立的數(shù)據(jù)庫(kù)存儲(chǔ)來(lái)支持他們的業(yè)務(wù)數(shù)據(jù)。與此同時(shí),仍有許多中小客戶(hù),可以繼續(xù)使用公共庫(kù)以滿(mǎn)足其需求。技術(shù)實(shí)現(xiàn)方面,此前持久層框架使用的Mybatis-plus,部分業(yè)務(wù)場(chǎng)景使用到了Sharding-JDBC用于分表,另外,我們的數(shù)據(jù)庫(kù)版本控制工具使用的是Flyway。

方案說(shuō)明

這里將方案進(jìn)行簡(jiǎn)要說(shuō)明,配置統(tǒng)一通過(guò)Nacos管理(有需要的可以自行定義租戶(hù)配置頁(yè)面)。

1.首先多數(shù)據(jù)源管理使用Mybatis-Plus官方推薦的dynamic-datasource-spring-boot-starter組件,需要注意的是構(gòu)建動(dòng)態(tài)多數(shù)據(jù)源時(shí),我們要把Sharding-JDBC數(shù)據(jù)源也納入管理。因?yàn)槲覀兊膸?kù)里面畢竟只有部分表用到了Sharding-JDBC,這樣可以復(fù)用數(shù)據(jù)源。

2.其次,租戶(hù)與數(shù)據(jù)源之間在Nacos建立關(guān)系配置,確保根據(jù)租戶(hù)ID能夠路由到唯一的租戶(hù)數(shù)據(jù)源。我們需要自定義Sharding分片策略和多數(shù)據(jù)源切換邏輯,根據(jù)http請(qǐng)求傳入的租戶(hù)ID,設(shè)置正確的數(shù)據(jù)源。

3.動(dòng)態(tài)數(shù)據(jù)源與Sharding數(shù)據(jù)源配置做為公共配置在Nacos維護(hù),在業(yè)務(wù)服務(wù)啟動(dòng)時(shí),讀取公共配置初始化多數(shù)據(jù)源,并添加對(duì)公共多數(shù)據(jù)源配置的監(jiān)聽(tīng)。當(dāng)配置變更時(shí),重新構(gòu)造Sharding數(shù)據(jù)源,并并更新動(dòng)態(tài)多數(shù)據(jù)源。另外數(shù)據(jù)庫(kù)腳本通過(guò)自定義flyway配置執(zhí)行。

技術(shù)實(shí)現(xiàn)

前提

需要在Nacos提前維護(hù)租戶(hù)與數(shù)據(jù)源關(guān)系配置。

不使用Sharding-JDBC場(chǎng)景

1.引入相關(guān)組件依賴(lài)。

<dependency>
          <groupId>com.alibaba.nacos</groupId>
          <artifactId>nacos-client</artifactId>
          <version>2.1.0</version>
      </dependency>
      <dependency>
          <groupId>org.flywaydb</groupId>
          <artifactId>flyway-core</artifactId>
          <version>7.15.0</version>
      </dependency>
      <dependency>
          <groupId>com.baomidou</groupId>
          <artifactId>mybatis-plus-boot-starter</artifactId>
          <version>3.4.1</version>
      </dependency>
      <dependency>
          <groupId>com.baomidou</groupId>
          <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
          <version>3.4.1</version>
      </dependency>
      <dependency>
          <groupId>com.alibaba</groupId>
          <artifactId>druid</artifactId>
          <version>1.2.6</version>
      </dependency>

2.關(guān)閉Flyway自動(dòng)配置和配置多數(shù)據(jù)源。

spring:
flyway:
  #關(guān)閉flyway自動(dòng)配置,自定義實(shí)現(xiàn)
  enabled: false
datasource:
  dynamic:
    #默認(rèn)數(shù)據(jù)源
    primary: ds0
    datasource:
      ds0:
        type: com.alibaba.druid.pool.DruidDataSource
        driverClassName: org.postgresql.Driver
        url: jdbc:postgresql://127.0.0.1:5432/ds0
        username: ds0
        password: ds0123
      ds1:
        type: com.alibaba.druid.pool.DruidDataSource
        driverClassName: org.postgresql.Driver
        url: jdbc:postgresql://127.0.0.1:5432/ds1
        username: ds1
        password: ds1123

3.自定義實(shí)現(xiàn)Flyway配置類(lèi),對(duì)應(yīng)的flyway腳本目錄結(jié)構(gòu)見(jiàn)下圖,主庫(kù)和租戶(hù)庫(kù)SQL腳本獨(dú)立維護(hù)。

Java
@Slf4j
@Configuration
@EnableTransactionManagement
public class FlywayConfig {
  @Value("${spring.application.name}")
  private String appName;
  @Autowired
  private DataSource dataSource;
  @Bean
  public void migrate() {
      log.info("flyway開(kāi)始逐數(shù)據(jù)源執(zhí)行腳本");
      DynamicRoutingDataSource ds = (DynamicRoutingDataSource) dataSource;
      Map<String, DataSource> dataSources = ds.getDataSources();
      dataSources.forEach((k, v) -> {
          if (!"sharding".equals(k)) {
                          // Flyway相關(guān)參數(shù)建議通過(guò)配置管理,以下代碼僅供參考
              Flyway flyway = Flyway.configure()
                      .dataSource(v)
                      .table("t_" + k + "_" + appName + "_version")
                      .baselineOnMigrate(true)
                      .outOfOrder(true)
                      .baselineVersion("1.0.0")
                      .baselineDescription(k + "初始化")
                      .locations(CommonConstant.SQL_BASE_LOCATION + (CommonConstant.DEFAULT_DS_NAME.equals(k) ? CommonConstant.MASTER_DB : CommonConstant.TENANT_DB))
                      .load();
              flyway.migrate();
              log.info("flyway在 {} 數(shù)據(jù)源執(zhí)行腳本成功", k);
          }
      });
  }
}

4.自定義實(shí)現(xiàn)數(shù)據(jù)源切換Filter類(lèi)。

@Slf4j
@Component
@WebFilter(filterName = "dynamicDatasourceFilter", urlPatterns = {"/*"})
public class DynamicDatasourceFilter implements Filter {
  // 構(gòu)建演示用租戶(hù)與數(shù)據(jù)源關(guān)系配置
      private static Map<String, String> tenantDsMap = new HashMap<>();
  static {
      tenantDsMap.put("tenant123", "ds0");
      tenantDsMap.put("tenant456", "ds0");
              tenantDsMap.put("tenant789", "ds1");
  }
  @Override
  public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
      HttpServletRequest httpRequest = (HttpServletRequest) request;
      // 從請(qǐng)求頭獲取租戶(hù)ID
      String tenantId = httpRequest.getHeader(CommonConstant.TENANT_HEADER);
      try {
          // 設(shè)置數(shù)據(jù)源
          if (tenantDsMap.get(tenantId) == null) {
              // 如果根據(jù)租戶(hù)ID未找到租戶(hù)數(shù)據(jù)源配置,默認(rèn)走主庫(kù)
              DynamicDataSourceContextHolder.push(CommonConstant.DEFAULT_DS_NAME);
          } else {
              //注意,如果是分片表,那么需要在分片表Service類(lèi)或方法上加@DS("sharding")注解,最終由sharding的庫(kù)分片策略決定SQL在哪個(gè)庫(kù)執(zhí)行。而這里的設(shè)置將會(huì)被@DS注解配置覆蓋
              DynamicDataSourceContextHolder.push(tenantDsMap.get(tenantId));
          }
          // 執(zhí)行
          chain.doFilter(request, response);
      } catch (Exception e) {
          log.error("切換數(shù)據(jù)源失敗,tenantId={},請(qǐng)求接口uri={},異常原因:{}", tenantId, httpRequest.getRequestURI(), ExceptionUtils.getStackTrace(e));
      } finally {
          // 清空當(dāng)前線程數(shù)據(jù)源
          DynamicDataSourceContextHolder.poll();
      }
  }

使用Sharding-JDBC

如果微服務(wù)還需要使用Sharding分片,那么還需要引入sharding-jdbc組件依賴(lài),并配置sharding數(shù)據(jù)源和分片規(guī)則。如果是多個(gè)服務(wù)共用數(shù)據(jù)庫(kù),那么建議將Sharding數(shù)據(jù)源配置做為公共配置在Nacos管理,而Sharding分片規(guī)則則做為服務(wù)個(gè)性化配置單獨(dú)維護(hù)(分片規(guī)則基本不需要?jiǎng)討B(tài)變更),這樣當(dāng)有新租戶(hù)需要申請(qǐng)開(kāi)通獨(dú)立租戶(hù)庫(kù)的時(shí)候,直接變更Sharding數(shù)據(jù)源公共配置,服務(wù)在監(jiān)聽(tīng)到公共配置變更后,即可重新構(gòu)建新的Sharding數(shù)據(jù)源實(shí)例和動(dòng)態(tài)數(shù)據(jù)源更新,無(wú)需重啟服務(wù)。

1.引入sharding-jdbc組件依賴(lài)。

<dependency>
          <groupId>org.apache.shardingsphere</groupId>
          <artifactId>sharding-jdbc-core</artifactId>
          <version>4.1.1</version>
      </dependency>

2.配置Sharding數(shù)據(jù)源和分片規(guī)則。

# sharding數(shù)據(jù)源配置
dataSources:
ds0: !!com.alibaba.druid.pool.DruidDataSource
  driverClassName: org.postgresql.Driver
  url: jdbc:postgresql://127.0.0.1:5432/ds0
  username: ds0
  password: ds0123
ds1: !!com.alibaba.druid.pool.DruidDataSource
  driverClassName: org.postgresql.Driver
  url: jdbc:postgresql://127.0.0.1:5432/ds1
  username: ds1
  password: ds1123
ds2: !!com.alibaba.druid.pool.DruidDataSource
  driverClassName: org.postgresql.Driver
  url: jdbc:postgresql://127.0.0.1:5432/ds2
  username: ds2
  password: ds2123
# sharding分片規(guī)則配置
shardingRule:
tables:
  t_order:
    actualDataNodes: ds$->{0..2}.t_order$->{0..1}
    tableStrategy:
      inline:
        shardingColumn: order_no
        algorithmExpression: t_order$->{order_no.toBigInteger() % 2}
defaultDataSourceName: ds0
# 默認(rèn)庫(kù)分片策略
defaultDatabaseStrategy:
  standard:
    shardingColumn: tenant_id
          # 自定義精確分片策略
    preciseAlgorithmClassName: cn.xtstu.demo.config.CustomDataSourcePreciseShardingAlgorithm
  #hint:
          # 
  #  algorithmClassName: cn.xtstu.demo.config.CustomHintShardingAlgorithm
defaultTableStrategy:
  none:
props:
sql.show: true

3.自定義精確分片策略。

public class CustomDataSourcePreciseShardingAlgorithm implements PreciseShardingAlgorithm<String> {

  // 構(gòu)建演示用租戶(hù)與數(shù)據(jù)源關(guān)系配置
      private static Map<String, String> tenantDsMap = new HashMap<>();
  static {
      tenantDsMap.put("tenant123", "ds0");
      tenantDsMap.put("tenant456", "ds0");
              tenantDsMap.put("tenant789", "ds1");
  }
      
  @Override
  public String doSharding(Collection<String> dataSourceNames, PreciseShardingValue<String> shardingValue) {
          // 庫(kù)分片策略配置的分片鍵是字段tenant_id,根據(jù)分片鍵查詢(xún)配置的數(shù)據(jù)源
          String dsName = tenantDsMap.get(shardingValue.getValue());
      // 如果如前文所屬,Sharding子數(shù)據(jù)源key與dynamic數(shù)據(jù)源key保持一致的話,這里直接返回就行了
              return dsName;
      // TODO 需要處理未匹配到數(shù)據(jù)源的情況
  }
}

4.自定義Hint分片策略(可選),適用于分片鍵與SQL無(wú)關(guān)的場(chǎng)景。

public class CustomHintShardingAlgorithm implements HintShardingAlgorithm<Integer> {
  // 構(gòu)建演示用租戶(hù)與數(shù)據(jù)源關(guān)系配置
      private static Map<String, String> tenantDsMap = new HashMap<>();
  static {
      tenantDsMap.put("tenant123", "ds0");
      tenantDsMap.put("tenant456", "ds0");
              tenantDsMap.put("tenant789", "ds1");
  }
  @Override
  public Collection<String> doSharding(Collection<String> collection, HintShardingValue<Integer> hintShardingValue) {
      Collection<String> result = new ArrayList<>();
      // 從請(qǐng)求頭取到當(dāng)前租戶(hù)ID
              HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
      result.add(tenantDsMap.get(request.getHeader("tenantId")));
              // TODO  需要處理未匹配到數(shù)據(jù)源的情況
      return result;
  }
}

5.自定義動(dòng)態(tài)數(shù)據(jù)源配置(核心就是將sharding數(shù)據(jù)源及其子數(shù)據(jù)源添加到動(dòng)態(tài)數(shù)據(jù)源一起管理)。

@Slf4j
@Configuration
public class CustomDynamicDataSourceConfig {
  @Value("${spring.cloud.nacos.config.extension-configs[0].data-id}")
  private String dataId;
  @Value("${spring.cloud.nacos.config.group:DEFAULT_GROUP}")
  private String group;
  @Resource
  private DynamicDataSourceProperties properties;
  @Resource
  private NacosHelper nacosHelper;

  /**
   * 啟動(dòng)時(shí)通過(guò)查詢(xún)Nacos上sharding數(shù)據(jù)源及分片規(guī)則yaml配置初始化sharding-jdbc數(shù)據(jù)源
   *
   * @return
   */
  @Bean
  public ShardingDataSource shardingDataSource() {
      ConfigService configService = nacosHelper.getConfigService();
      if (configService == null) {
          log.error("連接nacos失敗");
      }
      String configInfo = null;
      try {
          configInfo = configService.getConfig(dataId, group, 5000);
      } catch (NacosException e) {
          log.error("獲取{}配置失敗,異常原因:{}", dataId, ExceptionUtils.getStackTrace(e));
      }
      if (StringUtils.isBlank(configInfo)) {
          log.error("{}配置為空,啟動(dòng)失敗", dataId);
          throw new NullPointerException(dataId + "配置為空");
      }
      try {
          // 通過(guò)工廠類(lèi)和yaml配置創(chuàng)建Sharding數(shù)據(jù)源
          return (ShardingDataSource) YamlShardingDataSourceFactory.createDataSource(configInfo.getBytes(StandardCharsets.UTF_8));
      } catch (Exception e) {
          log.error("創(chuàng)建sharding-jdbc數(shù)據(jù)源異常:{}", ExceptionUtils.getStackTrace(e));
          throw new NullPointerException("sharding-jdbc數(shù)據(jù)源為空");
      }
  }

  /**
   * 將動(dòng)態(tài)數(shù)據(jù)源設(shè)置為首選的
   * 當(dāng)spring存在多個(gè)數(shù)據(jù)源時(shí), 自動(dòng)注入的是首選的對(duì)象
   * 設(shè)置為主要的數(shù)據(jù)源之后,就可以支持shardingJdbc原生的配置方式了
   */
  @Primary
  @Bean
  public DataSource dataSource() {
      DynamicRoutingDataSource dataSource = new DynamicRoutingDataSource();
      dataSource.setPrimary(properties.getPrimary());
      dataSource.setStrict(properties.getStrict());
      dataSource.setStrategy(properties.getStrategy());
      dataSource.setP6spy(properties.getP6spy());
      dataSource.setSeata(properties.getSeata());
      return dataSource;
  }

  /**
   * 初始化動(dòng)態(tài)數(shù)據(jù)源
   *
   * @return
   */
  @Bean
  public DynamicDataSourceProvider dynamicDataSourceProvider(ShardingDataSource shardingDataSource) {
      return new AbstractDataSourceProvider() {
          @Override
          public Map&lt;String, DataSource&gt; loadDataSources() {
              Map&lt;String, DataSource&gt; dataSourceMap = new HashMap&lt;&gt;();
              // 將sharding數(shù)據(jù)源整體添加到動(dòng)態(tài)數(shù)據(jù)源里
              dataSourceMap.put(CommonConstant.SHARDING_DS_NAME, shardingDataSource);
              // 同時(shí)把sharding內(nèi)部管理的子數(shù)據(jù)源也添加到動(dòng)態(tài)數(shù)據(jù)源里
              Map&lt;String, DataSource&gt; shardingInnerDataSources = shardingDataSource.getDataSourceMap();
              dataSourceMap.putAll(shardingInnerDataSources);
              return dataSourceMap;
          }
      };
  }
}

6.最后給出一份通過(guò)監(jiān)聽(tīng)Nacos配置變更動(dòng)態(tài)更新數(shù)據(jù)源的示例代碼。注意:這份示例代碼中只給出了Sharding配置變更時(shí)的處理邏輯,如果是dynamic數(shù)據(jù)源配置的話,有需要的可以參考著自行實(shí)現(xiàn)。

@Slf4j
@Configuration
public class NacosShardingConfigListener {
  @Value("${spring.cloud.nacos.config.extension-configs[0].data-id}")
  private String dataId;
  @Value("${spring.cloud.nacos.config.group:DEFAULT_GROUP}")
  private String group;
  @Value("${spring.application.name}")
  private String appName;
  @Autowired
  private DataSource dataSource;
  @Autowired
  private NacosHelper nacosHelper;
  @PostConstruct
  public void shardingConfigListener() throws Exception {
      ConfigService configService = nacosHelper.getConfigService();
      if (configService == null) {
          return;
      }
      configService.addListener(dataId, group, new Listener() {
          @Override
          public Executor getExecutor() {
              return null;
          }
          @Override
          public void receiveConfigInfo(String configInfo) {
              log.info("configInfo:\n{}", configInfo);
              if (StringUtils.isBlank(configInfo)) {
                  log.warn("sharding-jdbc配置為空,不會(huì)刷新數(shù)據(jù)源");
                  return;
              }
              try {
                  if (StringUtils.isNotBlank(configInfo)) {
                      // 通過(guò)yaml配置創(chuàng)建sharding數(shù)據(jù)源(注意:如果分片規(guī)則是獨(dú)立配置文件,那么需要提前合并數(shù)據(jù)源和分片規(guī)則配置)
                      ShardingDataSource shardingDataSource = (ShardingDataSource) YamlShardingDataSourceFactory.createDataSource(configInfo.getBytes(StandardCharsets.UTF_8));
                      Map<String, DataSource> shardingInnerDataSources = shardingDataSource.getDataSourceMap();
                      DynamicRoutingDataSource ds = (DynamicRoutingDataSource) dataSource;
                      // 遍歷sharding子數(shù)據(jù)源
                      for (String poolName : shardingInnerDataSources.keySet()) {
                          // TODO 這里還有個(gè)細(xì)節(jié),如果yaml配置刪減了數(shù)據(jù)源,對(duì)應(yīng)數(shù)據(jù)源應(yīng)該要從ds中remove掉,且主數(shù)據(jù)源不能被remove。另外其實(shí)只有新增的數(shù)據(jù)源才需要執(zhí)行flyway腳本
                          // 將sharding子數(shù)據(jù)源逐個(gè)添加到動(dòng)態(tài)數(shù)據(jù)源
                          ds.addDataSource(poolName, shardingInnerDataSources.get(poolName));
                          // 通過(guò)代碼完成數(shù)據(jù)源Flyway配置,并執(zhí)行遷移操作
                                                      Flyway flyway = Flyway.configure()
                                  .dataSource(dataSource)
                                  .table("t_" + poolName + "_" + appName + "_version")
                                  .baselineOnMigrate(true)
                                  .outOfOrder(true)
                                  .baselineVersion("1.0.0")
                                  .baselineDescription(poolName + "初始化")
                                  .locations(CommonConstant.SQL_BASE_LOCATION + CommonConstant.TENANT_DB)
                                  .load();
                          flyway.migrate();
                      }
                      // 將sharding數(shù)據(jù)源自身也添加到動(dòng)態(tài)數(shù)據(jù)源
                      ds.addDataSource(CommonConstant.SHARDING_DS_NAME, shardingDataSource);
                      log.info("動(dòng)態(tài)數(shù)據(jù)源刷新完成,現(xiàn)有數(shù)據(jù)源:{}", JSONUtil.toJsonStr(ds.getDataSources().keySet()));
                  }
              } catch (Exception e) {
                  log.error("創(chuàng)建sharding-jdbc數(shù)據(jù)源異常:{}", ExceptionUtils.getStackTrace(e));
              }
          }
      });
  }
}

以上就是Mybatis-Plus集成Sharding-JDBC與Flyway實(shí)現(xiàn)多租戶(hù)分庫(kù)分表實(shí)戰(zhàn)的詳細(xì)內(nèi)容,更多關(guān)于Mybatis-Plus集成Sharding-JDBC的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Java利用Spire.XLS for Java實(shí)現(xiàn)刪除Excel指定行或列

    Java利用Spire.XLS for Java實(shí)現(xiàn)刪除Excel指定行或列

    在Java應(yīng)用中處理Excel數(shù)據(jù)是常見(jiàn)的任務(wù),而其中一項(xiàng)核心需求便是對(duì)工作表中的行或列進(jìn)行管理,本文將深入探討如何利用功能強(qiáng)大的Spire.XLS for Java庫(kù),輕松實(shí)現(xiàn)Excel行和列的刪除操作,需要的可以了解下
    2025-09-09
  • 詳解Spring加載Properties配置文件的四種方式

    詳解Spring加載Properties配置文件的四種方式

    這篇文章主要介紹了詳解Spring加載Properties配置文件的四種方式,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2018-05-05
  • spring boot實(shí)戰(zhàn)教程之shiro session過(guò)期時(shí)間詳解

    spring boot實(shí)戰(zhàn)教程之shiro session過(guò)期時(shí)間詳解

    這篇文章主要給大家介紹了關(guān)于spring boot實(shí)戰(zhàn)教程之shiro session過(guò)期時(shí)間的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起看看吧。
    2017-10-10
  • Redisson可重入鎖解鎖邏輯詳細(xì)講解

    Redisson可重入鎖解鎖邏輯詳細(xì)講解

    Redisson開(kāi)源框架是一個(gè)Redis的分布式鎖的現(xiàn)成實(shí)現(xiàn)方案,是Redis的java實(shí)現(xiàn)的客戶(hù)端。通過(guò)Netty支持非阻塞I/O。Redisson實(shí)現(xiàn)了分布式鎖的自動(dòng)續(xù)期機(jī)制、鎖的互斥自等待機(jī)制、鎖的可重入加鎖與釋放鎖的機(jī)制
    2023-02-02
  • js判斷是否含有GBK以外的字符的函數(shù)

    js判斷是否含有GBK以外的字符的函數(shù)

    判斷是否含有GBK以外的特殊字符
    2008-10-10
  • java實(shí)現(xiàn)基于UDP協(xié)議網(wǎng)絡(luò)Socket編程(C/S通信)

    java實(shí)現(xiàn)基于UDP協(xié)議網(wǎng)絡(luò)Socket編程(C/S通信)

    這篇文章主要介紹了java實(shí)現(xiàn)基于UDP協(xié)議網(wǎng)絡(luò)Socket編程(C/S通信),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-10-10
  • Java substring原理及使用方法實(shí)例

    Java substring原理及使用方法實(shí)例

    這篇文章主要介紹了Java substring原理及使用方法實(shí)例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-06-06
  • @PostConstruct在項(xiàng)目啟動(dòng)時(shí)被執(zhí)行兩次或多次的原因及分析

    @PostConstruct在項(xiàng)目啟動(dòng)時(shí)被執(zhí)行兩次或多次的原因及分析

    這篇文章主要介紹了@PostConstruct在項(xiàng)目啟動(dòng)時(shí)被執(zhí)行兩次或多次的原因及分析,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-08-08
  • Spring Boot 中 RestTemplate 的核心用法指南

    Spring Boot 中 RestTemplate 的核心用法指南

    本文詳細(xì)介紹了RestTemplate的使用,包括基礎(chǔ)用法、進(jìn)階配置技巧、實(shí)戰(zhàn)案例以及最佳實(shí)踐建議,通過(guò)一個(gè)騰訊地圖路線規(guī)劃的案例,展示了如何在實(shí)際項(xiàng)目中應(yīng)用RestTemplate進(jìn)行服務(wù)間通信,感興趣的朋友跟隨小編一起看看吧
    2025-12-12
  • SpringBoot+Vue實(shí)現(xiàn)數(shù)據(jù)添加功能

    SpringBoot+Vue實(shí)現(xiàn)數(shù)據(jù)添加功能

    這篇文章主要介紹了SpringBoot+Vue實(shí)現(xiàn)數(shù)據(jù)添加功能,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-03-03

最新評(píng)論

法库县| 九龙坡区| 磐石市| 象州县| 抚顺市| 阿坝县| 深圳市| 宁城县| 晋州市| 墨脱县| 武冈市| 高要市| 新余市| 文水县| 廉江市| 张家港市| 兴义市| 东乌珠穆沁旗| 五原县| 彭山县| 福清市| 桂林市| 读书| 苍溪县| 滦南县| 镇沅| 响水县| 宜良县| 宝鸡市| 湾仔区| 如东县| 青阳县| 霍山县| 临泉县| 宜兰县| 偏关县| 侯马市| 普格县| 介休市| 乐至县| 大城县|