Record 详解 - jOOQ 系列教程

Record 详解

在之前的章节中,基本每章都有出现的 Record 接口,也做过一些简单的讲解和一些代码示例。作为jOOQ中最重要的一个接口,本章将详细讲解 Record 的各种API和基础用法

Record 形式

Record 是jOOQ定义的用于储存数据库结果记录的一个接口,其主要是将一个表字段的列表和值的列表使用相同的顺序储存在一起,可以看做是一个用于储存列/值的映射的对象。通常有以下几种形式

表记录

与数据库表一一对应,如果包含主键,会继承UpdatableRecordImpl类,该类提供了使用 update, delete API进行数据操作。进行查询操作时,jOOQ会将结果集包装为一个TableRecord 对象。 在使用代码生成器的时候,会生成更详细的表记录类,包含表的每个字段操作等,通常以表名为开头 XxxxRecord

此类 Record 对象一般都有对应字段的getter/setter方法,但其都只是去调用get/set方法。其储存的方式还是通过两个数组来储存对应列/值的数据的,所以Record对象是不能被JSON直接序列化和反序列化的

UDT 记录

通常用于 Oracle 等支持用户自定义数据类型的数据库记录,这里接触较少,不作讲解

明确数据的记录

通用记录类型的一种,当你的字段不超过22个时,会根据字段个数反射成 Record1, Record2Record22 类的对象。这些对象需要的泛型个数和后面的数字一致,类型和字段类型一致。jOOQ自动生成的Record对象里,如果字段个数不超过 22 个,会同时实现 Record[N] 接口

举个例子

再看看 Record[N] 的接口定义,此接口主要是提供了获取字段,获取值,设置值的方法。由泛型决定字段/值类型和顺序,N 决定字段/值的个数。此接口的目的其实也很简单,就是为了更快速的操作指定位置的字段/值

interface Record[N]<T1, ... T[N]> {
    // 获取字段
    Field<T1> field1();
    ...
    Field<T[N]> field[N]();

    // 获取值
    T1 value1();
    ...
    T[N] valueN();

    // 设置值
    Record[1]<T1> value1(T1 value)
    ...
    Record[N]<TN> value1(T[N] value)
}

创建 Record 对象

这里说的创建Record对象,指的是在jOOQ已经生成了对应表的Record类的情况下,创建Record进行。使用 Record 提供的方法,我们可以方便的做一些针对表数据的操作。创建对象的方式有以下几种

new

由jOOQ生成的Record对象,可以通过 new 的方式直接创建一个实例。 通过直接 new 的方式创建对象,由于没有连接相关信息,无法直接进行 insert, update, delete 方法的调用。但是可以通过 DSLContext 的API进行操作数据操作,通过这种方式创建的Record对象可以理解为一个单纯的数据储存对象

S1UserRecord s1UserRecord = new S1UserRecord();
s1UserRecord.setUsername("new1");
s1UserRecord.setEmail("diamondfsd@gmail.com");
// insert会报错,因为没有连接配置
s1UserRecord.insert();

// 不需要获取主键,直接执行insert
int row = dslContext.insertInto(S1_USER).set(s1UserRecord)
        .execute()

// 执行insert并返回相应字段
Integer id = dslContext.insertInto(S1_USER).set(s1UserRecord)
                .returning(S1_USER.ID)
                .fetchOne().getId();

newRecord

在获取到 DSLContext 实例后,可以使用 dslContext.newRecord(Table<?> table) 方法来创建一个指定表的Record对象,这也是比较常用的方法。通过此方法创建的对象包含了dslContext内的数据连接配置,可以直接进行 insert, update, delete 等操作

S1UserRecord s1UserRecord = dslContext.newRecord(S1_USER);
s1UserRecord.setUsername("newRecord1");
s1UserRecord.setEmail("diamondfsd@gmail.com");
s1UserRecord.insert();

fetch

通过fetch*方法读取到结果 Record 对象,同样带有数据库连接相关配置,并且带有查询结果的数据。可以直接进行数据操作

