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

服務(wù)從mysql遷移達(dá)夢(mèng)數(shù)據(jù)庫(kù)的實(shí)現(xiàn)方案

 更新時(shí)間:2026年05月09日 11:04:21   作者:用戶911245902760  
達(dá)夢(mèng)數(shù)據(jù)庫(kù)(DM8)作為主流國(guó)產(chǎn)關(guān)系型數(shù)據(jù)庫(kù),兼容MySQL協(xié)議和大部分語(yǔ)法,這篇文章主要介紹了服務(wù)從mysql遷移達(dá)夢(mèng)數(shù)據(jù)庫(kù)的實(shí)現(xiàn)方案,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下

一、背景

根據(jù)信創(chuàng)國(guó)產(chǎn)適配要求,針對(duì)業(yè)務(wù)系統(tǒng)遷移至達(dá)夢(mèng)數(shù)據(jù)庫(kù),保持系統(tǒng)穩(wěn)定、可靠。 當(dāng)前系統(tǒng)技術(shù)棧:springboot+mybatis(3.5.9)+mybaits plus 我們采用的是攔截器方案,結(jié)合JavaAgent字節(jié)碼增強(qiáng)技術(shù),實(shí)現(xiàn)最小化代碼改動(dòng)。

二、遷移方案

  • 通過(guò)攔截器實(shí)現(xiàn)
  • 通過(guò)mybatis自帶的databaseId 多數(shù)據(jù)源機(jī)制

三、梳理現(xiàn)有使用方式

使用方式demoMapperXmldemoMapperAnnodemoMapperdemoBeanServicequeryWrap

四、方案實(shí)現(xiàn)

4.1 攔截器方式實(shí)現(xiàn)

主要實(shí)現(xiàn)兩個(gè)攔截器:

攔截器 作用 備注 mapper調(diào)用方法級(jí)別攔截器 攔截復(fù)雜sql的執(zhí)行方法 精確到單個(gè)方法的攔截,僅對(duì)達(dá)夢(mèng)不支持的方法進(jìn)行更改 sql函數(shù)攔截器 簡(jiǎn)單的函數(shù)替換例如(?group_concat->WM_CONCA )

4.1.1 方法攔截器

實(shí)現(xiàn)原理:

在MyBatis調(diào)用Mapper方法時(shí)進(jìn)行攔截 自動(dòng)將原方法調(diào)用重定向到對(duì)應(yīng)的DM版本方法 示例:

// 原方法
@Select("SELECT * FROM user WHERE id = #{id}")
User selectById(@Param("id") Long id); 
// DM專用方法(僅當(dāng)原SQL不兼容時(shí)需實(shí)現(xiàn))
@Select("SELECT * FROM user WHERE id = #{id}") // 修改后的DM兼容SQL
User selectByIdDM(@Param("id") Long id);

攔截邏輯:

檢查是否存在原方法名+DM后綴的方法 存在則調(diào)用DM版本方法,否則調(diào)用原方法

4.1.2 sql函數(shù)攔截器

對(duì)sql的進(jìn)行攔截并處理,處理簡(jiǎn)單函數(shù)轉(zhuǎn)換

例如:

group_concat → WM_CONCAT 遷移SQL分類(lèi)處理

類(lèi)別處理方式描述示例
無(wú)需更改直接執(zhí)行DM 數(shù)據(jù)庫(kù)原生兼容的 SQL 語(yǔ)法,無(wú)需任何轉(zhuǎn)換。SELECT * FROM employees WHERE id = 1;
僅替換函數(shù)函數(shù)攔截器處理僅需將源數(shù)據(jù)庫(kù)特有的函數(shù)名一對(duì)一映射為 DM 的等效函數(shù)。GROUP_CONCAT(name) → WM_CONCAT(name)
需要重寫(xiě)SQL方法攔截器 + DM 方法SQL 的語(yǔ)義或語(yǔ)法結(jié)構(gòu)不同,需用 DM 的特定語(yǔ)法重寫(xiě)整條語(yǔ)句。REPLACE INTO table ... → MERGE INTO table ...
重寫(xiě)SQL + 替換函數(shù)方法攔截器 + DM 方法復(fù)雜的 SQL 語(yǔ)句,其結(jié)構(gòu)需要重寫(xiě),并且其中包含需要替換的特殊函數(shù)。包含 GROUP_CONCAT 和 LIMIT 分頁(yè)的復(fù)雜查詢,需重寫(xiě)為使用 WM_CONCAT 和 ROWNUM 的 DM 語(yǔ)法。

