{"record":{"id":"5038be665355aea7","repo":"hibernate/hibernate-orm","slug":"given-entity-does-not-define-natural-id","errorCode":null,"errorMessage":"Given entity [{}] does not define natural-id","messagePattern":"Given entity \\[(.+?)\\] does not define natural-id","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/stat/internal/StatisticsImpl.java","lineNumber":1007,"sourceCode":"\t\t\t\t\",update timestamps cache misses=\" + updateTimestampsCacheMissCount +\n\t\t\t\t\",max query time=\" + queryExecutionMaxTime +\n\t\t\t\t\",query plan cache hits=\" + queryPlanCacheHitCount +\n\t\t\t\t\",query plan cache misses=\" + queryPlanCacheMissCount +\n\t\t\t\t']';\n\t}\n\n\tprivate EntityStatisticsImpl instantiateEntityStatistics(final String entityName) {\n\t\treturn new EntityStatisticsImpl( metamodel.getEntityDescriptor( entityName ) );\n\t}\n\n\tprivate CollectionStatisticsImpl instantiateCollectionStatistics(final String role) {\n\t\treturn new CollectionStatisticsImpl( metamodel.getCollectionDescriptor( role ) );\n\t}\n\n\tprivate NaturalIdStatisticsImpl instantiateNaturalStatistics(final String entityName) {\n\t\tfinal EntityPersister entityDescriptor = metamodel.getEntityDescriptor( entityName );\n\t\tif ( !entityDescriptor.hasNaturalIdentifier() ) {\n\t\t\tthrow new IllegalArgumentException( \"Given entity [\" + entityName + \"] does not define natural-id\" );\n\t\t}\n\t\treturn new NaturalIdStatisticsImpl( entityDescriptor );\n\t}\n\n\tprivate CacheRegionStatisticsImpl instantiateCacheRegionStatistics(final String regionName) {\n\t\tfinal Region region = cache.getRegion( regionName );\n\t\tif ( region == null ) {\n\t\t\tthrow new IllegalArgumentException( \"Unknown cache region : \" + regionName );\n\t\t}\n\t\tif ( region instanceof QueryResultsRegion ) {\n\t\t\tthrow new IllegalArgumentException(\n\t\t\t\t\t\"Region name [\" + regionName + \"] referred to a query result region, not a domain data region\"\n\t\t\t);\n\t\t}\n\t\treturn new CacheRegionStatisticsImpl( region );\n\t}\n\n\tprivate CacheRegionStatisticsImpl instantiateCacheRegionStatsForQueryResults(final String regionName) {","sourceCodeStart":989,"sourceCodeEnd":1025,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/stat/internal/StatisticsImpl.java#L989-L1025","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nNaturalIdStatistics stats = statistics.getNaturalIdStatistics(User.class.getName());\n// throws IllegalArgumentException: User has no natural id\n\n// after\nSessionFactoryImplementor impl = sessionFactory.unwrap(SessionFactoryImplementor.class);\nEntityPersister user = impl.getRuntimeMetamodel().getEntityDescriptor(User.class.getName());\nNaturalIdStatistics stats = user.hasNaturalIdentifier()\n        ? statistics.getNaturalIdStatistics(User.class.getName())\n        : null;","handlingStrategy":"validation","validationCode":"EntityPersister p = sessionFactory.unwrap(SessionFactoryImplementor.class)\n        .getRuntimeMetamodel().getEntityDescriptor(entityName);\nif (p.hasNaturalIdentifier()) {\n    NaturalIdStatistics stats = statistics.getNaturalIdStatistics(entityName);\n}","typeGuard":null,"tryCatchPattern":"try {\n    NaturalIdStatistics stats = statistics.getNaturalIdStatistics(entityName);\n} catch (IllegalArgumentException e) {\n    // entity has no natural id — skip it, do not retry\n    log.debug(\"No natural-id statistics for {}\", entityName);\n}","preventionTips":["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."],"tags":["hibernate","statistics","natural-id","entity-mapping","illegalargumentexception"],"backgroundTag":"natural-id-not-mapped","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}