hibernate/hibernate-orm · error · UnsupportedOperationException

EntityPersister implementation '{className}' does not suppor

Error message

EntityPersister implementation '{className}' does not support 'NaturalIdLoader'

What it means

EntityPersister.getNaturalIdLoader() backs the natural-id load API (session.byNaturalId(...), bySimpleNaturalId(...)). AbstractEntityPersister implements it; the interface default throws UnsupportedOperationException('EntityPersister implementation ... does not support NaturalIdLoader'), so persisters outside that hierarchy — custom implementations that do not override the method — fail the moment a natural-id query runs, regardless of whether @NaturalId is mapped.

Source

Thrown at hibernate-core/src/main/java/org/hibernate/persister/entity/EntityPersister.java:597

						y,
						valueConsumer,
						session
				);
			}
		}
		return span;
	}

	/**
	 * Determine whether this entity defines any lazy properties (when bytecode
	 * instrumentation is enabled).
	 *
	 * @return True if the entity has properties mapped as lazy; false otherwise.
	 */
	boolean hasLazyProperties();

	default NaturalIdLoader<?> getNaturalIdLoader() {
		throw new UnsupportedOperationException(
				"EntityPersister implementation '" + getClass().getName()
						+ "' does not support 'NaturalIdLoader'"
		);
	}

	default MultiNaturalIdLoader<?> getMultiNaturalIdLoader() {
		throw new UnsupportedOperationException(
				"EntityPersister implementation '" + getClass().getName()
						+ "' does not support 'MultiNaturalIdLoader'"
		);
	}

	/**
	 * Load an instance of the persistent class.
	 */
	Object load(Object id, Object optionalObject, LockMode lockMode, SharedSessionContractImplementor session);

	/**

View on GitHub (pinned to fad1729dce)

Solutions

  1. Implement getNaturalIdLoader() (and getMultiNaturalIdLoader()) in the custom persister, modelling AbstractEntityPersister's implementation
  2. Remove the custom persister for entities that need natural-id access
  3. Use id-based loading (session.find / em.find) instead of the natural-id API for that entity

Example fix

// before
MyEntity e = session.bySimpleNaturalId(MyEntity.class).load("ABC"); // custom persister -> UnsupportedOperationException

// after
MyEntity e = session.find(MyEntity.class, id); // PK lookup works on any persister
Defensive patterns

Strategy: validation

Validate before calling

EntityPersister p = sessionFactory.getRuntimeMetamodels()
        .getMappingMetamodel()
        .getEntityDescriptor(Product.class);
if ( p.hasNaturalIdentifier()
        && p instanceof org.hibernate.persister.entity.AbstractEntityPersister ) {
    Object loaded = session.byNaturalId(Product.class)
                          .using("code", code)
                          .load();
}

Type guard

static boolean supportsNaturalIdLoad(EntityPersister persister) {
    return persister.hasNaturalIdentifier()
        && persister instanceof org.hibernate.persister.entity.AbstractEntityPersister;
}

Prevention

When it happens

Trigger: session.byNaturalId(MyEntity.class).using("code", value).load() or session.bySimpleNaturalId(MyEntity.class).simpleLoad() against an entity whose persister is a custom EntityPersister implementation (persisterClass/@Persister) that does not override getNaturalIdLoader().

Common situations: Custom persisters for legacy integration; wrapper/decorating persisters added for instrumentation; unit tests with stub persisters; third-party persister providers.

Related errors


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