sybn sybn-util 项目说明文档 - 基于java的跨数据库联合查询

execBatch 批量操作

2022-08-22
sybn

某些场景下, 需要批量执行某些 sql, 其中 sql 中表名或者字段名是变量。

原生sql中, 一般使用存储过程实现。 本工具为了满足此场景, 引入了 execBatch 批量操作。

说明

  • SQL
-- 前置sql, 列出所有表名
show tables;
-- 单线程批量操作, 获取所有表的表结构
execBatch(show create table @tables);

```sql
-- 前置sql, 列出所有表名
show tables;
-- 多线程批量操作, 获取所有表的表结构, 如果单个任务失败不影响其他任务继续执行
execBatch(show create table @tables) ON concurrent=5 and ignore_error=true;
  • 语法

execBatch(任意sql可以使用@变量名和${变量名}) [ON concurrent=5 and ignore_error=true and safe_params=(ip) and on_error_query=(select ‘${ip}查询失败’ as msg)];

  • 参数 V:0.3.25
参数 必填 默认值 举例 说明
concurrent 否 1 5 并发数
ignore_error 否 false true 是否忽略异常继续执行其他任务
safe_params 否 - ip 允许不加引号引入sql的变量列表
on_error_query 否 - select ‘${ip}查询失败’ as msg 子任务失败时时执行的sql
on_empty_query 否 - select ‘${ip}查询为空’ as msg 子任务为空时时执行的sql
  • 变量

允许使用 ${}, #{}, $@{} 变量, 它们都能引用变量, 但 @ 的个数决定变量被替换的层级(延迟层级), 详见下方「延迟变量(多级 @)的处理位置」; 引用时忽略外层单引号 ( ${} 与 ‘${}’ 等效 ).

默认除了safe_params以外, 所有的值都会强制嵌套外层单引号吗, 且内部的单引号会转移成两个单引号. (xxx = what’s 则 ${xxx} = ‘what’’s’)

-- xxx = what's 则 ${xxx} = 'what''s'

-- 推荐使用 '${xxx}' 表示变量名, 比如:
execBatch(select '${xxx}' as a)
-- 执行: select 'what''s' as a

-- 以下写法等效
execBatch(select ${xxx} as a)
execBatch(select '#{xxx}' as a)
-- 执行: select 'what''s' as a

-- 如果在 mybatis 环境下使用, ${} 和 #{} 会被 mybatis处理并报错, 可用 '$@{xxx}' 作延迟变量
execBatch(select '$@{xxx}' as a)
-- 执行: select 'what''s' as a

-- 以下写入有单引号问题
-- xxx = what's
execBatch(select '${xxx} you name' as a)
-- 执行: select 'what''s' as a
-- 错误: select ''what''s'you name' as a 引号不配对

-- safe_params 可以是一个或多个值
execBatch(select '${xxx} you name' as a) on safe_params=aaa
execBatch(select '${xxx} you name' as a) on safe_params=(aaa, bbb)

延迟变量(多级 @)的处理位置

execBatch / dynamicSourceQuery 的子 sql 会在「mybatis 接口参数 → execBatch 解析阶段 → 每行 data 阶段 → 内层嵌套命令 → DSQ 引擎」之间流转。 变量前每多一个 @,表示多延迟一层才被真正替换。这样设计是为了:

  • 在 mybatis / http 接口环境下,${x} 会被 mybatis 抢先吃掉而报错,用 $@{x} 可逃过该层;
  • 在嵌套 execBatch 时,避免外层把内层才该用的变量提前替换掉。

两类变量都遵守「@ 个数 = 延迟层数」

写法 类型 含义
${x} / #{x} $ 变量(mybatis 风格) 字符串值会被套引号;在 mybatis 接口最先被替换
$@{x} $ 变量,延迟 1 层 逃过 mybatis,到第 1 层才生效
$@@{x} $ 变量,延迟 2 层 逃过前两层
$@@@{x} $ 变量,延迟 3 层 用于 execBatch 内再嵌 execBatch/DSQ
@item / @aa @ 变量(行变量) 由 ReplaceAtValueUtil 替换,取自每行 data,不加引号
@$item @ 变量,延迟 1 层 第 1 层降为 @item
@$$item @ 变量,延迟 2 层 第 2 层降为 @item

源码只会逐级降级($@{→${、$@@{→$@{…;@$→@、@$$→@$…),不会跨级跳。写错 @ 个数就会在错误的层级被替换(或残留不替换)。

各功能在哪些函数里处理延迟变量

层级 处理位置(源码) 降级动作 实际替换(变量来源)
第 0 层(入口) dbutil-dao SqlAndParams:ReplaceSqlUtil.replaceMybatisParam(mybatis 参数) 无(这就是最早吃 ${} 的地方) 接口传入的 mybatis 参数
第 1 层 SySqlExecBatchQueryExecutor.getBashSql(181-182 行)+ replaceMybatisParamNew(session.getQueryValue()) 降一级 $@{→${、@$→@ session 变量(getQueryValue(),含延迟键)
第 2 层 SySqlExecBatchQueryExecutor.getAndReplaceSubSql(543-544 行)+ replaceMybatisParamNew(data) + ReplaceAtValueUtil.replaceStr(data) + session.getQueryValueIgnoreLaggedKeys() 再降一级 每行 data(@item/@aa/${行字段})+ session(排除 sybn_lagged_*、sybn_last_* 等延迟变量)
第 3 层 子 sql 被 SySqlEntityFactory.create 重新解析为内层 execBatch/dynamicSourceQuery,其各自的 getBashSql/getAndReplaceSubSql(或 SySqlDynamicSourceQuery*Engine.getAndReplaceSubSql,144/159-160 行)再降一级 再降一级 内层命令自己的 bash/item 阶段
  • dynamicSourceQuery(含 SySqlDynamicSourceQuerySySqlEngine / DirectSqlEngine / RedisSqlEngine):只有一次降级(相当于第 1 层),之后用 replaceAtStr(session.getQueryValueIgnoreLaggedKeys()) 替换,且排除延迟键。它不消费每行 data,所以 DSQ 里想引用 @ 变量要在第 1 层前写好。
  • cacheResult / peekResult 自身不做 $@{} 降级,只用 ReplaceSqlUtil.replaceMybatisParamNew(session.getQueryValue()) + @list} 处理(见 SySqlCacheResultQueryExecutor 136-142 行)。若 cacheResult 子 sql 内含 execBatch/DSQ,延迟变量交给内层命令处理。

session 的「延迟键」机制(lagged)

SySqlQuerySession.getQueryValueIgnoreLaggedKeys() 会剔除 sybn_lagged_*、sybn_last_*(@sybn_last_select@table 等)及 sybn_lagged_variables 列表中的键。

  • 第 1 层 getBashSql 用 getQueryValue()(含这些键),第 2 层用 getQueryValueIgnoreLaggedKeys()(不含)。
  • 因此 @sybn_last_select@table 这类中间结果表引用,要等到第 2 层(item 阶段)才被替换为真实数据,不会在第 1 层被提前吃掉。这正是 SySqlExecBatchQueryExecutorTest#bug20250926Test 验证的行为(execBatch 内 union all select * from @sybn_last_select@table 取到的是内层 b=3 而非外层 b=2)。

选型速查

需求 写法
变量来自 mybatis 接口参数 直接 ${x}(第 0 层替换)
变量来自 session,在 execBatch 解析阶段就要用 ${x}(第 1 层)
变量想延迟到每行 data 才替换(如按行表名) @item/@aa(推荐)或 ${x}/$@{x}(需 session 无同名 key,否则第 1 层被 session 顶掉);要强制延迟到第 2 层用 $@@{x}
变量想延迟到嵌套内层命令才替换 $@@{x} / $@@@{x}(按嵌套深度加 @)
按行 @item/@aa 想延迟一级 写 @$item / @$aa

注意:$${} 是另一种「二级变量」(来自 ServerSettingUtil 的全局配置,见 ReplaceSqlUtil),与 $@{} 延迟语法无关,勿混淆。

相关测试类

  • sybn-core/.../executor/SySqlExecBatchQueryExecutorTest#bug20250926Test:验证 @sybn_last_select@table 在 execBatch 内延迟到 item 阶段取正确值。
  • sybn-core/.../finder/ConvertSySqlExecBatchQueryTest:验证 @aa(每行 data)与 #{t}(session)在 execBatch 中替换。
  • sybn-core/.../part/util/ReplaceSqlUtilTest:$${} 全局配置变量替换。
  • sybn-jdbc-driver/.../dql/SySqlDynamicSourceQueryTest:验证 @host/@port 在 DSQ 的 on 配置中替换。
  • 当前没有专门覆盖 $@{x} / $@@{x} 字符串降级的单元测试,改动降级逻辑时建议补一个回归测试。

Similar Posts

Comments

暂不开放评论! 可微信联系