代碼整體情況

方法攔截器代碼

import cn.hutool.json.JSONUtil;
import java.util.Properties;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.cache.CacheKey;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
 
// 定義攔截器簽名
@Intercepts({
        @Signature(
                type = Executor.class,
                method = "update",
                args = {MappedStatement.class, Object.class}),
         @Signature(
        type = Executor.class,
        method = "query",
        args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),
        @Signature(
                type = Executor.class,
                method = "query",
                args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class})
})
//@Component
@Slf4j
public class MethodInterceptor implements Interceptor {
  @Value("${task.dbType:mysql}")
  private String dbType; // 數(shù)據(jù)庫(kù)類(lèi)型配置
 
  @Override
  public Object plugin(Object target) {
    return Plugin.wrap(target, this); // 包裝目標(biāo)對(duì)象
  }
 
  @Override
  public void setProperties(Properties properties) {} // 設(shè)置屬性方法
 
  @Override
  public Object intercept(Invocation invocation) throws Throwable {
    System.out.println("intercept-開(kāi)始執(zhí)行方法-" + invocation.getMethod().getName());
    // 獲取StatementHandler
    Object[] args = invocation.getArgs(); // 方法參數(shù)
    MappedStatement ms = (MappedStatement) args[0]; // 映射語(yǔ)句
    String methodId = ms.getId(); // 方法ID
    RowBounds rowBounds = (RowBounds)args[2];
    // 如果是DM數(shù)據(jù)庫(kù)且方法名不帶Dm后綴
    if ("dm".equals(dbType) && !methodId.endsWith("DM")) {
      try {
        // 嘗試查找對(duì)應(yīng)的Dm方法
        Configuration configuration = ms.getConfiguration();
        // 處理 namespace 邏輯
        String fullMethodId = methodId;
        String noFullMethodId = methodId;
        if (!methodId.contains(".")) {
          // 如果不包含點(diǎn)(.),說(shuō)明是簡(jiǎn)短的方法ID,需要加上namespace
          String namespace = ms.getResource().replace(".", "/"); // 獲取namespace
          fullMethodId = namespace + "." + methodId;
        }else{
           noFullMethodId = methodId.substring(methodId.lastIndexOf(".") + 1);
        }
 
        String dmFullMethodId = fullMethodId + "DM";
        String dmNoFullMethodId = noFullMethodId + "DM";
        String dmMethodId = dmFullMethodId;
        MappedStatement dmMs = getDmMappedStatement(configuration, dmFullMethodId);
        if(dmMs == null){
          dmMethodId = dmNoFullMethodId;
          dmMs = getDmMappedStatement(configuration, dmNoFullMethodId);
        }
        Object parameter = args[1];
        // 如果找到Dm方法則替換執(zhí)行
        if (dmMs != null) {
          BoundSql newBoundSql = dmMs.getBoundSql(parameter);
          log.info("intercept執(zhí)行的方法是-" + dmMethodId);
          args[0] = dmMs; // 替換為DM方法
          if (args.length == 4) {
            args[2] = newBoundSql;
          } else {
            args[5] = newBoundSql;
          }
 
        }
      } catch (Exception e) {
        log.error("intercept-沒(méi)有找到對(duì)應(yīng)的Dm方法-繼續(xù)執(zhí)行原方法-或嘗試使用sql替換-" + methodId,e);
      }
    }
    Object proceed = invocation.proceed(); // 執(zhí)行攔截方法
    log.info(JSONUtil.parse(proceed).toStringPretty()); // 記錄執(zhí)行結(jié)果
    return proceed;
  }
  public MappedStatement getDmMappedStatement(Configuration configuration, String methodId) {
    return configuration.getMappedStatement(methodId);
  }
}

sql攔截器代碼

@Intercepts({
        @Signature(
                type = StatementHandler.class,
                method = "prepare",
                args = {Connection.class, Integer.class})
})
@Log
public class ReWriteInterceptor implements Interceptor {
  @Override
  public Object intercept(Invocation invocation) throws Throwable {
    // 獲取 StatementHandler
    StatementHandler handler = (StatementHandler) invocation.getTarget();
    BoundSql boundSql = handler.getBoundSql();
    String originalSql = boundSql.getSql();
    log.info("OriginalSql: " + originalSql.replaceAll("\n"," "));
    String rewrittenSql = rewriteSql(originalSql);
 
    // 用反射重新設(shè)置 SQL
    Field sqlField = boundSql.getClass().getDeclaredField("sql");
    sqlField.setAccessible(true);
    sqlField.set(boundSql, rewrittenSql);
    Object proceed = invocation.proceed();
    return proceed;
  }
 
