【SpringBoot DB系列】Jooq批量寫入采坑記錄
前面介紹了jooq的三種批量插入方式,結果最近發現這里面居然還有一個深坑,我以為的批量插入居然不是一次插入多條數據,而是一條一條的插入...,這就有點尬了
1. 三種插入姿勢
關於項目創建以及jooq的相關使用姿勢,推薦查看之前的博文: 【DB系列】Jooq之新增記錄使用姿勢
下面是我們采用的三種批量插入方式
/**
* 通過Record執行批量添加
*
* 通過源碼查看,這種插入方式實際上是單條單條的寫入數據,和下面的一次插入多條有本質區別
*
* @param list
* @return
*/
public boolean batchSave(List<PoetBO> list) {
List<PoetPO> poList = list.stream().map(this::bo2po).collect(Collectors.toList());
int[] ans = dsl.batchInsert(poList).execute();
System.out.println(JSON.toJSONString(ans));
return true;
}
/**
* 類sql寫法,批量添加
*
* @param list
* @return
*/
public boolean batchSave2(List<PoetBO> list) {
InsertValuesStep2<PoetPO, Integer, String> step = dsl.insertInto(table).columns(table.ID, table.NAME);
for (PoetBO bo : list) {
step.values(bo.getId(), bo.getName());
}
return step.execute() > 0;
}
/**
* 不基於自動生成的代碼,來批量添加數據
*
* @param list
* @return
*/
public boolean batchSave3(List<PoetBO> list) {
InsertQuery insertQuery = dsl.insertQuery(DSL.table("poet"));
for (PoetBO bo : list) {
insertQuery.addValue(DSL.field("id", Integer.class), bo.getId());
insertQuery.addValue(DSL.field("name", String.class), bo.getName());
insertQuery.newRecord();
}
return insertQuery.execute() > 0;
}
請注意上面的三種批量插入方式,基本上對應的就是jooq的三種常見的用法
- 直接借助自動生成的
Record
類來操作 - 類sql的拼接寫法,基本上我們平時的sql怎么寫,這里就怎么用
InsertQuery
:借助jooq提供的各種Query類來執行目標操作
2. 日志驗證
上面三種寫法中,第一種批量插入方式,並不是我們傳統理解的一次插入多條記錄,相反它是一條一條的插入的,我們可以通過開啟jooq的日志來查看一些執行的sql情況
配置文件 application.properties
,添加下面的配置
debug=false
trace=false
logging.level.org.jooq=DEBUG
如果有自己的logback.xml
配置文件,可以調整一下日志級別,將jooq的debug日志放出來
一個簡單的測試case
public void test() {
this.batchSave(Arrays.asList(new PoetBO(14, "yh"), new PoetBO(15, "yhh")));
this.batchSave2(Arrays.asList(new PoetBO(16, "yihui"), new PoetBO(17, "yihuihui")));
this.batchSave3(Arrays.asList(new PoetBO(18, "YiHui"), new PoetBO(19, "YiHuiBlog")));
}
從上面的sql來看,后面兩個確實是一次插入多條,但是第一個,也沒有將具體執行的sql打印出來,所有不看源碼的話,也沒有辦法實錘是一條一條插入的
為了驗證這個問題,一個簡單的解決辦法就是批量插入兩條數據,第一條正常,第二條異常,如果第一條插入成功,第二條失敗那就大概率是單個插入的了
// 表結構中,name的字段最大為20,下面插入的第二條數據長度超限
try {
this.batchSave(Arrays.asList(new PoetBO(14, "yh"), new PoetBO(15, "1234567890098765432112345")));
} catch (Exception e) {
e.printStackTrace();
}
try {
this.batchSave2(Arrays.asList(new PoetBO(16, "yihui"), new PoetBO(17, "1234567890098765432112345")));
} catch (Exception e) {
e.printStackTrace();
}
this.batchSave3(Arrays.asList(new PoetBO(18, "YiHui"), new PoetBO(19, "YiHuiBlog")));
第一種批量插入失敗
第二種插入失敗
插入后結果
請注意上面的報錯,以及最終插入的結果,第一種插入方式一個插入成功一個失敗;第二種批量插入方式,兩條都插入失敗;
通常情況下,一次插入多條數據時,一個插入失敗,會導致整個插入都失敗,如下
3. 源碼分析
上面是從日志以及結果表現來推測實際的執行情況,接下來就需要從源碼角度來看一下,是否真的是單個的執行了
省略掉具體的定位過程,直接找到org.jooq.impl.BatchCRUD#execute
,對應的代碼
@Override
public final int[] execute() throws DataAccessException {
// [#1180] Run batch queries with BatchMultiple, if no bind variables
// should be used...
if (executeStaticStatements(configuration.settings())) {
return executeStatic();
}
else {
return executePrepared();
}
}
上面有兩種插入方式,對於插入的核心邏輯一樣
遍歷集合,獲取單個 record,執行 CURD
II. 其他
0. 項目
系列博文
- 【SpringBoot DB系列】Jooq之記錄更新與刪除
- 【SpringBoot DB系列】Jooq之新增記錄使用姿勢
- 【SpringBoot DB系列】Jooq代碼自動生成
- 【SpringBoot DB系列】Jooq初體驗
項目源碼
- 工程:https://github.com/liuyueyi/spring-boot-demo
- 項目源碼: https://github.com/liuyueyi/spring-boot-demo/tree/master/spring-boot/108-jooq-curd
1. 一灰灰Blog
盡信書則不如,以上內容,純屬一家之言,因個人能力有限,難免有疏漏和錯誤之處,如發現bug或者有更好的建議,歡迎批評指正,不吝感激
下面一灰灰的個人博客,記錄所有學習和工作中的博文,歡迎大家前去逛逛
- 一灰灰Blog個人博客 https://blog.hhui.top
- 一灰灰Blog-Spring專題博客 http://spring.hhui.top