S1UserRecord s1UserRecord = dslContext.selectFrom(S1_USER).where(S1_USER.ID.eq(1))
        .fetchOne();
s1UserRecord.setEmail("hello email");
int row = s1UserRecord.update();

//

S1UserRecord fetchIntoUserRecord = dslContext.select().from(S1_USER)
        .where(S1_USER.ID.eq(1))
        .fetchOneInto(S1UserRecord.class);
fetchIntoUserRecord.setEmail("hello email2");
int row2 = fetchIntoUserRecord.update();

数据库交互API

insert

此方法进行数据插入操作,几个重载可以指定插入的数据字段

需要注意的是,插入的字段必须显式的进行set操作,才会在最终的SQL语句中体现出来

// 常规用法
S1UserRecord userRecord = dslContext.newRecord(S1_USER);
userRecord.setUsername("insertUsername");
userRecord.setEmail("email");
userRecord.insert();
// insert into `learn-jooq`.`s1_user` (`username`, `email`)
// values ('insertUsername', 'email')

// 指定字段插入
userRecord = dslContext.newRecord(S1_USER);
userRecord.setUsername("insertUsername");
userRecord.setEmail("email");
userRecord.insert(S1_USER.USERNAME, S1_USER.ADDRESS);
// insert into `learn-jooq`.`s1_user` (`username`)
// values ('insertUsername')

update

此方法进行更新操作,和 insert 方法类似,重载的也一样

重载参数的目的是为了约束更新的字段,同样的,只有经过set的字段,才会被更新处理

S1UserRecord userRecord = dslContext.selectFrom(S1_USER)
                .where(S1_USER.ID.eq(1))
                .fetchSingle();
userRecord.setEmail(null);
userRecord.setUsername("hello");
userRecord.update(S1_USER.USERNAME, S1_USER.ADDRESS);
// update `learn-jooq`.`s1_user`
// set `learn-jooq`.`s1_user`.`username` = 'hello'
// where `learn-jooq`.`s1_user`.`id` = 1

delete

delete 方法根据主键进行数据删除操作

// 有主键值
S1UserRecord userRecord = dslContext.newRecord(S1_USER);
userRecord.setId(1);
userRecord.delete();
// delete from `learn-jooq`.`s1_user` where `learn-jooq`.`s1_user`.`id` = 1

// 无主键值
userRecord = dslContext.newRecord(S1_USER);
userRecord.delete();
// delete from `learn-jooq`.`s1_user` where `learn-jooq`.`s1_user`.`id` is null

数据处理API

get

get 系列方法主要是用于获取字段值

set

set 系列方法主要是用于设置字段值

changed

此方法用于修改字段更新标识,一般 update/insert 方法配合使用,可以设置指定字段是否 更新/储存

S1UserRecord userRecord = dslContext.newRecord(S1_USER);
userRecord.setId(1);
userRecord.setUsername("username");
userRecord.setEmail(null);
userRecord.changed(S1_USER.EMAIL, false);
userRecord.update();

reset

此方法用于重置字段更新标识,效果和 changed(Field field, false) 相同

S1UserRecord userRecord = dslContext.newRecord(S1_USER);
userRecord.setId(1);
userRecord.setUsername("username");
userRecord.setEmail(null);
userRecord.reset(S1_USER.EMAIL);
userRecord.update();

对象转换API

Record 提供了转换类API,可以方便快捷的将Record转换为其他任意类型,也可以将任意类型填充至Record对象中。其核心主要是 from/into 系列方法

from

此系列方法包含 from(...), fromMap(...) , fromArray(...) 三个方法,主要是可以将任意对象填充至Record

into

此系列方法是将Record转换为其他任意指定类型,常用的方法有

内容总结

本章源码: https://github.com/k55k32/learn-jooq/tree/master/section-4

本章主要是讲解了 Record 的各种形式和常用的API,单独拿一章出来讲解 Record,主要是因为在jOOQ中,基本所有操作都是在和 Record 类接口打交道。本章对大部分常用API做了简单的示例,大家可以根据测试源码内测试用例进行参考,能更好的掌握 Record API的使用