hibernate/hibernate-orm · error · AnnotationException
Entity '" + propertyHolder.getEntityName() + "' is a subclas
Error message
Entity '" + propertyHolder.getEntityName() + "' is a subclass in an entity class hierarchy and may not have a property annotated '@Version'
What it means
In an inherited entity hierarchy the @Version property may only be declared on the root entity, because there is exactly one version column per hierarchy and it is owned by the root table. Declaring @Version on a subclass fails the RootClass check in checkVersionProperty and throws AnnotationException.
Source
Thrown at hibernate-core/src/main/java/org/hibernate/boot/model/internal/PropertyBinder.java:1099
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,
boolean isIdentifierMapper,
boolean isComponentEmbedded,
ColumnsBuilder columnsBuilder,
AnnotatedColumns columns,
ClassDetails returnedClass) {View on GitHub (pinned to fad1729dce)
Solutions
- Move the @Version property to the root @Entity of the hierarchy (all subtypes share it).
- Remove the duplicate @Version from the subclass if the root already declares one.
- If per-subtype versioning is genuinely required, the types cannot share one inheritance hierarchy — split them into separate root entities.
Example fix
// before
@Entity
@Inheritance(strategy = InheritanceType.JOINED)
public abstract class Item { ... }
@Entity
public class Book extends Item {
@Version // rejected: not the root
private int version;
}
// after
public abstract class Item {
@Version
private int version;
}
// subclass: no @Version Defensive patterns
Strategy: validation
Validate before calling
// Only the hierarchy root may carry @Version
for (Class<?> entity : annotatedClasses) {
Class<?> sup = entity.getSuperclass();
if (sup != null && sup.isAnnotationPresent(Entity.class)) {
for (Field f : entity.getDeclaredFields()) {
if (f.isAnnotationPresent(Version.class)) {
throw new IllegalStateException("Subclass " + entity.getName() + " declares @Version; move it to the root");
}
}
}
} Type guard
static boolean isRootOfEntityHierarchy(Class<?> c) {
return c.getSuperclass() == null || !c.getSuperclass().isAnnotationPresent(Entity.class);
} Try / catch
try {
SessionFactory sf = cfg.buildSessionFactory();
} catch (AnnotationException e) {
throw new IllegalStateException("@Version placement invalid: " + e.getMessage(), e);
} Prevention
- Declare @Version once on the hierarchy root before adding subclasses
- When merging entities under a base class, remove duplicate version fields from subtypes
When it happens
Trigger: @Entity subclass (SINGLE_TABLE, JOINED, or TABLE_PER_CLASS) with its own @Version field; refactoring that moves the version property from the root into a subtype; adding @Version to one subclass because only that subtype needed locking.
Common situations: Introducing optimistic locking piecemeal into an existing hierarchy; merging independently-developed entities under a common base where a subtype already had a version column; generated per-subclass templates that include version fields.
Related errors
- Property '${property}' belongs to an entity subclass and may
- Property '${property}' is annotated '@OptimisticLock(exclude
- Attribute '${attribute}' is declared as an '@Id' or '@Embedd
- 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/fce92111bc247168.
Report an issue: GitHub.