Skip to main content

Primary Key Types

For every table with a primary key, Typr generates a distinct ID type. This prevents mixing up IDs from different tables—a common source of bugs.

Simple Primary Keys

A table with a single-column primary key gets a wrapper type:

CompanyId
/** Type for the primary key of table `showcase.company` */
public record CompanyId(String value) {
public CompanyId withValue(String value) {
return new CompanyId(value);
}

@Override
public java.lang.String toString() {
return value.toString();
}

static public Bijection<CompanyId, String> bijection =
Bijection.of(CompanyId::value, CompanyId::new);

static public PgType<CompanyId> pgType =
PgTypes.text.to(Bijection.of(CompanyId::new, CompanyId::value));

static public PgType<List<CompanyId>> pgTypeArray =
pgType.array();
}

The wrapper ensures you can't accidentally pass a CustomerId where a CompanyId is expected. The compiler catches these mistakes.

Composite Primary Keys

Tables with multi-column primary keys get a composite ID type:

ProjectAssignmentId
/** Type for the composite primary key of table `showcase.project_assignment` */
public record ProjectAssignmentId(
EmployeeId employeeId,
ProjectId projectId
) implements Tuple2<EmployeeId, ProjectId> {
public ProjectAssignmentId withEmployeeId(EmployeeId employeeId) {
return new ProjectAssignmentId(employeeId, projectId);
}

public ProjectAssignmentId withProjectId(ProjectId projectId) {
return new ProjectAssignmentId(employeeId, projectId);
}

public static RowCodec<ProjectAssignmentId> rowCodec = RowCodecs.of(EmployeeId.pgType, ProjectId.pgType, ProjectAssignmentId::new, row -> new Object[]{row.employeeId(), row.projectId()});

@Override
public EmployeeId _1() {
return employeeId;
}

@Override
public ProjectId _2() {
return projectId;
}
}

The composite type bundles all key columns together, making it easy to pass around and use in lookups.

Using ID Types

ID types integrate with repositories for type-safe lookups:

CompanyRepo
  Optional<CompanyRow> selectById(
CompanyId id,
ConnectionRead c
);

Disabling ID Types

If you don't want wrapper types for certain tables, configure enablePrimaryKeyType:

Options options = new Options(
"mypkg",
Lang.Java,
// ... other options
Selector.relationNames("legacy_table"), // Only generate ID types for this table
);

Use Selector.All (default) for all tables, Selector.None to disable, or Selector.relationNames(...) for specific tables.

Composite ID types are always generated regardless of this setting.