hibernate/hibernate-orm · error · ModelsException
You can only annotate one callback method per callback type
Error message
You can only annotate one callback method per callback type and target class in callback class: %s
What it means
Within one listener class, each callback type may back only one method per target class. TargetedLifecycleEventHandlerBuilder stores callbacks in a per-callback-type map; when a second, different method with the same callback annotation is registered for the same target entity, checkDuplicate throws naming the listener class.
Source
Thrown at hibernate-core/src/main/java/org/hibernate/boot/models/internal/GlobalRegistrationsImpl.java:843
}
private static class TargetedLifecycleEventHandlerBuilder {
private final EnumMap<CallbackType, MethodDetails> callbackMethods = new EnumMap<>( CallbackType.class );
private TargetedLifecycleEventHandlerBuilder() {
}
private void setCallbackMethod(CallbackType callbackType, MethodDetails method) {
checkDuplicate( callbackMethods.putIfAbsent( callbackType, method ), method );
}
private LifecycleEventHandler build(JpaEventListenerStyle style, ClassDetails listenerClassDetails) {
return new LifecycleEventHandler( style, listenerClassDetails, callbackMethods );
}
private void checkDuplicate(MethodDetails previous, MethodDetails method) {
if ( previous != null && previous != method ) {
throw new ModelsException( "You can only annotate one callback method per callback type and target class"
+ " in callback class: " + method.getDeclaringType().getClassName() );
}
}
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Id generators
public void collectIdGenerators(JaxbEntityMappingsImpl jaxbRoot) {
collectSequenceGenerators( jaxbRoot.getSequenceGenerators() );
collectTableGenerators( jaxbRoot.getTableGenerators() );
collectGenericGenerators( jaxbRoot.getGenericGenerators() );
// todo : add support for @IdGeneratorType in mapping.xsd?
}
public void collectIdGenerators(ClassDetails classDetails) {View on GitHub (pinned to fad1729dce)
Solutions
- Delete or un-annotate one of the duplicates - keep exactly one method per callback type in the listener class
- If both behaviors are needed, chain them from the single surviving callback or split them into two listener classes
- Search the listener class for repeated @Pre*/@Post* annotations of the same kind to catch the second occurrence
Example fix
// before
public class AuditListener {
@PrePersist void onCreate(Object e) { log(e); }
@PrePersist void onCreateToo(Object e) { audit(e); } // duplicate
}
// after
public class AuditListener {
@PrePersist void onCreate(Object e) {
log(e);
audit(e);
}
} Defensive patterns
Strategy: validation
Validate before calling
// Pre-flight: at most one method per callback annotation per listener class
for (Class<?> listener : listeners) {
Map<Class<? extends Annotation>, Long> counts = new java.util.HashMap<>();
for (java.lang.reflect.Method m : listener.getDeclaredMethods())
for (var a : m.getAnnotations())
counts.merge(a.annotationType(), 1L, Long::sum);
counts.forEach((anno, n) -> {
if (anno.getName().matches(".*(Pre|Post)(Persist|Update|Remove|Load)") && n > 1)
throw new IllegalStateException("duplicate " + anno.getSimpleName() + " in " + listener);
});
} Try / catch
catch (org.hibernate.models.ModelsException e) during bootstrap: the message names the listener class - find the two methods sharing the same callback annotation there and keep only one
Prevention
- After renaming a callback method, delete the original instead of leaving both annotated
- Avoid overloading callback methods in one listener class
- When merging listener classes, audit for repeated callback annotations
When it happens
Trigger: Two distinct methods in the same listener class both annotated @PrePersist (an overload, or a renamed copy left behind) resolving to the same target entity argument type; likewise two @PostLoad methods, etc.
Common situations: Copy-paste plus rename leaving the original annotated method in place; overloads differing in parameter types but targeting the same entity; merging two listener classes into one during refactoring.
Related errors
- Mapping for entity listener specified no callback methods: %
- Callback method annotated '@%s' in '%s' must return void and
- 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/6e7e3cd7d129f969.
Report an issue: GitHub.