hibernate/hibernate-orm · error · IdentifierGenerationException
Null id generated for entity '
Error message
Null id generated for entity '
What it means
When an entity has a composite id with values generated before execution (CompositeNestedGeneratedValueGenerator, e.g. @GeneratedValue attributes inside an @EmbeddedId), AbstractSaveEventListener pre-generates the id instance during persist. If that generation returns null it throws IdentifierGenerationException('Null id generated for entity ...'): one of the nested per-attribute generators, or the mapping around it, produced no value.
Source
Thrown at hibernate-core/src/main/java/org/hibernate/event/internal/AbstractSaveEventListener.java:109
protected Object saveWithGeneratedId(
@Nonnull Object entity,
@Nullable String entityName,
@Nonnull C context,
@Nonnull EventSource source,
boolean requiresImmediateIdAccess) {
final var persister = source.getEntityPersister( entityName, entity );
final var generator = persister.getGenerator();
final boolean generatedOnExecution = generator.generatedOnExecution( entity, source );
final boolean generatedBeforeExecution = generator.generatedBeforeExecution( entity, source );
final Object generatedId;
if ( generatedOnExecution ) {
if ( generatedBeforeExecution
&& generator instanceof CompositeNestedGeneratedValueGenerator compositeGenerator ) {
// for a composite id, we might need to
// create the composite id instance early
final Object preGeneratedId = compositeGenerator.generate( source, entity );
if ( preGeneratedId == null ) {
throw new IdentifierGenerationException(
"Null id generated for entity '" + persister.getEntityName() + "'" );
}
persister.setIdentifier( entity, preGeneratedId, source );
}
// the id gets generated by the database and is
// not yet available
generatedId = null;
}
else if ( !generator.generatesOnInsert() ) {
// get it from the entity later, since we need
// the @PrePersist callback to happen first
generatedId = null;
}
else if ( generatedBeforeExecution ) {
// go ahead and generate id, and then set it to
// the entity instance, so it will be available
// to the entity in the @PrePersist callback
generatedId = generateId( entity, source, (BeforeExecutionGenerator) generator, persister );View on GitHub (pinned to fad1729dce)
Solutions
- Review each @GeneratedValue attribute inside the composite id and replace custom generators with built-ins (sequence, uuid) or fix their null path
- Set every manually-assigned attribute of the composite id before calling persist()
- Write a minimal persist test for the entity to identify which id attribute generates null
- If the mapping mixes assigned and generated parts awkwardly, restructure to a single generated @Id or a fully-assigned composite id
Example fix
// before: composite id with a custom generator that can return null
@Embeddable
class OrderId {
@GeneratedValue(generator = "myGen")
Long number;
String tenant;
}
// after: built-in sequence for the generated part, assigned part set before persist
@Embeddable
class OrderId {
@GeneratedValue(generator = "order_seq")
@SequenceGenerator(name = "order_seq", sequenceName = "order_seq", allocationSize = 50)
Long number;
String tenant;
}
order.setId(new OrderId(null, "acme")); Defensive patterns
Strategy: validation
Validate before calling
// before persist(): every assigned attribute of the composite id must be non-null
OrderId id = order.getId();
if (id == null || id.getTenant() == null) { // list all assigned parts
throw new IllegalStateException("assigned parts of the composite id must be set before persist()");
}
em.persist(order); Type guard
static boolean isCompositeIdComplete(OrderId id) {
return id != null && id.getTenant() != null; // check each assigned part
} Try / catch
try {
em.persist(order);
} catch (IdentifierGenerationException e) {
// identify which @GeneratedValue attribute inside the composite id produced null
throw new IllegalStateException("composite id generation failed for " + order.getClass().getName(), e);
} Prevention
- Use built-in generators (sequence/uuid) for generated parts of composite ids
- Initialize assigned parts of the id in the entity factory method
- Add a persist test for every composite-id entity
- Avoid mixing custom generators inside @EmbeddedId unless they are unit-tested
When it happens
Trigger: A @GeneratedValue attribute inside a composite id whose generator can return null (custom generator with an unhandled path); the manually-assigned part of the composite is null so the nested generator cannot assemble the id; generator type mismatched to the attribute type (e.g. a UUID strategy feeding a numeric field).
Common situations: Entities with @EmbeddedId/@IdClass containing @GeneratedValue members migrated to newer Hibernate where composite pre-generation semantics changed; custom BeforeExecutionGenerator implementations embedded in composite ids; test fixtures persisting entities without initializing the assigned part of the id.
Related errors
- Identifier of entity '
- Identifier field '{}' named in '@MapsId' does not exist in e
- Property '${property}' is annotated '@OptimisticLock(exclude
- Property '" + getPath( propertyHolder, inferredData ) + "' b
- Attribute '%s' of entity '%s' is mapped by association '%s'
AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22).
Data as JSON: /api/errors/e803e4c7a196143b.
Report an issue: GitHub.