Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

jOOQ Record API: Forms, Creation, and Practical Usage

Tech 2

Record shapes

A Record in jOOQ is a typed, positional container that pairs a sequence of Field definitions with their corresponding values. It behaves like a column-to-value tuple stored in order. Generated table-specific records expose getters and setters for each column, but internally a Record stores values in arrays; direct JSON serialization/deserialization is not supported without custom mapping.

Table-specific records

For each database table, the code generator produces a TableRecord subtype. When a table has a primary key, its record type extends UpdatableRecordImpl, enabling insert, update, and delete operations through instance methods. Query results retrieved via jOOQ are materialized as these table-specific record instanecs.

Example (table with 6 columns):

class S1UserRecord extends UpdatableRecordImpl<S1UserRecord>
    implements Record6<Integer, String, String, String, Timestamp, Timestamp> {
}

Example (table with > 22 columns):

class S4ColumenGt22Record extends UpdatableRecordImpl<S4ColumenGt22Record> {
}

UDT records

Databases that support user-defined types (e.g., Oracle) map those types to UDTRecord implementations.

Arity-based records

jOOQ exposes generic Record1..Record22 interfaces to operate on a fixed number of values and types. Generated table records implement RecordN when the number of columns is ≤ 22, aliginng the generic types and order with the table’s columns.

Sketch of the RecordN API:

interface RecordN<T1, /* ... */ TN> {
    // Field accessors
    Field<T1> field1();
    /* ... */
    Field<TN> fieldN();

    // Value accessors
    T1 value1();
    /* ... */
    TN valueN();

    // Value mutators
    RecordN<T1, /*...*/ TN> value1(T1 v1);
    /* ... */
    RecordN<T1, /*...*/ TN> valueN(TN vN);
}

Creating records

new

Direct instantiation creates a detached record with no configured connection. You can still pass it to DSLContext statements, but record.insert()/update()/delete() will fail when no configuration is attached.

S1UserRecord r = new S1UserRecord();
r.setUsername("u_new");
r.setEmail("u_new@example.com");

// Throws when no Configuration is attached
r.insert();

// Use the record as a value container in DSLContext statements
int inserted = dsl.insertInto(S1_USER)
                 .set(r)
                 .execute();

Integer generatedId = dsl.insertInto(S1_USER)
                         .set(r)
                         .returning(S1_USER.ID)
                         .fetchOne()
                         .getId();

DSLContext.newRecord

Records created via DSLContext are attached to the same configuration and can call insert/update/delete directly.

S1UserRecord r = dsl.newRecord(S1_USER);
r.setUsername("newRecordUser");
r.setEmail("nr@example.com");
r.insert();

fetch/fetchInto

Query methods return attached record instances ready for further persistence operations.

S1UserRecord r1 = dsl.selectFrom(S1_USER)
                     .where(S1_USER.ID.eq(1))
                     .fetchOne();
r1.setEmail("updated+1@example.com");
int rows1 = r1.update();

S1UserRecord r2 = dsl.select()
                     .from(S1_USER)
                     .where(S1_USER.ID.eq(1))
                     .fetchOneInto(S1UserRecord.class);
r2.setEmail("updated+2@example.com");
int rows2 = r2.update();

Persistence APIs

insert

  • insert(): persists all fields that were set
  • insert(Field<?>... fields): persists the specified subset of set fields
  • insert(Collection> fields): same as above using a collection

Only fields explicitly set on the record are rendered into the SQL statement.

S1UserRecord r = dsl.newRecord(S1_USER);
r.setUsername("insert_user");
r.setEmail("insert@example.com");
r.insert();

r = dsl.newRecord(S1_USER);
r.setUsername("subset_user");
r.setEmail("ignored@example.com");
r.insert(S1_USER.USERNAME, S1_USER.ADDRESS); // only USERNAME persisted here

update

  • update(): updates all changed fields
  • update(Field<?>... fields): updates a specified subset
  • update(Collection> fields): same with a collection

