hibernate/hibernate-orm · error · IllegalArgumentException
Given entity [{}] does not define natural-id
Error message
Given entity [{}] does not define natural-id What it means
Thrown by Statistics#getNaturalIdStatistics(String) when the named entity has no natural id in its mapping. Hibernate looks up the EntityPersister and checks hasNaturalIdentifier() before building NaturalIdStatisticsImpl; natural-id resolution and cache counters only exist for entities that declare @NaturalId (or <natural-id/>), so the request is rejected with an IllegalArgumentException instead of returning empty counters.
Source
Thrown at hibernate-core/src/main/java/org/hibernate/stat/internal/StatisticsImpl.java:1007
",update timestamps cache misses=" + updateTimestampsCacheMissCount +
",max query time=" + queryExecutionMaxTime +
",query plan cache hits=" + queryPlanCacheHitCount +
",query plan cache misses=" + queryPlanCacheMissCount +
']';
}
private EntityStatisticsImpl instantiateEntityStatistics(final String entityName) {
return new EntityStatisticsImpl( metamodel.getEntityDescriptor( entityName ) );
}
private CollectionStatisticsImpl instantiateCollectionStatistics(final String role) {
return new CollectionStatisticsImpl( metamodel.getCollectionDescriptor( role ) );
}
private NaturalIdStatisticsImpl instantiateNaturalStatistics(final String entityName) {
final EntityPersister entityDescriptor = metamodel.getEntityDescriptor( entityName );
if ( !entityDescriptor.hasNaturalIdentifier() ) {
throw new IllegalArgumentException( "Given entity [" + entityName + "] does not define natural-id" );
}
return new NaturalIdStatisticsImpl( entityDescriptor );
}
private CacheRegionStatisticsImpl instantiateCacheRegionStatistics(final String regionName) {
final Region region = cache.getRegion( regionName );
if ( region == null ) {
throw new IllegalArgumentException( "Unknown cache region : " + regionName );
}
if ( region instanceof QueryResultsRegion ) {
throw new IllegalArgumentException(
"Region name [" + regionName + "] referred to a query result region, not a domain data region"
);
}
return new CacheRegionStatisticsImpl( region );
}
private CacheRegionStatisticsImpl instantiateCacheRegionStatsForQueryResults(final String regionName) {View on GitHub (pinned to fad1729dce)
Solutions
- If the entity should have a natural id, map one: annotate the immutable, unique properties with @NaturalId (or add <natural-id/> in hbm.xml).
- Guard the call: request natural-id statistics only for entities whose EntityPersister#hasNaturalIdentifier() returns true.
- Verify the entity-name string (FQN or explicit entity name) so it resolves to the entity you expect.
Example fix
// before
NaturalIdStatistics stats = statistics.getNaturalIdStatistics(User.class.getName());
// throws IllegalArgumentException: User has no natural id
// after
SessionFactoryImplementor impl = sessionFactory.unwrap(SessionFactoryImplementor.class);
EntityPersister user = impl.getRuntimeMetamodel().getEntityDescriptor(User.class.getName());
NaturalIdStatistics stats = user.hasNaturalIdentifier()
? statistics.getNaturalIdStatistics(User.class.getName())
: null; Defensive patterns
Strategy: validation
Validate before calling
EntityPersister p = sessionFactory.unwrap(SessionFactoryImplementor.class)
.getRuntimeMetamodel().getEntityDescriptor(entityName);
if (p.hasNaturalIdentifier()) {
NaturalIdStatistics stats = statistics.getNaturalIdStatistics(entityName);
} Try / catch
try {
NaturalIdStatistics stats = statistics.getNaturalIdStatistics(entityName);
} catch (IllegalArgumentException e) {
// entity has no natural id — skip it, do not retry
log.debug("No natural-id statistics for {}", entityName);
} Prevention
- Query natural-id statistics only for entities annotated with @NaturalId.
- Centralize the hasNaturalIdentifier() guard in one helper used by all monitoring code.
- Keep the list of natural-id-bearing entities in configuration instead of scattering entity-name strings through code.
When it happens
Trigger: Calling statistics.getNaturalIdStatistics("com.acme.User") (or the natural-id cache statistics accessor) for an entity whose mapping has no @NaturalId property and no <natural-id/> element: instantiateNaturalStatistics resolves the descriptor, sees hasNaturalIdentifier() == false, and throws before constructing the statistics object.
Common situations: A monitoring dashboard iterates every entity name from the metamodel and unconditionally asks for natural-id statistics; the call was written against an entity that later lost its @NaturalId mapping; the entity-name string resolves to a different, natural-id-free entity.
Related errors
- Entity '<entityName>' is not audited
- Entity '<entityName>' is not audited
- %s
- Duplicate entity definition '%s'
- persistent class not known: {}
AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22).
Data as JSON: /api/errors/5038be665355aea7.
Report an issue: GitHub.