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

  1. Move the @Version property to the root @Entity of the hierarchy (all subtypes share it).
  2. Remove the duplicate @Version from the subclass if the root already declares one.
  3. 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

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


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