Skip to content

SpringBoot ID generation strategies

Runtime boundary

The assigned-ID and CUSTOM paths are stable. Provider-managed JPA strategies remain subject to the selected persistence provider, key type and database.

The key marker and the generation strategy answer two different questions:

  • ** marks a field as the identity of its complex type;
  • the SpringBoot generation property decides whether application code or a JPA provider can create a missing value for that key.

The property is part of the CMN model. It is not a joinedworkz.properties setting.

1. Model the effective strategy

Prefer a reusable simple ID type when several entities share one strategy:

cmn
package org.example.records

import org.joinedworkz.facilities.common.base
import org.joinedworkz.facilities.profiles.springboot

platform SpringBoot

type GeneratedRecordId specialization of Id generation='CUSTOM'

type<entity> GeneratedRecord {
    id**:   GeneratedRecordId
    label*: String(100)
}

generation propagates from GeneratedRecordId to the key field. A value on the field itself has precedence and can select another strategy for one entity:

cmn
type<entity> JpaGeneratedRecord {
    id**:   GeneratedRecordId generation='UUID'
    label*: String(100)
}

You can also configure a one-off key directly:

cmn
type<entity> DirectlyConfiguredRecord {
    id**: Id generation='CUSTOM'
}

SpringBoot evaluates the effective property only on the JPA key field of a persistent entity. A generation value elsewhere does not define general CMN, DTO or OpenAPI behavior.

An entity that selects generation must have exactly one effective scalar key field. If an entity has several key fields and at least one of them has an effective strategy, SpringBoot rejects that generated-ID combination. This targeted guard neither changes assigned composite models that omit generation nor claims general runtime support for composite IDs.

2. Strategy matrix

Values are case-sensitive and must use the exact uppercase spelling shown below. There is no ASSIGNED literal; omit the property for assigned IDs.

Effective valueGenerated entityID producerJoinedWorkz contract
not set@Idcalling applicationAssigned ID; JoinedWorkz does not generate a missing value
CUSTOM@Idgenerated data-access serviceStable for effective Java key type UUID or String
UUID@Id plus @GeneratedValue(strategy = GenerationType.UUID)JPA providerAdvanced provider-managed path
AUTO@Id plus GenerationType.AUTOJPA providerCompatibility pass-through
IDENTITY@Id plus GenerationType.IDENTITYJPA provider and databaseCompatibility pass-through
SEQUENCE@Id plus GenerationType.SEQUENCEJPA provider and databaseCompatibility pass-through
TABLE@Id plus GenerationType.TABLEJPA provider and databaseCompatibility pass-through

Omitting the property preserves the assigned-ID behavior used by existing models. A create caller must then supply the key. JoinedWorkz makes no promise about the exact provider error produced when an assigned key is absent.

3. Built-in CUSTOM generation

For CUSTOM, the generated data-access service overrides its ID-generation hook:

  • a Java UUID key receives UUID.randomUUID();
  • a Java String key receives UUID.randomUUID().toString().

CUSTOM does not name an application class and is not an open-ended generator plugin. Other effective Java key types are rejected instead of producing a null fallback.

3.1 Create lifecycle and explicit IDs

The reference persistence glue calls the generated hook from AbstractDataAccessService.create(dto) only when the DTO contains no ID. Its default precedence is therefore:

  1. retain a non-null ID supplied by the caller;
  2. otherwise generate the CUSTOM ID;
  3. persist the entity and return the effective ID in the resulting DTO.

This rule does not define duplicate-ID handling or upsert behavior. Those outcomes still belong to the repository, persistence provider and database.

The CUSTOM hook is specific to the create(dto) path. Calling a generated data-access save(...) method or a Spring Data repository directly bypasses that application-side hook. Provider-managed JPA generation can have different behavior because it runs during persistence.

4. Provider-managed JPA strategies

