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

如何在jOOQ的VisitListener中为多行操作正确设置StoreQuery审计字段?

jOOQ VisitListener 审计字段配置问题解答

问题1:不执行auditFields.clear()时,无审计字段的表也会被添加审计字段的原因

你的AuditVisitListener实例会被jOOQ复用(即使配置类中每次通过Provider新建实例,jOOQ在查询上下文或缓存机制中仍可能复用该实例)。auditFields是类的实例变量,若不在visitEnd中清空,上一次查询收集到的审计字段会残留。当下一次处理无审计字段的表时,auditFields中仍保留之前的字段记录,导致addAuditFieldsForInsert/Update方法执行storeQuery.addValue(),强行将审计字段添加到SQL中。

问题2:链式.values()多行插入仅最后一行设置审计字段的解决方案

问题原因

链式调用.values()构建的是InsertValuesStepN类型的插入语句,所有行数据维护在同一个InsertQuery中。StoreQuery.addValue()方法仅对当前最后一行设置值,而非所有行。这就导致只有最后一行被填充审计字段值,前面的行仅在列列表中添加了审计字段,但值为null。

而batchInsert是创建多个独立的InsertQuery,每个查询都会触发一次visitStart,因此每一行都会被正确设置审计字段。

修复方案

方案1:遍历所有行设置审计字段

修改addAuditFieldsForInsert方法,针对多行插入场景遍历所有行,逐个设置审计字段值:

private void addAuditFieldsForInsert(StoreQuery<?> storeQuery) {
    if (!(storeQuery instanceof InsertQuery<?> insertQuery)) {
        return;
    }

    Timestamp now = now();
    String userId = userId();
    List<Row> rows = insertQuery.getValues();

    // 遍历所有行添加审计字段值
    for (Row row : rows) {
        if (auditFields.contains(createdAt)) {
            row.add(createdAt, now);
        }
        if (auditFields.contains(createdBy)) {
            row.add(createdBy, userId);
        }
        if (auditFields.contains(updatedAt)) {
            row.add(updatedAt, now);
        }
        if (auditFields.contains(updatedBy)) {
            row.add(updatedBy, userId);
        }
    }

    // 确保审计字段被添加到插入列列表
    if (auditFields.contains(createdAt) && !insertQuery.getFields().contains(createdAt)) {
        insertQuery.addField(createdAt);
    }
    if (auditFields.contains(createdBy) && !insertQuery.getFields().contains(createdBy)) {
        insertQuery.addField(createdBy);
    }
    if (auditFields.contains(updatedAt) && !insertQuery.getFields().contains(updatedAt)) {
        insertQuery.addField(updatedAt);
    }
    if (auditFields.contains(updatedBy) && !insertQuery.getFields().contains(updatedBy)) {
        insertQuery.addField(updatedBy);
    }
}

方案2:使用set方法统一设置所有行

改用StoreQuery.set()方法替代addValue(),该方法会为所有行设置字段值(需确保字段已添加到插入列列表):

private void addAuditFieldsForInsert(StoreQuery<?> storeQuery) {
    if (auditFields.contains(createdAt)) {
        storeQuery.set(createdAt, now());
    }
    if (auditFields.contains(createdBy)) {
        storeQuery.set(createdBy, userId());
    }
    addAuditFieldsForUpdate(storeQuery);
}

private void addAuditFieldsForUpdate(StoreQuery<?> storeQuery) {
    if (auditFields.contains(updatedAt)) {
        storeQuery.set(updatedAt, now());
    }
    if (auditFields.contains(updatedBy)) {
        storeQuery.set(updatedBy, userId());
    }
}

额外优化建议

collectAuditFields方法中,使用table.field(createdAt)判断字段存在性的方式不够可靠(未绑定表的字段可能无法匹配),建议改用表的元数据或代码生成的字段常量判断:

// 示例:通过字段名称判断
if (table.getField("created_at") != null) {
    auditFields.add(createdAt);
}
// 若使用代码生成的表,可直接用生成的字段常量
// if (BRAND.CREATED_AT != null) { ... }

内容的提问来源于stack exchange,提问作者Elyorbek Ibrokhimov

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 17:47:16