jOOQ batchInsert()结合AFTER INSERT触发器时行为不符合预期
问题描述
我在sku表上创建了一个AFTER INSERT触发器,用于在插入数据后向sku_price_history表插入记录:
DELIMITER $$ CREATE TRIGGER trig_after_sku_insert AFTER INSERT ON sku FOR EACH ROW BEGIN INSERT INTO sku_price_history(sku_id, action_type, purchase_price, selling_price, base_margin_rate, vat, vat_type, tax_type, selling_price_started_at, updated_by) VALUES (NEW.id, 'CREATE', NEW.purchase_price, NEW.selling_price, NEW.base_margin_rate, NEW.vat, NEW.vat_type, NEW.tax_type, NOW(), NEW.created_by); END $$ DELIMITER ;
之后我用jOOQ的batchInsert()方法做批量插入:
void saveMany(List<SkuDto.SaveBody> skus) { List<SkuRecord> skuRecords = new ArrayList<>(); for(SkuDto.SaveBody sku : skus) { SkuRecord skuRecord = ctx.newRecord(SKU, sku); skuRecords.add(skuRecord); } ctx.batchInsert(skuRecords).execute(); }
问题现象
传入多条记录后,只有第一条数据插入到了sku表,没有抛出任何异常。从LoggerListener日志能看到批量大小是正确的:
DEBUG 65174 --- [nio-9292-exec-1] org.jooq.tools.LoggerListener : Executing batch query : sql query DEBUG 65174 --- [nio-9292-exec-1] org.jooq.tools.LoggerListener : Batch size : 3
已尝试的操作
- 删除触发器后执行相同代码,批量插入正常,确认触发器是问题根源。
- 用SQL客户端模拟事务内批量插入:
SET autocommit = OFF; START TRANSACTION; INSERT INTO sku (fields) VALUES (values); INSERT INTO sku (fields) VALUES (values); INSERT INTO sku (fields) VALUES (values); COMMIT;
这段SQL执行正常,sku和sku_price_history表都插入了对应记录。
当前用的是Spring Boot默认配置,没找到触发器和批量插入关联问题的相关资料。
版本信息
- jOOQ: 3.18.2
- Java: 17
- Spring Boot: 3.0.5
- 数据库:MariaDB 10.6(RDS)
解决方案
这个问题是jOOQ批量插入的执行模式与触发器交互导致的,可尝试以下解决方法:
1. 强制jOOQ使用单条语句批量模式
jOOQ默认会根据JDBC驱动能力选择合并式批量(INSERT ... VALUES (...), (...))或模拟批量(循环单条INSERT),强制关闭合并模式,让执行逻辑和SQL客户端一致:
// 代码中配置 Settings settings = new Settings() .withExecuteBatchUnmerged(true); // 或Spring Boot配置文件(application.yml) spring: jooq: settings: execute-batch-unmerged: true
2. 排查触发器潜在问题
虽然SQL客户端执行正常,但jOOQ的参数绑定或事务上下文可能有差异:
- 确认
NEW.created_by字段在批量插入时无空值、类型不匹配问题 - 将触发器中的
NOW()替换为CURRENT_TIMESTAMP(),提升兼容性 - 给触发器添加异常捕获,方便定位问题:
DELIMITER $$ CREATE TRIGGER trig_after_sku_insert AFTER INSERT ON sku FOR EACH ROW BEGIN DECLARE EXIT HANDLER FOR SQLEXCEPTION BEGIN SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Trigger error: ' || SQLERRM; END; INSERT INTO sku_price_history(sku_id, action_type, purchase_price, selling_price, base_margin_rate, vat, vat_type, tax_type, selling_price_started_at, updated_by) VALUES (NEW.id, 'CREATE', NEW.purchase_price, NEW.selling_price, NEW.base_margin_rate, NEW.vat, NEW.vat_type, NEW.tax_type, CURRENT_TIMESTAMP(), NEW.created_by); END $$ DELIMITER ;
3. 升级MariaDB JDBC驱动
旧版本驱动在批量插入+触发器场景下可能存在bug,升级到最新稳定版(如3.3.x以上):
<!-- pom.xml中修改依赖 --> <dependency> <groupId>org.mariadb.jdbc</groupId> <artifactId>mariadb-java-client</artifactId> <version>3.3.3</version> </dependency>
4. 手动控制事务
在批量插入外层手动开启事务,对齐SQL客户端的执行环境:
void saveMany(List<SkuDto.SaveBody> skus) { ctx.transaction(config -> { DSLContext txCtx = DSL.using(config); List<SkuRecord> skuRecords = new ArrayList<>(); for(SkuDto.SaveBody sku : skus) { SkuRecord skuRecord = txCtx.newRecord(SKU, sku); skuRecords.add(skuRecord); } txCtx.batchInsert(skuRecords).execute(); }); }
内容的提问来源于stack exchange,提问作者Elyorbek Ibrokhimov
相关产品推荐
相关产品推荐

