hibernate/hibernate-orm · error · ModelsException
Mapping for entity listener specified no callback methods: %
Error message
Mapping for entity listener specified no callback methods: %s
What it means
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.
Source
Thrown at hibernate-core/src/main/java/org/hibernate/boot/models/internal/GlobalRegistrationsImpl.java:742
final var lifecycleEventHandler = LifecycleEventHandler.from(
JpaEventListenerStyle.LISTENER,
listenerClassDetails,
jaxbEntityListener,
modelsContext,
LifecycleEventHandler.hasExplicitXmlCallbackMappings( jaxbEntityListener )
);
final var persistenceUnitLifecycleEventHandler =
PersistenceUnitLifecycleEventHandler.from( listenerClassDetails, jaxbEntityListener );
if ( lifecycleEventHandler.hasCallbackMethods() ) {
addJpaEventListener( lifecycleEventHandler );
}
if ( persistenceUnitLifecycleEventHandler.hasCallbackMethods() ) {
addPersistenceUnitLifecycleEventHandler( persistenceUnitLifecycleEventHandler );
}
if ( !lifecycleEventHandler.hasCallbackMethods()
&& !persistenceUnitLifecycleEventHandler.hasCallbackMethods() ) {
throw new ModelsException( "Mapping for entity listener specified no callback methods: "
+ listenerClassDetails.getClassName() );
}
} );
}
public void addJpaEventListener(LifecycleEventHandler listener) {
if ( lifecycleEventHandlers == null ) {
lifecycleEventHandlers = new ArrayList<>();
}
lifecycleEventHandlers.add( listener );
}
public void addTargetedJpaEventListener(ClassDetails listenerClassDetails) {
final var persistenceUnitHandler = PersistenceUnitLifecycleEventHandler.from( listenerClassDetails );
if ( persistenceUnitHandler.hasCallbackMethods() ) {
addPersistenceUnitLifecycleEventHandler( persistenceUnitHandler );
}View on GitHub (pinned to fad1729dce)
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
Example fix
// before - AuditListener registered in mapping.xml but has no callbacks
public class AuditListener { }
// after
public class AuditListener {
@PrePersist
void onPrePersist(Object entity) { ... }
} Defensive patterns
Strategy: validation
Validate before calling
// Pre-flight in tests: every registered listener must expose at least one JPA callback
static final List<Class<? extends Annotation>> CB = List.of(
jakarta.persistence.PrePersist.class, jakarta.persistence.PostPersist.class,
jakarta.persistence.PreUpdate.class, jakarta.persistence.PostUpdate.class,
jakarta.persistence.PreRemove.class, jakarta.persistence.PostRemove.class,
jakarta.persistence.PostLoad.class);
static boolean hasCallback(Class<?> listener) {
return java.util.Arrays.stream(listener.getDeclaredMethods())
.flatMap(m -> java.util.Arrays.stream(m.getAnnotations()))
.anyMatch(a -> CB.contains(a.annotationType()));
}
// assertEquals over the set of listeners registered in orm.xml before bootstrap Try / catch
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>
Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Callback method annotated '@%s' in '%s' must return void and
- You can only annotate one callback method per callback type
- Duplicate named query '%s'
- Duplicate named stored procedure '{}'
- Duplicate SQL result set mapping '{}'
AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22).
Data as JSON: /api/errors/b7b71edcd8b37bb1.
Report an issue: GitHub.