hibernate/hibernate-orm · error · AnnotationException
Class '" + propertyHolder.getEntityName() + "' is annotated
Error message
Class '" + propertyHolder.getEntityName() + "' is annotated '@IdClass' and may not have a property annotated '@Version'
What it means
A class mapped with @IdClass must not declare a @Version property: version/optimistic-lock state belongs to the entity itself, not to the composite-id mirror class. PropertyBinder detects the situation while binding the id-class property mapper (isIdentifierMapper) and throws AnnotationException at bootstrap.
Source
Thrown at hibernate-core/src/main/java/org/hibernate/boot/model/internal/PropertyBinder.java:1094
getMappedSuperclassOrNull( declaringClass,
inheritanceStatePerClass, buildingContext );
if ( mappedSuperclass != null ) {
// Don't overwrite an existing version property
if ( mappedSuperclass.getDeclaredVersion() == null ) {
mappedSuperclass.setDeclaredVersion( property );
}
}
else {
//we know the property is on the actual entity
rootClass.setDeclaredVersion( property );
}
rootClass.setOptimisticLockStyle( OptimisticLockStyle.VERSION );
}
private static void checkVersionProperty(PropertyHolder propertyHolder, boolean isIdentifierMapper) {
if ( isIdentifierMapper ) {
throw new AnnotationException( "Class '" + propertyHolder.getEntityName()
+ "' is annotated '@IdClass' and may not have a property annotated '@Version'"
);
}
if ( !( propertyHolder.getPersistentClass() instanceof RootClass ) ) {
throw new AnnotationException( "Entity '" + propertyHolder.getEntityName()
+ "' is a subclass in an entity class hierarchy and may not have a property annotated '@Version'" );
}
if ( !propertyHolder.isEntity() ) {
throw new AnnotationException( "Embedded class '" + propertyHolder.getEntityName()
+ "' may not have a property annotated '@Version'" );
}
}
private AnnotatedColumns bindBasicOrComposite(
PropertyHolder propertyHolder,
Nullability nullability,
PropertyData inferredData,
EntityBinder entityBinder,View on GitHub (pinned to fad1729dce)
Solutions
- Delete the @Version property from the @IdClass; keep only the id fields.
- Put @Version on the owning @Entity class instead (its root, per the related subclass check).
Example fix
// before
public class OrderLineId implements Serializable {
private Long order;
private Long line;
@Version // rejected inside an @IdClass
private int version;
}
// after
public class OrderLineId implements Serializable {
private Long order;
private Long line;
}
// and on the entity:
@Entity
@IdClass(OrderLineId.class)
public class OrderLine {
@Id private Long order;
@Id private Long line;
@Version private int version;
} Defensive patterns
Strategy: validation
Validate before calling
// Id classes must mirror only id fields — no @Version
for (Class<?> entity : annotatedClasses) {
IdClass idClass = entity.getAnnotation(IdClass.class);
if (idClass == null) continue;
for (Field f : idClass.value().getDeclaredFields()) {
if (f.isAnnotationPresent(Version.class)) {
throw new IllegalStateException("@IdClass " + idClass.value().getName() + " contains @Version field " + f.getName());
}
}
} Try / catch
try {
SessionFactory sf = cfg.buildSessionFactory();
} catch (AnnotationException e) {
throw new IllegalStateException("@IdClass contains forbidden property: " + e.getMessage(), e);
} Prevention
- Keep id classes minimal: only the id fields, Serializable, equals/hashCode
- Declare @Version on the entity, never on the id class
When it happens
Trigger: An @IdClass class (e.g. OrderLineId) containing a field annotated @Version; usually the id-class was created by copying all fields from the entity, version included.
Common situations: Hand-written id classes cloned from the entity; introducing optimistic locking into a composite-key entity by adding @Version 'everywhere'; code generators that mirror all entity fields into the id class.
Related errors
- Property '${property}' is annotated '@OptimisticLock(exclude
- Embedded class '" + propertyHolder.getEntityName() + "' may
- Property '${property}' is annotated '@OptimisticLock(exclude
- Property '${property}' is annotated '@OptimisticLock(exclude
- Entity '" + propertyHolder.getEntityName() + "' is a subclas
AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22).
Data as JSON: /api/errors/af852ad82e902057.
Report an issue: GitHub.