  private String rewriteSql(String originalSql) {
    // mysql IFNULL(col1,col2) => NVL(col1,col2) dm
      originalSql = originalSql.replaceAll("(?i)GROUP_CONCAT", "WM_CONCAT");
      originalSql = originalSql.replaceAll("(?i)!JSON_CONTAINS", " not JSON_CONTAINS ");
      originalSql = originalSql.replaceAll("(?i)INSERT\\s+IGNORE\\s+INTO", "INSERT INTO ");
      originalSql = convertCastToToChar(originalSql);
      log.info("OriginalSql: " + originalSql+"\nRewrittenSql: " + originalSql);
      return originalSql;
  }
 
  /**
   * 將cast(... as char)轉(zhuǎn)換為to_char(...)格式
   * @param sql
   * @return {@link String}
   */
  public static String convertCastToToChar(String sql) {
    // 正則表達(dá)式匹配CAST(... AS CHAR)模式
    Pattern pattern = Pattern.compile(
            "CONVERT\\s*\\(\\s*(.+?)\\s*,\\s*(?:CHAR|VARCHAR|VARCHAR2|TEXT)\\s*(?:\\(\\d+\\))?\\s*\\)",
            Pattern.CASE_INSENSITIVE
    );
 
    Matcher matcher = pattern.matcher(sql);
    StringBuffer result = new StringBuffer();
 
    while (matcher.find()) {
      // 獲取CAST內(nèi)部的表達(dá)式
      String innerExpression = matcher.group(1).trim();
      // 替換為T(mén)O_CHAR格式
      matcher.appendReplacement(result, "TO_CHAR(" + innerExpression + ")");
    }
    matcher.appendTail(result);
 
    return result.toString();
  }

mybatis2dmAutoConfiguration 代碼

@Configuration
@ConditionalOnExpression("'${spring.datasource.url}'.startsWith('jdbc:dm:')")//必須是達(dá)夢(mèng)的驅(qū)動(dòng)才生效
public class mybatis2dmAutoConfiguration {
    @Bean
    public DmMethodRouterInterceptor dmMethodRouterInterceptor() {
        return new DmMethodRouterInterceptor();
    }
    @Bean
    public ReWriteInterceptor reWriteInterceptor() {
        return new ReWriteInterceptor();
    }
    @Bean
    public ConfigurationCustomizer mybatisConfigurationCustomizer() {
        return configuration -> {
            configuration.addInterceptor(new DmMethodRouterInterceptor());
            configuration.addInterceptor(new ReWriteInterceptor());
        };
    }
}

4.2 通過(guò)mybatis自帶的databaseId 多數(shù)據(jù)源機(jī)制

話不多說(shuō),直接上代碼。

MyBatisConfig 代碼

@Configuration
public class MyBatisConfig {
    
    @Bean
    public DatabaseIdProvider databaseIdProvider() {
        VendorDatabaseIdProvider databaseIdProvider = new VendorDatabaseIdProvider();
        Properties properties = new Properties();
        properties.setProperty("MySQL", "mysql");
        properties.setProperty("DM DBMS", "dm");
        databaseIdProvider.setProperties(properties);
        return databaseIdProvider;
    }
}

xml代碼

<!-- 通用SQL(所有數(shù)據(jù)庫(kù)都會(huì)嘗試使用) -->
<select id="selectUser" resultType="User">
  SELECT * FROM user
</select>
 
<!-- MySQL專用SQL(僅MySQL使用) -->
<select id="selectUser" resultType="User" databaseId="mysql">
  SELECT * FROM user LIMIT 1
</select>
 
<!-- DM專用SQL(僅達(dá)夢(mèng)使用) -->
<select id="selectUser" resultType="User" databaseId="dm">
  SELECT TOP 1 * FROM user
</select>

在運(yùn)行代碼過(guò)程中,會(huì)根據(jù)databaseId來(lái)選擇數(shù)據(jù)源運(yùn)行對(duì)象的sql。

方案對(duì)比

方案特性攔截器方式原生 MyBatis DatabaseId
實(shí)現(xiàn)原理通過(guò)自定義 MyBatis 攔截器(及JavaAgent字節(jié)碼增強(qiáng))動(dòng)態(tài)解析和重寫(xiě)SQL依賴 MyBatis 內(nèi)置的 <databaseIdProvider> 和 XML 中 databaseId 屬性
支持的映射方式
- XML 映射文件 (demoMapperXml)? 支持? 支持
- 注解方式 (demoMapperAnno)? 支持不支持
- MyBatis-Plus 等擴(kuò)展 (demoMapper)? 支持不支持
- Service/Bean 中的直接SQL (demoBeanService)? 支持不支持
- 查詢包裝器 (queryWrap)? 支持不支持
靈活性:可處理任意位置的SQL,支持復(fù)雜邏輯和函數(shù)替換:僅限XML中定義的語(yǔ)句,無(wú)法處理運(yùn)行時(shí)生成的SQL
侵入性低:對(duì)業(yè)務(wù)代碼基本無(wú)侵入,通過(guò)配置和字節(jié)碼增強(qiáng)實(shí)現(xiàn)中:需要在所有XML語(yǔ)句中顯式定義 databaseId 屬性
維護(hù)成本集中維護(hù)攔截規(guī)則,一改全改分散維護(hù),每個(gè)XML語(yǔ)句都需要適配不同數(shù)據(jù)庫(kù)的版本

實(shí)施步驟

