hibernate/hibernate-orm · error · AnnotationException
Property '${property}' is annotated '@OptimisticLock(exclude
Error message
Property '${property}' is annotated '@OptimisticLock(excluded=true)' and '@EmbeddedId' What it means
Same rule as for @Id, but for composite identifiers: an @EmbeddedId property (or the embedded-id aggregate) may not carry @OptimisticLock(excluded = true). Hibernate rejects the combination during property binding because identifier lock participation cannot be toggled with this flag.
Source
Thrown at hibernate-core/src/main/java/org/hibernate/boot/model/internal/PropertyBinder.java:688
&& !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(
List<PropertyData> elements,
PropertyContainer propertyContainer,
MetadataBuildingContext context,
int idPropertyCounter) {View on GitHub (pinned to fad1729dce)
Solutions
- Remove @OptimisticLock(excluded = true) from the @EmbeddedId property.
- Keep excluded=true only on non-id mutable attributes if lock narrowing is really needed.
Example fix
// before @EmbeddedId @OptimisticLock(excluded = true) private OrderLineId id; // after @EmbeddedId private OrderLineId 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(EmbeddedId.class)) {
throw new IllegalStateException("@EmbeddedId 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
- Treat identifier fields (simple or embedded) as off-limits for @OptimisticLock(excluded=true)
When it happens
Trigger: A field annotated @EmbeddedId that is additionally annotated @OptimisticLock(excluded = true); commonly happens when templates or IDE refactoring apply the exclusion to all fields of a composite-id entity.
Common situations: Composite-key entities generated from templates that add lock exclusions; teams applying excluded=true broadly for performance and catching the id by accident.
Related errors
- Property '${property}' is annotated '@OptimisticLock(exclude
- Property '${property}' is annotated '@OptimisticLock(exclude
- Property '" + getPath( propertyHolder, inferredData ) + "' b
- Attribute '" + memberDetails.getName() + "' is declared by '
- Class '" + propertyHolder.getEntityName() + "' is annotated
AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22).
Data as JSON: /api/errors/0e37cbda13486e2f.
Report an issue: GitHub.