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();
}

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

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

public static 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.