  • 準(zhǔn)備工作: 對(duì)SQL及其mapper方法進(jìn)行分類(lèi)標(biāo)注
  • 開(kāi)發(fā)攔截器: 實(shí)現(xiàn)方法級(jí)別攔截邏輯 配置函數(shù)替換規(guī)則
  • JavaAgent集成: 打包攔截器為JavaAgent 配置JVM啟動(dòng)參數(shù)加載Agent
  • 部署上線: 添加JavaAgent到生產(chǎn)環(huán)境啟動(dòng)參數(shù) 代碼在最后面,具體的工程我后續(xù)會(huì)上傳至:

github.com/qqinc/mybat…

優(yōu)勢(shì)

最小化代碼改動(dòng):僅需為不兼容的方法添加DM版本 非侵入式:通過(guò)字節(jié)碼增強(qiáng)避免直接修改業(yè)務(wù)代碼 靈活可控:可精確控制哪些方法需要特殊處理 易于維護(hù):DM相關(guān)修改集中管理,與主邏輯分離 補(bǔ)充點(diǎn): 事務(wù)都是支持的

最終實(shí)現(xiàn)代碼

整體工程結(jié)構(gòu)

RewriteAgent

public class RewriteAgent {
    public static void premain(String agentArgs, Instrumentation inst) {
        System.out.println("mysql2dm agent start");
 
        AgentBuilder agentBuilder = new AgentBuilder.Default()
                .with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION)
                .with(AgentBuilder.Listener.StreamWriting.toSystemOut().withTransformationsOnly());
 
// 攔截 BaseExecutor.query 和 update
        agentBuilder = agentBuilder
                .type(ElementMatchers.named("org.apache.ibatis.executor.BaseExecutor"))
                .transform((builder, typeDescription, classLoader, module) ->
                        builder
                                .method(ElementMatchers.named("query").or(ElementMatchers.named("update")))
                                .intercept(Advice.to(ExecutorAdvice.class))
                )
 
                // ?? 注意:下面這個(gè)攔截器是追加,而不是覆蓋上面
                .type(ElementMatchers.named("org.apache.ibatis.executor.statement.RoutingStatementHandler"))
                .transform((builder, typeDescription, classLoader, module) ->
                        builder
                                .method(ElementMatchers.named("prepare"))
                                .intercept(Advice.to(SqlRewriteAdvice.class))
                );
        agentBuilder.installOn(inst);
    }
}

BoundSqlHolder

public class BoundSqlHolder {
    private static final ThreadLocal<BoundSql> holder = new ThreadLocal<>();
 
    public static void setBoundSql(BoundSql boundSql) {
        holder.set(boundSql);
    }
 
    public static BoundSql getBoundSql() {
        return holder.get();
    }
 
    public static void clear() {
        holder.remove();
    }
}

ExecutorAdvice