Only changed fields are updated.

S1UserRecord r = dsl.selectFrom(S1_USER)
                    .where(S1_USER.ID.eq(1))
                    .fetchSingle();

r.setEmail(null);
r.setUsername("alice");
r.update(S1_USER.USERNAME, S1_USER.ADDRESS);

delete

Deletes by primary key.

S1UserRecord r = dsl.newRecord(S1_USER);
r.setId(42);
r.delete();

S1UserRecord rNoKey = dsl.newRecord(S1_USER);
rNoKey.delete(); // WHERE id IS NULL

Data access APIs

get

  • get(Field<T>): read a field value
  • get(Field<T>, Class<U>): read and convert to a target type
Record any = dsl.select().from(S1_USER).limit(1).fetchOne();
Integer id = any.get(S1_USER.ID);
Long createdAsLong = any.get(S1_USER.CREATE_TIME, Long.class);

Generated table records also expose typed getters:

S1UserRecord ur = dsl.selectFrom(S1_USER).fetchAny();
Integer id2 = ur.getId();
Timestamp created = ur.getCreateTime();

RecordN value accessors provide positional reads when working with fixed arity:

Record3<Integer, String, Timestamp> triple = dsl
    .select(S1_USER.ID, S1_USER.USERNAME, S1_USER.CREATE_TIME)
    .from(S1_USER)
    .fetchAny();

Integer id3 = triple.value1();
String uname = triple.value2();
Timestamp created3 = triple.value3();

set

  • set(Field<T>, T): assign a value programmatically
S1UserRecord r = dsl.newRecord(S1_USER);
r.set(S1_USER.ID, 7);
r.set(S1_USER.USERNAME, "prog_user");
r.update();

Generated setters mirror the table columns:

S1UserRecord r2 = dsl.newRecord(S1_USER);
r2.setId(7);
r2.setUsername("setter_user");
r2.update();

changed

Toggle whether a field is considered changed. Useful to avoid updating or inserting a particular column.

S1UserRecord r = dsl.newRecord(S1_USER);
r.setId(1);
r.setUsername("kept");
r.setEmail(null);
r.changed(S1_USER.EMAIL, false); // EMAIL will not be persisted
r.update();

reset

Equivalent to changed(field, false), clearing a field’s changed flag.

S1UserRecord r = dsl.newRecord(S1_USER);
r.setId(1);
r.setUsername("kept2");
r.setEmail(null);
r.reset(S1_USER.EMAIL);
r.update();

Object mapping APIs

Record supports converting from/to various representations via from/into methods.

from

Populate a record from objects, maps, or arrays.

S1UserPojo p = new S1UserPojo();
p.setUsername("p_user");
p.setAddress("p_addr");
S1UserRecord r1 = dsl.newRecord(S1_USER);
r1.from(p);
Map<String, Object> m = new HashMap<>();
m.put("username", "m_user");
m.put("address", "m_addr");
S1UserRecord r2 = dsl.newRecord(S1_USER);
r2.fromMap(m);
Object[] arr = new Object[] { "a_user", "a_addr" };
S1UserRecord r3 = dsl.newRecord(S1_USER);
r3.fromArray(arr, S1_USER.USERNAME, S1_USER.ADDRESS);

into

Convert a record into another type or projection.

S1UserRecord rec = dsl.selectFrom(S1_USER)
                      .where(S1_USER.ID.eq(1))
                      .fetchOne();
S1UserPojo pojo = rec.into(S1UserPojo.class);
Record2<Integer, String> idAndName = rec.into(S1_USER.ID, S1_USER.USERNAME);
Integer onlyId = idAndName.value1();
Object[] values = rec.intoArray();
Integer idFromArray = (Integer) values[0];
Map<String, Object> map = rec.intoMap();
Integer idFromMap = (Integer) map.get("id");
ResultSet rs = rec.intoResultSet();

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.