在使用 JDBC 批量插入 YashanDB 时,有用户反馈触发器行为异常,明明是“语句级”触发器,却执行了 N 次。实际测试显示,在 executeBatch() 的场景下,语句级触发器会被重复触发,导致插入性能大幅下降。
本文将详细解析问题成因、验证方法与规避策略。
一、问题现象
某客户使用 JDBC 接口向表 A 插入数据:
表 A 上有一个语句级触发器(用于执行 alter sequence 等操作);
同时还存在一个行级触发器(用于赋值 sequence.nextval 或 current_timestamp)。
在插入 90 万条数据时,整个过程耗时高达 2.5 小时,性能远低于预期。
二、影响版本
该问题在以下版本均存在:
22.2.14.100 及以前版本
23.2.1.100 及以前版本
三、问题原因分析
经过内部排查,发现 YashanDB 在批量执行 SQL 时存在如下实现逻辑:
JDBC 的 executeBatch() 实际上会循环调用内部的 anlExecuteSingle(),导致语句级触发器被重复触发。
也就是说:
虽然你执行的是一条 batch 语句,数据库却当成多条语句分别执行,语句级触发器自然被重复触发了 N 次。
这显然不符合 SQL 标准行为,在 Oracle 中,语句级触发器只会在整批执行前或执行后触发一次。
四、验证方式
可使用如下方式进行验证:
建表并创建触发器:
drop table trig_test;
create table trig_test(t1 number, t2 number);
drop table flag;
create table flag(t number);
create or replace trigger trig_01
before insert or update on trig_test
begin
insert into flag values(1);
end;
Java 模拟批量插入代码:
public static void main(String[] args) {
try (Connection conn = DBUtil.getConn()) {
conn.setAutoCommit(false);
PreparedStatement ps = conn.prepareStatement("insert into trig_test(t1) values(?)");
for (int i = 0; i < 100; i++) {
ps.setInt(1. i);
ps.addBatch();
}
ps.executeBatch();
conn.commit();
} catch (Exception e) {
e.printStackTrace();
}
}
验证触发次数:
select count(*) from flag;
你会发现 flag 表里有 100 条记录,也就是语句级触发器被触发了 100 次。
五、风险与影响
六、解决方案与规避建议
推荐方式:避免使用语句级触发器与executeBatch搭配
将语句级触发器逻辑迁移至业务代码控制;
或使用普通 insert 循环,牺牲一点性能换行为稳定。
临时规避:切换为 PL/SQL 块执行
使用单条 SQL + 触发一次触发器逻辑,例如:
begin
for i in 1..100 loop
insert into trig_test(t1) values(i);
end loop;
end;
/
七、官方解决方案
此问题已被官方定位为 驱动/执行器的设计缺陷,后续版本中将修复触发器触发逻辑,使其更符合标准行为。
八、经验总结
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。