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

  1. Delete or un-annotate one of the duplicates - keep exactly one method per callback type in the listener class
  2. If both behaviors are needed, chain them from the single surviving callback or split them into two listener classes
  3. 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

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


AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22). Data as JSON: /api/errors/6e7e3cd7d129f969. Report an issue: GitHub.