{"record":{"id":"5165ee28089b11d7","repo":"hibernate/hibernate-orm","slug":"entity-name-does-not-have-a-natural-id","errorCode":null,"errorMessage":"Entity '{name}' does not have a natural id","messagePattern":"Entity '(.+?)' does not have a natural id","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/persister/entity/BaseEntityPersister.java","lineNumber":631,"sourceCode":"\tprivate void mapPropertyToIndex(Property property, int i) {\n\t\tpropertyIndexes.put( property.getName(), i );\n\t\tif ( property.getValue() instanceof Component composite ) {\n\t\t\tfor ( var subproperty : composite.getProperties() ) {\n\t\t\t\tpropertyIndexes.put(\n\t\t\t\t\t\tproperty.getName() + '.' + subproperty.getName(),\n\t\t\t\t\t\ti\n\t\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * @return {@code true} if one of the properties belonging to the natural id\n\t *         is generated during the execution of an {@code insert} statement\n\t */\n\tpublic boolean isNaturalIdentifierInsertGenerated() {\n\t\tif ( naturalIdPropertyNumbers.length == 0 ) {\n\t\t\tthrow new IllegalStateException( \"Entity '\" + name + \"' does not have a natural id\" );\n\t\t}\n\t\tfor ( int naturalIdPropertyNumber : naturalIdPropertyNumbers ) {\n\t\t\tfinal var strategy = generators[naturalIdPropertyNumber];\n\t\t\tif ( strategy != null\n\t\t\t\t\t&& strategy.generatesOnInsert()\n\t\t\t\t\t&& strategy.generatedOnExecution() ) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\tpublic int[] getNaturalIdentifierProperties() {\n\t\treturn naturalIdPropertyNumbers;\n\t}\n\n\tpublic boolean hasNaturalIdentifier() {\n\t\treturn naturalIdPropertyNumbers!=null;","sourceCodeStart":613,"sourceCodeEnd":649,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/persister/entity/BaseEntityPersister.java#L613-L649","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Guard every call with EntityPersister.hasNaturalIdentifier() before probing","Define @NaturalId on the entity if it is supposed to have one","Fix generic framework code to skip entities without natural ids"],"exampleFix":"// before\nboolean generated = persister.isNaturalIdentifierInsertGenerated(); // throws if no natural id\n\n// after\nboolean generated = persister.hasNaturalIdentifier()\n        && persister.isNaturalIdentifierInsertGenerated();","handlingStrategy":"validation","validationCode":"EntityPersister p = sessionFactory.getRuntimeMetamodels()\n        .getMappingMetamodel()\n        .getEntityDescriptor(User.class);\nboolean generated = p.hasNaturalIdentifier()\n        && p.isNaturalIdentifierInsertGenerated();","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always pair capability probes with hasNaturalIdentifier() in generic framework code","Keep an inventory of which entities have @NaturalId when adding natural-id caching or post-insert hooks","Write one unit test per framework probe that runs it over an entity without a natural id"],"tags":["hibernate","orm","natural-id","persister","illegal-state"],"backgroundTag":"missing-natural-id","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}