You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在JMeter中使用JSR223处理参数化SQL查询遇问题求助

JMeter JSR223(Groovy)SQL文件处理问题解决

问题背景

在JMeter中使用JSR223 Sampler(Groovy)从分号分隔的键值对文件读取SQL查询并预处理,替代之前bash脚本传参时的字符串转义问题。核心需求:

  • 从JMeter属性(如user.properties中的SQL_FILE)读取SQL文件路径
  • 提取文件中的query键对应的SQL语句,用其余键值对替换语句中的占位符(如$name替换为John Doe)
  • 格式化SQL:将换行符转为空格、合并多空格为单个空格
  • 将处理后的SQL存入JMeter变量SQL_QUERY
  • 基于原SQL文件名(不含扩展名)和时间戳(格式:hhmmss_MMDDYYYY)生成唯一结果文件名

当前问题

  1. 占位符替换未生效,例如$name这类占位符未被替换为对应值
  2. 生成的结果文件名包含完整路径,仅需保留无扩展名的基础文件名

修正后的Groovy代码

import org.apache.jmeter.util.JMeterUtils
import java.nio.file.*
import java.text.SimpleDateFormat

// 格式化SQL:换行转空格、合并多空格、去除首尾空白
String cleanQuery(String query) {
    return query.replaceAll("[\r\n]+", " ").replaceAll("\\s+", " ").trim()
}

// 替换SQL中的占位符($key格式)
String substitutePlaceholders(String query, Map<String, String> properties) {
    properties.each { key, value ->
        if (key != "query") { // 跳过SQL语句本身的键
            query = query.replace("\$" + key, value)
        }
    }
    return query
}

// 生成唯一结果文件名:时间戳_原文件基础名.jtl
String generateResultsFilename(String sqlFilePath) {
    if (sqlFilePath == null || sqlFilePath.trim().isEmpty()) {
        throw new IllegalArgumentException("SQL文件路径为空或未提供")
    }
    
    // 获取时间戳(格式:HHmmss_MMddyyyy)
    String timestamp = new SimpleDateFormat("HHmmss_MMddyyyy").format(new Date())
    // 提取原文件名(不含路径)
    String fileName = Paths.get(sqlFilePath).getFileName().toString()
    // 提取基础文件名(不含扩展名)
    String baseName = fileName.replaceAll(/\.[^.]*$/, "")
    
    // 拼接最终文件名
    return "${timestamp}_${baseName}.jtl"
}

// 从JMeter属性读取SQL文件路径
String sqlFilePath = JMeterUtils.getPropDefault("SQL_FILE", "")
if (sqlFilePath == null || sqlFilePath.trim().isEmpty()) {
    throw new IllegalArgumentException("JMeter属性中未配置SQL_FILE参数")
}

try {
    // 读取文件内容
    String fileContent = new String(Files.readAllBytes(Paths.get(sqlFilePath)), "UTF-8").trim()
    
    // 解析分号分隔的键值对
    Map<String, String> properties = fileContent.split(";").collectEntries { entry ->
        def parts = entry.split("=", 2)
        parts.length == 2 ? [(parts[0].trim()): parts[1].trim()] : [:]
    }
    
    // 提取SQL语句
    String sqlQuery = properties.get("query", "").trim()
    if (sqlQuery.isEmpty()) {
        throw new IllegalArgumentException("SQL文件中未找到query键:${sqlFilePath}")
    }
    
    // 打印调试信息
    log.info("原始SQL:${sqlQuery}")
    properties.each { key, value ->
        if (key != "query") log.info("占位符${key} = ${value}")
    }
    
    // 格式化SQL
    sqlQuery = cleanQuery(sqlQuery)
    log.info("格式化后SQL:${sqlQuery}")
    
    // 替换占位符
    sqlQuery = substitutePlaceholders(sqlQuery, properties)
    log.info("最终SQL:${sqlQuery}")
    
    // 存储到JMeter变量
    vars.put("SQL_QUERY", sqlQuery)
    log.info("SQL已成功处理并存入变量SQL_QUERY")
    
    // 生成结果文件名并存储
    String resultsFile = generateResultsFilename(sqlFilePath)
    vars.put("RESULTS_FILE", resultsFile)
    log.info("结果文件名将使用:${resultsFile}")
    
} catch (Exception e) {
    log.error("处理SQL文件出错:${sqlFilePath}", e)
    throw new RuntimeException("处理SQL文件失败:${sqlFilePath}", e)
}

关键修正说明

  1. 占位符替换修复:

    • 修正了SQL语句提取逻辑,从读取sqlQuery键改为读取需求中的query键
    • 保留了$key格式的字符串替换逻辑,确保占位符能被正确匹配替换
  2. 结果文件名修复:

    • 移除了文件名中的路径部分,仅保留时间戳、原文件基础名(不含扩展名)和.jtl后缀
    • 简化了文件名处理逻辑,直接提取原文件的基础名称,避免路径干扰

内容的提问来源于stack exchange,提问作者Gajendra Shete

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 23:10:58