public class ExecutorAdvice {
    @Advice.OnMethodEnter
    public static void enter(@Advice.AllArguments Object[] args) {
        try {
            MappedStatement originalMs = (MappedStatement) args[0];
            String methodId = originalMs.getId();
            if (methodId != null && !methodId.endsWith("DM")) {
                Configuration config = originalMs.getConfiguration();
                String dmMethodId = methodId + "DM";
 
                if (config.hasStatement(dmMethodId)) {
                    MappedStatement dmMs = config.getMappedStatement(dmMethodId);
                    System.out.println("[ExecutorAdvice] 替換為 DM 方法: " + dmMethodId);
                    args[0] = dmMs;
                    Object parameter = args[1];
                    BoundSql newBoundSql = dmMs.getBoundSql(parameter);
                    BoundSqlHolder.setBoundSql(newBoundSql);
                    System.out.println("[ExecutorAdvice] 替換后的 SQL: " + newBoundSql.getSql());
                }
            }
        } catch (Exception e) {
            System.err.println("[ExecutorAdvice] 替換失敗: " + e.getMessage());
        }
    }
}

SqlRewriteAdvice

public class SqlRewriteAdvice {
    @Advice.OnMethodEnter
    public static void enter(@Advice.This Object handlerObj,
                             @Advice.Origin String method,
                             @Advice.AllArguments Object[] args) {
        try {
            StatementHandler handler = (StatementHandler) handlerObj; // 手動(dòng)強(qiáng)轉(zhuǎn)(運(yùn)行時(shí))
            Field delegateField = handler.getClass().getDeclaredField("delegate");
            delegateField.setAccessible(true);
            StatementHandler realHandler = (StatementHandler) delegateField.get(handler);
            BoundSql boundSql = handler.getBoundSql();
            BoundSql newBoundSql = BoundSqlHolder.getBoundSql();
            if (newBoundSql != null) {
                String originalSql = newBoundSql.getSql();
                System.out.println("Original SQL: " + originalSql);
                // 重寫(xiě) SQL
                String rewrittenSql = rewriteSql(originalSql);
                Field boundSqlField = realHandler.getClass().getSuperclass().getDeclaredField("boundSql");
                boundSqlField.setAccessible(true);
                boundSqlField.set(realHandler, newBoundSql);
                System.out.println("Rewritten SQL: " + rewrittenSql);
            }else {
                String originalSql = boundSql.getSql();
                System.out.println("Original SQL: " + originalSql);
                String rewrittenSql = rewriteSql(originalSql);
                Field sqlField = boundSql.getClass().getDeclaredField("sql");
                sqlField.setAccessible(true);
                sqlField.set(boundSql, rewrittenSql);
                System.out.println("Rewritten SQL: " + rewrittenSql);
            }
 
        } catch (Exception e) {
            System.out.println("SQL Rewrite Error: " + e);
 
        }finally {
            // 清理,防止內(nèi)存泄漏
            BoundSqlHolder.clear();
        }
    }
    public static String rewriteSql(String originalSql) {
        originalSql = originalSql.replaceAll("(?i)GROUP_CONCAT", "WM_CONCAT");
        originalSql = originalSql.replaceAll("(?i)!JSON_CONTAINS", " not JSON_CONTAINS ");
        originalSql = originalSql.replaceAll("(?i)INSERT\\s+IGNORE\\s+INTO", "INSERT INTO ");
        originalSql = originalSql.replaceAll("(?i)GROUP_CONCAT", "WM_CONCAT");
        return originalSql;
    }

在啟動(dòng)方法參數(shù)上添加:

-javaagent:E:\\cbimWorkSpace\\mybatis2dm-agent\\target\\mybatis2dm-agent-1.0-SNAPSHOT.jar

完結(jié),撒花!

總結(jié)

到此這篇關(guān)于服務(wù)從mysql遷移達(dá)夢(mèng)數(shù)據(jù)庫(kù)的文章就介紹到這了,更多相關(guān)mysql遷移達(dá)夢(mèng)數(shù)據(jù)庫(kù)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • MySQL外鍵類(lèi)型及應(yīng)用場(chǎng)景總結(jié)

    MySQL外鍵類(lèi)型及應(yīng)用場(chǎng)景總結(jié)

    這篇文章主要介紹了?MySQL?外鍵的類(lèi)型(RESTRICT、CASCADE、SET?NULL、NO?ACTION)及其應(yīng)用場(chǎng)景、優(yōu)缺點(diǎn)和使用注意事項(xiàng),通過(guò)創(chuàng)建和測(cè)試外鍵,闡述了不同類(lèi)型外鍵在主表刪除或更新數(shù)據(jù)時(shí)子表的變化,需要的朋友可以參考下
    2024-12-12
  • MySQL 鎖機(jī)制從原理到實(shí)踐深度解析

