【SpringBoot DB 系列】Jooq 之新增記錄使用姿勢

小灰灰Blog發表於2020-09-30

【SpringBoot DB 系列】Jooq 之新增記錄使用姿勢

接下來我們開始進入 jooq 的增刪改查的使用姿勢系列,本篇將主要介紹如何利用 jooq 來實現新增資料

<!-- more -->

I. 專案搭建

本專案藉助SpringBoot 2.2.1.RELEASE + maven 3.5.3 + IDEA進行開發

1. 專案依賴

關於如何建立一個 SpringBoot 的專案工程,不再本文的描述範圍內,如有興趣可以到文末的個人站點獲取

在這個示例工程中,我們的選用 h2dabase 作為資料庫(方便有興趣的小夥伴直接獲取工程原始碼之後,直接測試體驗),因此對應的 pom 核心依賴如下

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-jooq</artifactId>
    </dependency>
    <dependency>
        <groupId>com.h2database</groupId>
        <artifactId>h2</artifactId>
    </dependency>
</dependencies>

2. 資料庫初始化

我們藉助jooq-codegen-maven外掛來自動生成資料庫相關的程式碼,對這一段邏輯感興趣的小夥伴可以參考博文:【DB 系列】Jooq 程式碼自動生成

後文中使用的表結構如下

DROP TABLE IF EXISTS poet;

CREATE TABLE poet (
  `id` int NOT NULL,
  `name` varchar(20) NOT NULL default '',
  CONSTRAINT pk_t_poet PRIMARY KEY (ID)
);

DROP TABLE IF EXISTS poetry;
CREATE TABLE poetry (
  `id` int NOT NULL,
  `poet_id` int NOT NULL default '0',
  `title` varchar(128) not null default '',
  `content` varchar(128) not null default '',
  CONSTRAINT pk_t_poetry PRIMARY KEY (ID)
);

3. 配置檔案

h2database 的連線配置如 application.properties

#Database Configuration
spring.datasource.url=jdbc:h2:~/h2-jooq-poet
spring.datasource.username=test
spring.datasource.password=
spring.datasource.driverClassName=org.h2.Driver


#jOOQ Configuration
spring.jooq.sql-dialect=H2


spring.datasource.initialization-mode=never
spring.datasource.continueOnError=true


##h2 web console設定
spring.datasource.platform=h2
#進行該配置後,h2 web consloe就可以在遠端訪問了。否則只能在本機訪問。
spring.h2.console.settings.web-allow-others=true
#進行該配置,你就可以透過YOUR_URL/h2訪問h2 web consloe
spring.h2.console.path=/h2
#進行該配置,程式開啟時就會啟動h2 web consloe
spring.h2.console.enabled=true

II. 新增記錄

接下來我們進入正式的資料插入的使用姿勢介紹,一般來說新增資料會區分單個和批次兩種方式,下面我們分別進行介紹

1. Record 實體類新增方式

在 jooq 中,藉助自動生成的 Record 類來實現新增是最簡單的 case,如下


private static final PoetTB table = PoetTB.POET;
@Autowired
private DSLContext dsl;

/**
 * 新增記錄
 *
 * @param id
 * @param name
 * @return
 */
public boolean save(int id, String name) {
    PoetPO record = dsl.newRecord(table);
    record.setId(id);
    record.setName(name);
    return record.insert() > 0;
}

注意:

  • 實體類的建立方式:PoetPO record = dsl.newRecord(table);,不要直接 new 一個物件出來使用

2. 鏈式寫法

下面介紹的這種寫法和 sql 非常相似,也是我個人用的比較多的方式,特點就是一目瞭然

public boolean save2(int id, String name) {
    return dsl.insertInto(table).set(table.ID, id).set(table.NAME, name).execute() > 0;
}

3. InsertQuery 方式

上面兩種寫法比較常見,而直接使用 InsertQuery 的方式,在實際的業務開發中可能並沒有上面的優雅,但某些特殊場景下還是很有用的

/**
 * 不使用自動生成的程式碼來原生插入資料
 *
 * @param id
 * @param name
 * @return
 */
public boolean save3(int id, String name) {
    // 當不使用自動生成的物件時,table可以用 DSL.table()指定,列可以用 DSL.field()指定
    InsertQuery insertQuery = dsl.insertQuery(DSL.table("poet"));
    insertQuery.addValue(DSL.field("id", Integer.class), id);
    insertQuery.addValue(DSL.field("name", String.class), name);
    return insertQuery.execute() > 0;
}

注意一下上面的用法,InsertQuery本身的使用沒有什麼值得說到的,重點在上面的實現中,並沒有利用自動生成的程式碼,如

  • table: DSL.table(表名)
  • field: DSL.field(列名,型別)

透過上面的的 case,我們可以知道在不自動生成 DB 對應的程式碼前提下,如何進行資料庫的操作

4. Record 實體批次儲存

藉助dsl.batchInsert來批次新增實體,屬於最基礎的使用姿勢了

private PoetPO bo2po(PoetBO bo) {
    PoetPO po = dsl.newRecord(table);
    po.setId(bo.getId());
    po.setName(bo.getName());
    return po;
}

/**
 * 透過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;
}

5. 鏈式批次儲存

同樣是類 sql 的鏈式插入方式,需要注意一下與前面的單條記錄的鏈式插入的區別,下面這種寫法和 sql 的批次插入的寫法及其相似

/**
 * 類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;
}

6. InsertQuery 批次儲存

上面介紹了 InsetQuery 的單條插入方式,下面的批次寫法基本上沒有太大的區別

/**
 * 不基於自動生成的程式碼,來批次新增資料
 *
 * @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;
}

7. 測試 case

接下來測試一下上面的 6 個方法執行

public void test() {
    this.save(11, "一灰");
    this.save2(12, "一灰灰");
    this.save3(13, "一灰灰Blog");


    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")));

    RecordMapper<PoetPO, PoetBO> mapper =
            dsl.configuration().recordMapperProvider().provide(table.recordType(), PoetBO.class);
    List<PoetBO> result = dsl.selectFrom(table).fetch().map(mapper);
    System.out.println(result);
}

輸出結果如下

[1,1]
[PoetBO (1, 李白), PoetBO (2, 艾可翁), PoetBO (11, 一灰), PoetBO (12, 一灰灰), PoetBO (13, 一灰灰Blog), PoetBO (14, yh), PoetBO (15, yhh), PoetBO (16, yihui), PoetBO (17, yihuihui), PoetBO (18, YiHui), PoetBO (19, YiHuiBlog)]

II. 其他

0. 專案

系列博文

專案原始碼

1. 一灰灰 Blog

盡信書則不如,以上內容,純屬一家之言,因個人能力有限,難免有疏漏和錯誤之處,如發現 bug 或者有更好的建議,歡迎批評指正,不吝感激

下面一灰灰的個人部落格,記錄所有學習和工作中的博文,歡迎大家前去逛逛

一灰灰blog

相關文章