【SpringBoot DB 系列】Jooq 之新增记录使用姿势

866 阅读4分钟

【SpringBoot DB 系列】Jooq 之新增记录使用姿势

接下来我们开始进入 jooq 的增删改查的使用姿势系列,本篇将主要介绍如何利用 jooq 来实现添加数据

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