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
generationproperty 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:
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:
type<entity> JpaGeneratedRecord {
id**: GeneratedRecordId generation='UUID'
label*: String(100)
}You can also configure a one-off key directly:
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 value | Generated entity | ID producer | JoinedWorkz contract |
|---|---|---|---|
| not set | @Id | calling application | Assigned ID; JoinedWorkz does not generate a missing value |
CUSTOM | @Id | generated data-access service | Stable for effective Java key type UUID or String |
UUID | @Id plus @GeneratedValue(strategy = GenerationType.UUID) | JPA provider | Advanced provider-managed path |
AUTO | @Id plus GenerationType.AUTO | JPA provider | Compatibility pass-through |
IDENTITY | @Id plus GenerationType.IDENTITY | JPA provider and database | Compatibility pass-through |
SEQUENCE | @Id plus GenerationType.SEQUENCE | JPA provider and database | Compatibility pass-through |
TABLE | @Id plus GenerationType.TABLE | JPA provider and database | Compatibility 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
UUIDkey receivesUUID.randomUUID(); - a Java
Stringkey receivesUUID.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:
- retain a non-null ID supplied by the caller;
- otherwise generate the
CUSTOMID; - 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:
| Diagnostic | Meaning |
|---|---|
springboot.invalidIdGenerationStrategy | The effective key strategy is not one of CUSTOM, UUID, AUTO, IDENTITY, SEQUENCE or TABLE |
springboot.unsupportedCustomIdGenerationType | CUSTOM is used with an effective Java key type other than UUID or String |
springboot.unsupportedCompositeIdGeneration | At 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:
override-package.org.iworkz.spring.persistence=com.example.glue.persistenceIf 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
CUSTOMID 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:
CUSTOMcreates a UUID when the DTO omits the ID;CUSTOMretains an explicit UUID with the reference glue; and- JPA
UUIDcreates 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.