UUID, AUTO, IDENTITY, SEQUENCE and TABLE are emitted as JPA GenerationType values. JoinedWorkz selects the annotation; the JPA provider and database decide whether the selected strategy is compatible with the key type and runtime configuration.

UUID is the focused advanced path exercised by the public Spring Boot example. That example proves UUID generation with its documented Spring Boot, Hibernate and H2 stack. It is not a general compatibility statement for every provider or database.

AUTO, IDENTITY, SEQUENCE and TABLE remain accepted for compatibility, but JoinedWorkz does not define their runtime result. In particular:

  • do not infer an explicit-ID precedence rule for these strategies;
  • verify key-type support with the selected provider;
  • supply any required sequence, identity or generator-table configuration; and
  • run a persistence test against the actual database before deployment.

5. Schema and Flyway boundary

The generation property controls generated Java persistence annotations and, for CUSTOM, generated data-access code. It does not instruct the JoinedWorkz schema generator to create sequences, identity definitions, generator tables or database-side UUID defaults.

A strategy change can therefore alter generated Java without producing a structural Flyway difference. Review the requirements of the selected JPA provider and maintain any required database objects or migration SQL as part of the application. Continue with Flyway schema migrations for the separate schema-history contract.

6. Validation and diagnostics

SpringBoot validates the effective strategy before persistence generation. Use the exact supported value or omit the property:

DiagnosticMeaning
springboot.invalidIdGenerationStrategyThe effective key strategy is not one of CUSTOM, UUID, AUTO, IDENTITY, SEQUENCE or TABLE
springboot.unsupportedCustomIdGenerationTypeCUSTOM is used with an effective Java key type other than UUID or String
springboot.unsupportedCompositeIdGenerationAt least one effective strategy is set on an entity with several key fields

If a local entity inherits its effective key from another model, SpringBoot validates that local usage under the current platform configuration. A usage-specific diagnostic is owned by the local entity; this does not traverse or revalidate the complete imported model.

A lowercase value such as custom is not an alias. Correct the CMN model or the reusable ID type and regenerate. For a composite model, use one scalar key for generation or omit the strategy and retain the project's assigned-key design. Do not patch the generated entity or data-access service.

7. Runtime glue and package overrides

The built-in UUID expression is emitted into the replaceable generated data-access service. The surrounding create lifecycle belongs to the package-overridable runtime type org.iworkz.spring.persistence.service.AbstractDataAccessService.

The optional org.iworkz:genesis-spring:1.0.77 dependency provides the reference implementation used by the public example. It is convenient for examples, demos and prototypes, but it is not a mandatory JoinedWorkz application runtime.

A production project can provide a compatible implementation under its own package and rewrite generated imports, for example:

properties
override-package.org.iworkz.spring.persistence=com.example.glue.persistence

If the replacement base class changes when the generated hook is invoked or whether an explicit ID wins, that replacement defines the resulting lifecycle. Clean and regenerate all replaceable Java output, compile the complete application and test both missing and explicit IDs before removing the original runtime dependency. The complete workflow and prefix-matching rules are documented in Package overrides for helper libraries.

8. Executable example

The public example-spring-boot module keeps its Customer CRUD model on the assigned-ID default. Its separate model/id-generation.cmn demonstrates:

  • a reusable CUSTOM ID type;
  • a local key-field override to JPA UUID; and
  • two otherwise equivalent persistent record types.

DefaultGeneratedId is only the fixture-local name of that reusable type. It does not establish CUSTOM or any other strategy as a SpringBoot default.

Its H2-backed runtime tests call the generated data-access create(dto) path and check three distinct cases:

  1. CUSTOM creates a UUID when the DTO omits the ID;
  2. CUSTOM retains an explicit UUID with the reference glue; and
  3. JPA UUID creates a UUID during persistence.

The example does not claim runtime coverage for CUSTOM with a String key or for AUTO, IDENTITY, SEQUENCE and TABLE. See the Spring Boot executable example for the build and ownership boundary.