hibernate/hibernate-orm · error · AnnotationException

Property '${property}' is annotated '@OptimisticLock(exclude

Error message

Property '${property}' is annotated '@OptimisticLock(excluded=true)' and '@Id'

What it means

@OptimisticLock(excluded = true) on the identifier property would exclude the id from optimistic-lock checks, which Hibernate forbids because identifier participation in locking is fixed by the mapping, not by this flag. PropertyBinder throws AnnotationException when it finds @Id together with excluded=true.

Source

Thrown at hibernate-core/src/main/java/org/hibernate/boot/model/internal/PropertyBinder.java:684

	}

	private void checkAnnotation(Class<? extends Annotation> annotationClass, Class<?> propertyType) {
		if ( memberDetails.hasDirectAnnotationUsage( annotationClass )
				&& !memberDetails.getType().isImplementor( propertyType ) ) {
			throw new AnnotationException( "Property '" + qualify( holder.getPath(), name )
					+ "' is annotated '@" + annotationClass.getSimpleName()
					+ "' but is not of type '" + propertyType.getTypeName() + "'" );
		}
	}

	private void validateOptimisticLock(boolean excluded) {
		if ( excluded ) {
			if ( isVersion( memberDetails ) ) {
				throw new AnnotationException("Property '" + qualify( holder.getPath(), name )
						+ "' is annotated '@OptimisticLock(excluded=true)' and '@Version'" );
			}
			if ( isSimpleId( memberDetails ) ) {
				throw new AnnotationException("Property '" + qualify( holder.getPath(), name )
						+ "' is annotated '@OptimisticLock(excluded=true)' and '@Id'" );
			}
			if ( isEmbeddedId( memberDetails ) ) {
				throw new AnnotationException( "Property '" + qualify( holder.getPath(), name )
						+ "' is annotated '@OptimisticLock(excluded=true)' and '@EmbeddedId'" );
			}
		}
	}

	/**
	 * @param elements List of {@link PropertyData} instances
	 * @param propertyContainer Metadata about a class and its properties
	 * @param idPropertyCounter number of id properties already present in list of {@link PropertyData} instances
	 *
	 * @return total number of id properties found after iterating the elements of {@code annotatedClass}
	 * using the determined access strategy (starting from the provided {@code idPropertyCounter})
	 */
	static int addElementsOfClass(

View on GitHub (pinned to fad1729dce)

Solutions

  1. Remove @OptimisticLock(excluded = true) from the @Id property.
  2. Apply excluded=true only to genuinely lock-irrelevant mutable fields (e.g. audit counters, last-login timestamps).

Example fix

// before
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@OptimisticLock(excluded = true)
private Long id;

// after
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
Defensive patterns

Strategy: validation

Validate before calling

for (Class<?> entity : annotatedClasses) {
    for (Field f : entity.getDeclaredFields()) {
        OptimisticLock ol = f.getAnnotation(OptimisticLock.class);
        if (ol != null && ol.excluded() && f.isAnnotationPresent(Id.class)) {
            throw new IllegalStateException("@Id property may not use @OptimisticLock(excluded=true): " + f);
        }
    }
}

Try / catch

try {
    SessionFactory sf = cfg.buildSessionFactory();
} catch (AnnotationException e) {
    throw new IllegalStateException("Optimistic-lock annotation conflict: " + e.getMessage(), e);
}

Prevention

When it happens

Trigger: A simple @Id property also annotated @OptimisticLock(excluded = true); typically an over-broad copy-paste of the exclusion flag onto every field of an entity.

Common situations: Applying @OptimisticLock(excluded=true) wholesale via code templates; attempting to reduce lock-scope overhead by excluding the id.

Related errors


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