{"record":{"id":"8c9ca3bef7f0030d","repo":"hibernate/hibernate-orm","slug":"proxy-must-be-either-an-interface-or-the-class-it","errorCode":null,"errorMessage":"proxy must be either an interface, or the class itself: {}","messagePattern":"proxy must be either an interface, or the class itself: (.+?)","errorType":"exception","errorClass":"MappingException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/metamodel/internal/EntityRepresentationStrategyPojoStandard.java","lineNumber":247,"sourceCode":"\t\t\t\tmappedClass,\n\t\t\t\tproxyInterfaces\n\t\t);\n\t}\n\n\tprivate static Set<Class<?>> proxyInterfaces(\n\t\t\tPersistentClass bootDescriptor,\n\t\t\tClass<?> proxyInterface,\n\t\t\tClass<?> mappedClass) {\n\t\t// HHH-17578 - We need to preserve the order of the interfaces to ensure\n\t\t// that the most general @Proxy declared interface at the top of a class\n\t\t// hierarchy will be used first when a HibernateProxy decides what it\n\t\t// should implement.\n\n\t\tfinal Set<Class<?>> proxyInterfaces = new LinkedHashSet<>();\n\n\t\tif ( proxyInterface != null && ! mappedClass.equals( proxyInterface ) ) {\n\t\t\tif ( ! proxyInterface.isInterface() ) {\n\t\t\t\tthrow new MappingException( \"proxy must be either an interface, or the class itself: \"\n\t\t\t\t\t\t\t\t\t\t\t+ bootDescriptor.getEntityName() );\n\t\t\t}\n\t\t\tproxyInterfaces.add( proxyInterface );\n\t\t}\n\n\t\tif ( mappedClass.isInterface() ) {\n\t\t\tproxyInterfaces.add( mappedClass );\n\t\t}\n\n\t\tfor ( var subclass : bootDescriptor.getSubclasses() ) {\n\t\t\tfinal var subclassProxy = subclass.getProxyInterface();\n\t\t\tfinal var subclassClass = subclass.getMappedClass();\n\t\t\tif ( subclassProxy != null && !subclassClass.equals( subclassProxy ) ) {\n\t\t\t\tif ( !subclassProxy.isInterface() ) {\n\t\t\t\t\tthrow new MappingException( \"proxy must be either an interface, or the class itself: \"\n\t\t\t\t\t\t\t\t\t\t\t\t+ subclass.getEntityName() );\n\t\t\t\t}\n\t\t\t\tproxyInterfaces.add( subclassProxy );","sourceCodeStart":229,"sourceCodeEnd":265,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/metamodel/internal/EntityRepresentationStrategyPojoStandard.java#L229-L265","documentation":"A org.hibernate.mapping.MappingException thrown while Hibernate builds the proxy interface set for a POJO entity (EntityRepresentationStrategyPojoStandard, HHH-17578-related logic). If a @Proxy(proxyClass=...) declaration (or <proxy interface=...>) on the root entity names a class that is neither an interface nor the entity's own mapped class, the mapping is invalid because Hibernate can only generate proxies for interfaces, and bootstrap fails with the entity name in the message.","triggerScenarios":"Specifying @org.hibernate.annotations.Proxy(proxyClass = SomeConcreteClass.class) where that class is a concrete class different from the entity; XML <class ... proxy=\"com.acme.ConcreteThing\"> pointing at a class; accidentally passing the entity's superclass (a class) instead of its interface.","commonSituations":"Copying @Proxy annotations between hierarchies; intending to disable proxying and mistakenly pointing proxyClass at the impl class of a different type; refactoring an interface into a base class without updating the annotation.","solutions":["Point @Proxy(proxyClass=...) at an interface the entity implements","To disable proxying for the entity, use @Proxy(lazy = false) instead of naming a class","Omit proxyClass entirely when the default (entity-class proxying decision) is fine","After fixing, rebuild - this fails at SessionFactory creation so tests catch it immediately"],"exampleFix":"// before\n@Entity\n@Proxy(proxyClass = OrderImpl.class)   // concrete class -> MappingException\npublic class OrderImpl implements Order { ... }\n\n// after\n@Entity\n@Proxy(proxyClass = Order.class)       // interface\npublic class OrderImpl implements Order { ... }\n\n// or disable proxying\n@Entity\n@Proxy(lazy = false)\npublic class OrderImpl implements Order { ... }","handlingStrategy":"validation","validationCode":"// Scan @Proxy declarations at build time\nfor ( Class<?> c : mappedEntityClasses() ) {\n    Proxy p = c.getAnnotation(Proxy.class);\n    if ( p != null && !void.class.equals(p.proxyClass()) && !p.proxyClass().isInterface() && !p.proxyClass().equals(c) ) {\n        throw new IllegalStateException(\"@Proxy on \" + c + \" names a concrete class \" + p.proxyClass());\n    }\n}","typeGuard":"static boolean isValidProxyDeclaration(Class<?> entity, Class<?> proxyClass) {\n    return proxyClass.isInterface() || proxyClass.equals(entity);\n}","tryCatchPattern":"try {\n    return sessionFactoryBuilder.build();\n}\ncatch ( org.hibernate.mapping.MappingException e ) {\n    if ( e.getMessage() != null && e.getMessage().startsWith(\"proxy must be either an interface\") ) {\n        throw new ConfigurationError(\"Fix @Proxy(proxyClass=...) - must be an interface: \" + e.getMessage(), e);\n    }\n    throw e;\n}","preventionTips":["Only point @Proxy(proxyClass=...) at interfaces the entity implements","Use @Proxy(lazy = false) to disable proxying instead of naming a class","Build the SessionFactory in a CI smoke test so mapping errors surface before deployment"],"tags":["hibernate","proxy","mapping","lazy-initialization","bootstrap"],"backgroundTag":"invalid-proxy-mapping","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}