hibernate/hibernate-orm · error · IllegalStateException

Entity '{name}' does not have a natural id

Error message

Entity '{name}' does not have a natural id

What it means

BaseEntityPersister.isNaturalIdentifierInsertGenerated() answers whether any natural-id property is generated during INSERT execution; it assumes the entity has a natural id and throws IllegalStateException('Entity ... does not have a natural id') when naturalIdPropertyNumbers is empty. It is a persister capability probe reached from insert/natural-id handling paths, not a user query API, so hitting it usually means generic code probed an entity that has no @NaturalId properties.

Source

Thrown at hibernate-core/src/main/java/org/hibernate/persister/entity/BaseEntityPersister.java:631

	private void mapPropertyToIndex(Property property, int i) {
		propertyIndexes.put( property.getName(), i );
		if ( property.getValue() instanceof Component composite ) {
			for ( var subproperty : composite.getProperties() ) {
				propertyIndexes.put(
						property.getName() + '.' + subproperty.getName(),
						i
					);
			}
		}
	}

	/**
	 * @return {@code true} if one of the properties belonging to the natural id
	 *         is generated during the execution of an {@code insert} statement
	 */
	public boolean isNaturalIdentifierInsertGenerated() {
		if ( naturalIdPropertyNumbers.length == 0 ) {
			throw new IllegalStateException( "Entity '" + name + "' does not have a natural id" );
		}
		for ( int naturalIdPropertyNumber : naturalIdPropertyNumbers ) {
			final var strategy = generators[naturalIdPropertyNumber];
			if ( strategy != null
					&& strategy.generatesOnInsert()
					&& strategy.generatedOnExecution() ) {
				return true;
			}
		}
		return false;
	}

	public int[] getNaturalIdentifierProperties() {
		return naturalIdPropertyNumbers;
	}

	public boolean hasNaturalIdentifier() {
		return naturalIdPropertyNumbers!=null;

View on GitHub (pinned to fad1729dce)

Solutions

  1. Guard every call with EntityPersister.hasNaturalIdentifier() before probing
  2. Define @NaturalId on the entity if it is supposed to have one
  3. Fix generic framework code to skip entities without natural ids

Example fix

// before
boolean generated = persister.isNaturalIdentifierInsertGenerated(); // throws if no natural id

// after
boolean generated = persister.hasNaturalIdentifier()
        && persister.isNaturalIdentifierInsertGenerated();
Defensive patterns

Strategy: validation

Validate before calling

EntityPersister p = sessionFactory.getRuntimeMetamodels()
        .getMappingMetamodel()
        .getEntityDescriptor(User.class);
boolean generated = p.hasNaturalIdentifier()
        && p.isNaturalIdentifierInsertGenerated();

Prevention

When it happens

Trigger: Calling EntityPersister.isNaturalIdentifierInsertGenerated() (directly, or via framework/session-internal code that updates the natural-id region after insert) on an entity with no @NaturalId; generic scaffolding that iterates all persisters and probes this capability unconditionally.

Common situations: Generic DAO/audit/multi-tenant frameworks probing every persister; enabling natural-id caching globally while some entities lack natural ids; removing a base-class @NaturalId during refactoring while framework code still probes it.

Related errors


AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22). Data as JSON: /api/errors/5165ee28089b11d7. Report an issue: GitHub.