英文:
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
我尝试过的
- 在删除触发器后执行相同的代码,它可以正常工作。因此,触发器似乎是主要原因。
- 然后,我尝试使用 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)
英文:
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, '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 ;
Then, I am trying to do a simple batch insert using jOOQ's 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();
}
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
- I executed the same code after dropping the trigger and it worked. So the trigger seems to be a main reason here.
- 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相关的问题之前需要采取的步骤,以供公众知识参考。这可能有助于其他人(或我自己)在将来遇到类似问题时解决它。
- 当您认为jOOQ的工作不如预期时,请检查是否存在副作用。副作用可以是从应用程序级别的
VisitListener
到数据库级别的触发器的任何东西。我花了数小时来解决这个问题,直到我发现它是因为一个触发器而引起的。 - 使用JDBC直接重现问题。如果发生相同的问题,那么很可能不是jOOQ的问题。
- 最好始终使用官方驱动程序,但出于识别问题的目的,如果可以的话,请尝试切换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.
- Check for side effects when you think jOOQ is not working as expected. Side effects can be anything from application level
VisitListener
s to database level triggers. I spent hours trying to solve the problem until I found it was because of a trigger. - Reproduce the problem using JDBC directly. If the same problem occurs then most likely it's not the problem with the jOOQ
- 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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论