{"record":{"id":"f2496f108f05d383","repo":"hibernate/hibernate-orm","slug":"entitypersister-implementation-classname-does","errorCode":null,"errorMessage":"EntityPersister implementation '{className}' does not support 'NaturalIdLoader'","messagePattern":"EntityPersister implementation '(.+?)' does not support 'NaturalIdLoader'","errorType":"exception","errorClass":"UnsupportedOperationException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/persister/entity/EntityPersister.java","lineNumber":597,"sourceCode":"\t\t\t\t\t\ty,\n\t\t\t\t\t\tvalueConsumer,\n\t\t\t\t\t\tsession\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t\treturn span;\n\t}\n\n\t/**\n\t * Determine whether this entity defines any lazy properties (when bytecode\n\t * instrumentation is enabled).\n\t *\n\t * @return True if the entity has properties mapped as lazy; false otherwise.\n\t */\n\tboolean hasLazyProperties();\n\n\tdefault NaturalIdLoader<?> getNaturalIdLoader() {\n\t\tthrow new UnsupportedOperationException(\n\t\t\t\t\"EntityPersister implementation '\" + getClass().getName()\n\t\t\t\t\t\t+ \"' does not support 'NaturalIdLoader'\"\n\t\t);\n\t}\n\n\tdefault MultiNaturalIdLoader<?> getMultiNaturalIdLoader() {\n\t\tthrow new UnsupportedOperationException(\n\t\t\t\t\"EntityPersister implementation '\" + getClass().getName()\n\t\t\t\t\t\t+ \"' does not support 'MultiNaturalIdLoader'\"\n\t\t);\n\t}\n\n\t/**\n\t * Load an instance of the persistent class.\n\t */\n\tObject load(Object id, Object optionalObject, LockMode lockMode, SharedSessionContractImplementor session);\n\n\t/**","sourceCodeStart":579,"sourceCodeEnd":615,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/persister/entity/EntityPersister.java#L579-L615","documentation":"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.","triggerScenarios":"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().","commonSituations":"Custom persisters for legacy integration; wrapper/decorating persisters added for instrumentation; unit tests with stub persisters; third-party persister providers.","solutions":["Implement getNaturalIdLoader() (and getMultiNaturalIdLoader()) in the custom persister, modelling AbstractEntityPersister's implementation","Remove the custom persister for entities that need natural-id access","Use id-based loading (session.find / em.find) instead of the natural-id API for that entity"],"exampleFix":"// before\nMyEntity e = session.bySimpleNaturalId(MyEntity.class).load(\"ABC\"); // custom persister -> UnsupportedOperationException\n\n// after\nMyEntity e = session.find(MyEntity.class, id); // PK lookup works on any persister","handlingStrategy":"validation","validationCode":"EntityPersister p = sessionFactory.getRuntimeMetamodels()\n        .getMappingMetamodel()\n        .getEntityDescriptor(Product.class);\nif ( p.hasNaturalIdentifier()\n        && p instanceof org.hibernate.persister.entity.AbstractEntityPersister ) {\n    Object loaded = session.byNaturalId(Product.class)\n                          .using(\"code\", code)\n                          .load();\n}","typeGuard":"static boolean supportsNaturalIdLoad(EntityPersister persister) {\n    return persister.hasNaturalIdentifier()\n        && persister instanceof org.hibernate.persister.entity.AbstractEntityPersister;\n}","tryCatchPattern":null,"preventionTips":["Verify the persister type before using natural-id APIs on entities mapped with @Persister","Cover natural-id load paths with integration tests for every entity using a custom persister","Prefer em.find by primary key when a custom persister is unavoidable"],"tags":["hibernate","orm","natural-id","persister","api-unsupported"],"backgroundTag":"natural-id-not-supported","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}