hibernate/hibernate-orm · error · AnnotationException
Property '${property}' is annotated '@OptimisticLock(exclude
Error message
Property '${property}' is annotated '@OptimisticLock(excluded=true)' and '@Version' What it means
@OptimisticLock(excluded = true) removes a property from the optimistic-locking WHERE clause, which is meaningless (and contradictory) for the @Version property itself — the version is the optimistic lock. PropertyBinder detects the combination during binding and fails fast.
Source
Thrown at hibernate-core/src/main/java/org/hibernate/boot/model/internal/PropertyBinder.java:680
checkAnnotation( MapKeyColumn.class, Map.class );
checkAnnotation( MapKeyJoinColumn.class, Map.class );
checkAnnotation( MapKeyJoinColumns.class, Map.class );
}
}
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
*View on GitHub (pinned to fad1729dce)
Solutions
- Remove @OptimisticLock(excluded = true) from the @Version property.
- If the goal was to avoid updating the timestamp on certain operations, use @DynamicUpdate or session-level flush strategies — not lock exclusion.
Example fix
// before @Version @OptimisticLock(excluded = true) private LocalDateTime updatedAt; // after @Version private LocalDateTime updatedAt;
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(Version.class)) {
throw new IllegalStateException("@Version 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
- Never annotate @Version with @OptimisticLock(excluded=true)
- Apply excluded=true selectively to audit/counter fields only
When it happens
Trigger: A property annotated with both @Version and @OptimisticLock(excluded = true); usually a stray excluded=true copied from a different field, or an attempt to keep the version column out of update statements.
Common situations: Copy-paste of @OptimisticLock(excluded=true) across fields including the version field; misunderstanding the flag as controlling whether the column is written instead of whether it participates in lock checks.
Related errors
- Property '${property}' is annotated '@OptimisticLock(exclude
- Property '${property}' is annotated '@OptimisticLock(exclude
- Class '" + propertyHolder.getEntityName() + "' is annotated
- Embedded class '" + propertyHolder.getEntityName() + "' may
- Attribute '" + memberDetails.getName() + "' is declared by '
AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22).
Data as JSON: /api/errors/f8db75248a6614ad.
Report an issue: GitHub.