    MySQL 鎖機(jī)制從原理到實(shí)踐深度解析

    MySQL 作為主流關(guān)系型數(shù)據(jù)庫(kù),其鎖機(jī)制設(shè)計(jì)精巧卻也復(fù)雜,理解鎖的工作原理對(duì)解決并發(fā)問(wèn)題至關(guān)重要,這篇文章主要介紹了MySQL 鎖機(jī)制從原理到實(shí)踐深度解析,本文結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧
    2025-11-11
  • 解決JDBC的class.forName()問(wèn)題

    解決JDBC的class.forName()問(wèn)題

    這篇文章主要介紹了關(guān)于JDBC的class.forName()問(wèn)題,比較兩個(gè)Java文件可見(jiàn),連接Db2和連接MySQL的方式非常類(lèi)似,唯一的區(qū)別在于,調(diào)用?DriverManager.getConnection()?方法時(shí),傳入的URL不同,本文給大家詳細(xì)講解,需要的朋友參考下
    2022-09-09
  • MySQL學(xué)習(xí)之DDL數(shù)據(jù)庫(kù)定義與操作

    MySQL學(xué)習(xí)之DDL數(shù)據(jù)庫(kù)定義與操作

    本文詳細(xì)介紹SQL中DDL的數(shù)據(jù)庫(kù)操作,包括查詢、創(chuàng)建、刪除數(shù)據(jù)庫(kù)和表的操作,以及修改表結(jié)構(gòu)等功能,通過(guò)這些操作,讀者可以深入了解如何使用SQL進(jìn)行數(shù)據(jù)庫(kù)管理和維護(hù),需要的朋友可以參考下
    2024-11-11
  • mysql注入之長(zhǎng)字符截?cái)?orderby注入,HTTP分割注入,limit注入方式

    mysql注入之長(zhǎng)字符截?cái)?orderby注入,HTTP分割注入,limit注入方式

    這篇文章主要介紹了mysql注入之長(zhǎng)字符截?cái)?orderby注入,HTTP分割注入,limit注入方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-11-11
  • MySQL批量處理圖片URL統(tǒng)一去掉域名前綴的方法

    MySQL批量處理圖片URL統(tǒng)一去掉域名前綴的方法

    文章介紹了如何使用MySQL 8.0的新函數(shù)REGEXP_REPLACE()批量處理圖片URL,去掉域名前綴,將所有圖片路徑統(tǒng)一存為相對(duì)路徑,從而避免路徑重復(fù)和加載錯(cuò)誤,需要的朋友可以參考下
    2025-11-11
  • MySql中的索引失效、回表解析問(wèn)題

    MySql中的索引失效、回表解析問(wèn)題

    這篇文章主要介紹了MySql中的索引失效、回表解析問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2025-05-05
  • mysql 搜尋附近N公里內(nèi)數(shù)據(jù)的簡(jiǎn)單實(shí)例

    mysql 搜尋附近N公里內(nèi)數(shù)據(jù)的簡(jiǎn)單實(shí)例

    下面小編就為大家?guī)?lái)一篇mysql 搜尋附近N公里內(nèi)數(shù)據(jù)的簡(jiǎn)單實(shí)例。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-04-04
  • SQL?INSERT及批量的幾種方式總結(jié)

    SQL?INSERT及批量的幾種方式總結(jié)

    SQL提供了INSERT語(yǔ)句,用于將一行或多行插入表中,下面這篇文章主要給大家介紹了關(guān)于SQL?INSERT及批量的幾種方式,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2023-02-02
  • Mysql外鍵約束的創(chuàng)建與刪除的使用

    Mysql外鍵約束的創(chuàng)建與刪除的使用

    本文主要介紹了Mysql外鍵約束的創(chuàng)建與刪除的使用,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-03-03

最新評(píng)論

元谋县| 大足县| 开原市| 洛阳市| 怀柔区| 清涧县| 东兴市| 林州市| 浦城县| 太湖县| 宁蒗| 大关县| 札达县| 容城县| 镇江市| 保山市| 宁明县| 沙雅县| 松溪县| 宜春市| 房山区| 阳原县| 高邑县| 焦作市| 九台市| 红桥区| 长宁县| 罗平县| 瓮安县| 万盛区| 崇州市| 肇庆市| 台南县| 江城| 连州市| 兴宁市| 维西| 宁都县| 会泽县| 廊坊市| 都江堰市|