{"record":{"id":"74aac56b2455378d","repo":"hibernate/hibernate-orm","slug":"s-methods-of-lazy-classes-cannot-be-final-s-s","errorCode":null,"errorMessage":"%s methods of lazy classes cannot be final: %s#%s","messagePattern":"(.+?) methods of lazy classes cannot be final: (.+?)#(.+?)","errorType":"exception","errorClass":"HibernateException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/metamodel/internal/EntityRepresentationStrategyPojoStandard.java","lineNumber":382,"sourceCode":"\t\t\treturn identifierPropertyAccess;\n\t\t}\n\t\telse {\n\t\t\tfinal var propertyAccess = propertyAccessMap.get( bootAttributeDescriptor.getName() );\n\t\t\tif ( propertyAccess != null ) {\n\t\t\t\treturn propertyAccess;\n\t\t\t}\n\t\t\telse if ( mapsIdRepresentationStrategy != null ) {\n\t\t\t\treturn mapsIdRepresentationStrategy.resolvePropertyAccess( bootAttributeDescriptor );\n\t\t\t}\n\t\t\telse {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static void validateGetterSetterMethodProxyability(String getterOrSetter, Method method ) {\n\t\tif ( method != null && isFinal( method.getModifiers() ) ) {\n\t\t\tthrow new HibernateException(\n\t\t\t\t\tString.format(\n\t\t\t\t\t\t\t\"%s methods of lazy classes cannot be final: %s#%s\",\n\t\t\t\t\t\t\tgetterOrSetter,\n\t\t\t\t\t\t\tmethod.getDeclaringClass().getName(),\n\t\t\t\t\t\t\tmethod.getName()\n\t\t\t\t\t)\n\t\t\t);\n\t\t}\n\t}\n}\n","sourceCodeStart":364,"sourceCodeEnd":393,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/metamodel/internal/EntityRepresentationStrategyPojoStandard.java#L364-L393","documentation":"A HibernateException raised at bootstrap by validateGetterSetterMethodProxyability inside EntityRepresentationStrategyPojoStandard: when an entity is mapped as lazy/proxyable, Hibernate must be able to override the access methods of its attributes, so a final getter or setter makes proxy generation impossible. The message states 'Getter'/'Setter' methods of lazy classes cannot be final and names the declaring class and method.","triggerScenarios":"An entity with lazy=true/@Proxy(lazy=true) whose attribute getter or setter is declared final; Kotlin entity classes and properties, which are final by default unless marked open or compiled with the kotlin-allopen compiler plugin; Java classes hardened with 'final' on accessors for immutability.","commonSituations":"Introducing Kotlin entities without the kotlin.plugin.jpa/all-open plugin; adding 'final' to accessors during an immutability refactor; bytecode-obfuscated or code-generated models with final accessors; enabling lazy proxying on previously eagerly-loaded hierarchies.","solutions":["Remove 'final' from the reported getter/setter (and from the class if it is final) so the proxy can subclass it","For Kotlin, apply the all-open compiler plugin with the JPA targets (@Entity, @MappedSuperclass, @Embeddable) so classes and members are generated open","Alternatively disable proxy-based laziness for this entity with @Proxy(lazy = false) if you accept eager loading","Re-run SessionFactory creation - the validation fails fast at startup"],"exampleFix":"// Kotlin - before: final by default\n@Entity\nclass Person(@get:Column val name: String)   // getter is final -> HibernateException\n\n// Kotlin - after: all-open plugin makes @Entity classes open\n// build.gradle.kts: plugins { kotlin(\"plugin.jpa\") version \"...\" }\n@Entity\nclass Person(@get:Column open val name: String)\n\n// Java - before/after\npublic final String getName() { ... }   // -> remove 'final'\npublic String getName() { ... }","handlingStrategy":"validation","validationCode":"// Fail fast if a lazy-mapped entity has final accessors\nfor ( EntityType<?> t : emf.getMetamodel().getEntities() ) {\n    if ( isLazyMapped(t) ) {\n        for ( java.lang.reflect.Method m : t.getJavaType().getMethods() ) {\n            if ( ( m.getName().startsWith(\"get\") || m.getName().startsWith(\"set\") )\n                    && Modifier.isFinal(m.getModifiers()) ) {\n                throw new IllegalStateException(\"Final accessor blocks proxy: \" + m);\n            }\n        }\n    }\n}","typeGuard":"static boolean proxyableAccessor(Method m) { return !Modifier.isFinal(m.getModifiers()); }","tryCatchPattern":"try {\n    EntityManagerFactory emf = Persistence.createEntityManagerFactory(\"pu\");\n}\ncatch ( org.hibernate.HibernateException e ) {\n    if ( e.getMessage() != null && e.getMessage().contains(\"cannot be final\") ) {\n        throw new ConfigurationError(\"Remove 'final' from the named accessor or disable @Proxy lazy - \" + e.getMessage(), e);\n    }\n    throw e;\n}","preventionTips":["Never mark getters/setters of lazy/proxyable entities final (and avoid final entity classes)","Kotlin projects: apply the kotlin-alopen/JPA plugin so @Entity members are open","When introducing lazy proxying onto existing models, scan accessors for 'final' first"],"tags":["hibernate","proxy","lazy-initialization","final-method","kotlin","all-open"],"backgroundTag":"final-method-blocking-proxy","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}