jOOQ的batchInsert()与“after insert”触发器的行为不符合预期。

huangapple go评论69阅读模式
英文:

jOOQ's batchInsert() doesn't behave as expected with "after insert" trigger

问题

描述

我有一个触发器,在将记录插入到 sku 表后,会将记录插入到 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 的 DSLContext.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

我尝试过的

  1. 在删除触发器后执行相同的代码,它可以正常工作。因此,触发器似乎是主要原因。
  2. 然后,我尝试使用 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 语句按预期工作,记录被插入到 skusku_price_history 表中。

我可能遗漏了关于触发器和批量插入的一些要点,但我找不到相关信息。我正在使用 Spring Boot 默认配置。

版本信息:

  • jOOQ: 3.18.2
  • Java: 17
  • Spring Boot: 3.0.5
  • 数据库(包括供应商):MariaDB 10.6(RDS)
英文:

Description

I have a trigger that inserts into sku_price_history table after inserting into sku table:

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, &#39;CREATE&#39;, NEW.purchase_price, NEW.selling_price, NEW.base_margin_rate,
            NEW.vat, NEW.vat_type, NEW.tax_type, NOW(), NEW.created_by);
END $$
DELIMITER ;

Then, I am trying to do a simple batch insert using jOOQ's DSLContext.batchInsert():

void saveMany(List&lt;SkuDto.SaveBody&gt; skus) {
    List&lt;SkuRecord&gt; skuRecords = new ArrayList&lt;&gt;();
    for(SkuDto.SaveBody sku : skus) {
        SkuRecord skuRecord = ctx.newRecord(SKU, sku);
        skuRecords.add(skuRecord);
    }
    ctx.batchInsert(skuRecords).execute();
}

Problem

Above statement inserts only the first row into sku table even though there are many records. No exception is thrown and batch size is correct according to 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

What I tried

  1. I executed the same code after dropping the trigger and it worked. So the trigger seems to be a main reason here.
  2. Then, I tried to simulate the problem using an SQL client, run multiple insert statements in transaction:
SET autocommit = OFF;
START TRANSACTION;
INSERT INTO sku (fields) VALUES (values);
INSERT INTO sku (fields) VALUES (values);
INSERT INTO sku (fields) VALUES (values);
COMMIT;

Above SQL statement worked as expected, records were inserted into both sku and sku_price_history tables.

I may be missing some points about triggers and batch insert but I couldn't find relevant information. I am using Spring Boot with default configurations.

Versions:

  • jOOQ: 3.18.2
  • Java: 17
  • Spring Boot: 3.0.5
  • Database (include vendor): MariaDB 10.6 (RDS)

答案1

得分: 1

经过Lukas Eder的有益指导,我发现我遇到的问题并不是jOOQ本身的错误,而是与MariaDB JDBC驱动程序有关。

我正在记录在询问与jOOQ相关的问题之前需要采取的步骤,以供公众知识参考。这可能有助于其他人(或我自己)在将来遇到类似问题时解决它。

  1. 当您认为jOOQ的工作不如预期时,请检查是否存在副作用。副作用可以是从应用程序级别的VisitListener到数据库级别的触发器的任何东西。我花了数小时来解决这个问题,直到我发现它是因为一个触发器而引起的。
  2. 使用JDBC直接重现问题。如果发生相同的问题,那么很可能不是jOOQ的问题。
  3. 最好始终使用官方驱动程序,但出于识别问题的目的,如果可以的话,请尝试切换JDBC驱动程序。例如,从MariaDB Connector/J切换到MySQL Connector/J。

我按照上述步骤操作,并在从MariaDB Connector/J切换到MySQL Connector/J后解决了问题。这证实了问题不在于jOOQ本身,而是与我使用的特定MariaDB JDBC驱动程序有关。根据文档,我认为我可以继续使用MySQL驱动程序,因为它与JDBC 4.2规范兼容。

MySQL Connector/J 8.0是一个与JDBC 4.2规范兼容的JDBC Type 4驱动程序。

英文:

After helpful guidance from Lukas Eder, I've discovered the issue I was experiencing is not a bug with jOOQ itself but with the MariaDB JDBC driver.
I am documenting steps to take before asking jOOQ related questions for the sake of public knowledge. It may assist others(or myself) who encounter similar problem in the future.

  1. Check for side effects when you think jOOQ is not working as expected. Side effects can be anything from application level VisitListeners to database level triggers. I spent hours trying to solve the problem until I found it was because of a trigger.
  2. Reproduce the problem using JDBC directly. If the same problem occurs then most likely it's not the problem with the jOOQ
  3. It's always best to use the official driver but for the sake of identifying the problem try switching the JDBC driver if you can. For example from MariaDB Connector/J to MySQL Connector/J.

I followed above steps and resolved the problem after switching from MariaDB Connector/J to MySQL Connector/J. This confirms that the issue is not with jOOQ itself, but with the specific MariaDB JDBC driver I was using. I think I can continue using MySQL driver since it's compatible with the JDBC 4.2 specification according to the documentation.

> MySQL Connector/J 8.0 is a JDBC Type 4 driver that is compatible with the JDBC 4.2 specification.

huangapple
  • 本文由 发表于 2023年6月8日 16:58:57
  • 转载请务必保留本文链接:https://go.coder-hub.com/76430213.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定