Identifies the static Java method that extracts the parent-side join-key tuple when the parent is a @record-backed DTO with no FK metadata in the jOOQ catalog. Applies when the parent’s backing class is a plain Java record / POJO (not a jOOQ Record / TableRecord); the lifter extracts the key value(s) from the parent instance, the rewrite matches the lifted RowN against the columns the field’s first JOIN ON predicate consumes on the parent side, and a column-keyed DataLoader drives the batch.

@sourceRow composes with @reference for multi-hop paths from a DTO parent. Without @reference, the lifter’s RowN matches the leaf target table’s primary key columns directly.

Pairs with @record(record:) supplying the parent class. The directive is rejected on @table parents (use @reference there) and on jOOQ-record parents (the catalog already supplies the path).

SDL signature

directive @sourceRow(
    className: String!
    method: String!
) on FIELD_DEFINITION

Parameters

Name Type Default Description

className

String!

(required)

Fully-qualified Java class name carrying the static lifter method. Must be on the rewrite plugin’s classpath.

method

String!

(required)

Static method name on className. Must take a single argument assignable from the parent’s backing class and return Row1..Row22<…​>.

@sourceRow uses flat (className, method) args rather than the ExternalCodeReference wrapper used by @service and the other reflection-driven directives. Sibling directives are documented in How-to: Wire external Java code; the dedicated walk-through for @sourceRow lives in How-to: Source-row directive.

Composition with @reference

Two derivations, no third case:

  • With @reference. The lifter’s RowN matches the first FK hop’s source-side columns. Subsequent hops walk the catalog FK chain from there to the leaf target. The classifier produces BatchKey.LifterPathKeyed.

  • Without @reference. The lifter’s RowN matches the leaf target table’s primary key columns. The single column-equality JOIN folds source-side and target-side onto the same columns. The classifier produces BatchKey.LifterLeafKeyed.

The arity / column-class match is checked at build time against the derived parent-side tuple. A mismatch fails the build; the diagnostic message names the offending position, the lifter’s actual type, and the expected column (with two distinct phrasings for the two cases so the user-facing text reflects which derivation was applied).

Canonical examples

Leaf-PK case (no @reference). The DTO carries the leaf table’s primary-key value and the lifter returns it directly:

type CreateFilmPayload @record(record: {className: "no.sikt.graphitron.rewrite.test.services.CreateFilmPayload"}) {
    languageId: Int!
    language: [Language!]!
        @sourceRow(
            className: "no.sikt.graphitron.rewrite.test.services.CreateFilmPayloadLifter",
            method: "liftLanguageId"
        )
}

Path-keyed case (@reference-composed). The DTO carries a key the catalog can navigate from, and @reference walks the FK chain to the leaf:

type CustomerAddressSummary @record(record: {className: "no.sikt.graphitron.rewrite.test.services.CustomerAddressSummary"}) {
    customerId: Int!
    address: [Address!]!
        @sourceRow(
            className: "no.sikt.graphitron.rewrite.test.services.CustomerAddressSummaryLifter",
            method: "addressIdOf"
        )
        @reference(path: [{key: "customer_address_id_fkey"}])
}

The Java side for the leaf-PK lifter:

public final class CreateFilmPayloadLifter {
    public static Row1<Integer> liftLanguageId(CreateFilmPayload p) {
        return DSL.row(p.languageId());
    }
}

Per request the framework collects every parent, calls the lifter once per parent, gathers the resulting RowN keys, and dispatches a single batch keyed on those values. Each parent receives the matching leaf row(s) at scatter time.

Constraints

  • Required when a field on a non-table-backed @record parent returns a @table type and would otherwise be rejected as RecordTableField (or RecordLookupTableField) requires a FK join path and a typed backing class for batch key extraction.

  • The parent’s @record(record: {className: …​}) must declare a backing class. The directive is rejected on bare @record parents with no class.

  • Rejected on @table parents; there’s no DTO to lift from. Use @reference for catalog-driven joins instead.

  • Rejected on jOOQ-record parents (JooqTableRecordType / JooqRecordType); the catalog record already drives batching, and a typed accessor or jOOQ-record FK supplies the key.

  • The lifter’s RowN arity must equal the derived parent-side tuple size: path.first().sourceSideColumns() when @reference is present, or targetTable.primaryKeyColumns() otherwise.

  • Per-position Java types of the RowN arguments must equal the corresponding derived column’s Java class. Wildcards (Row1<? extends Number>) are rejected at build time.

  • Rejected on @asConnection fields.

  • @reference parse failures (unknown FK key, broken connectivity) surface directly, without the resolver double-validating the lifter’s signature against an unresolvable path.

See also

  • @record is the parent-side directive that decides whether @sourceRow is needed: jOOQ-backed @record types skip it; plain-Java-record / POJO types reach for it.

  • @reference is the catalog-driven counterpart on @table parents and the composition target for multi-hop @sourceRow paths.

  • How-to: Source-row directive walks two end-to-end examples.

  • How-to: Result-type variants covers JavaRecordType parents, accessor inference, and when to reach for @sourceRow.