{"record":{"id":"b7b71edcd8b37bb1","repo":"hibernate/hibernate-orm","slug":"mapping-for-entity-listener-specified-no-callback","errorCode":null,"errorMessage":"Mapping for entity listener specified no callback methods: %s","messagePattern":"Mapping for entity listener specified no callback methods: (.+?)","errorType":"exception","errorClass":"ModelsException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/boot/models/internal/GlobalRegistrationsImpl.java","lineNumber":742,"sourceCode":"\t\t\tfinal var lifecycleEventHandler = LifecycleEventHandler.from(\n\t\t\t\t\t\tJpaEventListenerStyle.LISTENER,\n\t\t\t\t\t\tlistenerClassDetails,\n\t\t\t\t\t\tjaxbEntityListener,\n\t\t\t\t\t\tmodelsContext,\n\t\t\t\t\t\tLifecycleEventHandler.hasExplicitXmlCallbackMappings( jaxbEntityListener )\n\t\t\t);\n\t\t\tfinal var persistenceUnitLifecycleEventHandler =\n\t\t\t\t\tPersistenceUnitLifecycleEventHandler.from( listenerClassDetails, jaxbEntityListener );\n\n\t\t\tif ( lifecycleEventHandler.hasCallbackMethods() ) {\n\t\t\t\taddJpaEventListener( lifecycleEventHandler );\n\t\t\t}\n\t\t\tif ( persistenceUnitLifecycleEventHandler.hasCallbackMethods() ) {\n\t\t\t\taddPersistenceUnitLifecycleEventHandler( persistenceUnitLifecycleEventHandler );\n\t\t\t}\n\t\t\tif ( !lifecycleEventHandler.hasCallbackMethods()\n\t\t\t\t\t&& !persistenceUnitLifecycleEventHandler.hasCallbackMethods() ) {\n\t\t\t\tthrow new ModelsException( \"Mapping for entity listener specified no callback methods: \"\n\t\t\t\t\t\t+ listenerClassDetails.getClassName() );\n\t\t\t}\n\t\t} );\n\t}\n\n\tpublic void addJpaEventListener(LifecycleEventHandler listener) {\n\t\tif ( lifecycleEventHandlers == null ) {\n\t\t\tlifecycleEventHandlers = new ArrayList<>();\n\t\t}\n\n\t\tlifecycleEventHandlers.add( listener );\n\t}\n\n\tpublic void addTargetedJpaEventListener(ClassDetails listenerClassDetails) {\n\t\tfinal var persistenceUnitHandler = PersistenceUnitLifecycleEventHandler.from( listenerClassDetails );\n\t\tif ( persistenceUnitHandler.hasCallbackMethods() ) {\n\t\t\taddPersistenceUnitLifecycleEventHandler( persistenceUnitHandler );\n\t\t}","sourceCodeStart":724,"sourceCodeEnd":760,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/boot/models/internal/GlobalRegistrationsImpl.java#L724-L760","documentation":"While collecting global registrations from mapping files, Hibernate builds a LifecycleEventHandler from each declared entity-listener class and its XML callback definitions. If neither source yields any callback method - the class carries no JPA callback annotations and the XML declares none - registration fails with a ModelsException naming the listener class.","triggerScenarios":"A mapping file declares <entity-listener class='com.acme.AuditListener'/> (directly or under <entity-listeners>) but AuditListener contains no @PrePersist/@PostPersist/@PreUpdate/@PostUpdate/@PreRemove/@PostRemove/@PostLoad methods and the XML element declares no <pre-persist/>-style callbacks inside it.","commonSituations":"Listener classes using framework annotations (e.g. Spring @EventListener) or interceptor interfaces instead of JPA callbacks; leftovers after moving callbacks out; registering a listener that exists only for persistence-unit lifecycle events.","solutions":["Add at least one JPA callback to the listener, e.g. @PrePersist void onPrePersist(Object entity) {...}, or declare the callbacks inside the <entity-listener> element in XML","Verify the annotation import is jakarta.persistence (or javax.persistence on older stacks) - lookalike packages are silently ignored","If the class is not meant to observe entity lifecycle, remove its registration"],"exampleFix":"// before - AuditListener registered in mapping.xml but has no callbacks\npublic class AuditListener { }\n\n// after\npublic class AuditListener {\n    @PrePersist\n    void onPrePersist(Object entity) { ... }\n}","handlingStrategy":"validation","validationCode":"// Pre-flight in tests: every registered listener must expose at least one JPA callback\nstatic final List<Class<? extends Annotation>> CB = List.of(\n        jakarta.persistence.PrePersist.class, jakarta.persistence.PostPersist.class,\n        jakarta.persistence.PreUpdate.class, jakarta.persistence.PostUpdate.class,\n        jakarta.persistence.PreRemove.class, jakarta.persistence.PostRemove.class,\n        jakarta.persistence.PostLoad.class);\nstatic boolean hasCallback(Class<?> listener) {\n    return java.util.Arrays.stream(listener.getDeclaredMethods())\n            .flatMap(m -> java.util.Arrays.stream(m.getAnnotations()))\n            .anyMatch(a -> CB.contains(a.annotationType()));\n}\n// assertEquals over the set of listeners registered in orm.xml before bootstrap","typeGuard":null,"tryCatchPattern":"catch (org.hibernate.models.ModelsException e) during bootstrap: the message names the listener class - open it and add a JPA callback annotation or declare the callback in XML inside <entity-listener>","preventionTips":["Verify listener imports are jakarta.persistence (javax on legacy stacks) after every migration","Add a unit test asserting each registered listener declares at least one callback","Do not register classes whose lifecycle role is handled by framework events - Hibernate only sees JPA callbacks"],"tags":["hibernate","jpa","entity-listener","callbacks","orm"],"backgroundTag":"entity-listener-no-callbacks","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}