hibernate/hibernate-orm · error · HibernateException

Entity '${persister.getEntityName()}' has no version and may

Error message

Entity '${persister.getEntityName()}' has no version and may not be locked at level ${lockMode}

What it means

Thrown from DefaultPostLoadEventListener.onPostLoad when the entity's current LockMode requires a version (lockMode.requiresVersion(), e.g. OPTIMISTIC, OPTIMISTIC_FORCE_INCREMENT, PESSIMISTIC_FORCE_INCREMENT) but the persister reports isVersioned() == false. Optimistic locking works by verifying or incrementing a @Version column at flush; with no version attribute there is nothing to verify or increment, so Hibernate refuses the lock. The message names the entity and the offending lock mode.

Source

Thrown at hibernate-core/src/main/java/org/hibernate/event/internal/DefaultPostLoadEventListener.java:56

		if ( lockMode.requiresVersion() ) {
			final var persister = entry.getPersister();
			if ( persister.isVersioned() ) {
				switch ( lockMode ) {
					case PESSIMISTIC_FORCE_INCREMENT:
						OptimisticLockHelper.forceVersionIncrement( entity, entry, session );
						break;
					case OPTIMISTIC_FORCE_INCREMENT:
						session.getActionQueue()
								.registerCallback( new EntityIncrementVersionProcess( entity ) );
						break;
					case OPTIMISTIC:
						session.getActionQueue()
								.registerCallback( new EntityVerifyVersionProcess( entity ) );
						break;
				}
			}
			else {
				throw new HibernateException( "Entity '" + persister.getEntityName()
							+ "' has no version and may not be locked at level " + lockMode);
			}
		}
	}
}

View on GitHub (pinned to fad1729dce)

Solutions

  1. Add a @Version attribute to the entity (e.g. @Version private Long version;) and add the corresponding version column to the table (ALTER TABLE ... ADD COLUMN version BIGINT)
  2. If the schema cannot take a version column, switch to a pessimistic mode: LockModeType.PESSIMISTIC_READ or LockModeType.WRITE-style SELECT ... FOR UPDATE locking
  3. Remove the lock() call if no row-level concurrency control is actually needed, or implement manual version checking in an UPDATE ... WHERE clause

Example fix

// before
em.lock(order, LockModeType.OPTIMISTIC_FORCE_INCREMENT); // Order has no @Version -> HibernateException at post-load

// after
@Entity
public class Order {
    @Id Long id;
    @Version Long version; // add version attribute + column
}
em.lock(order, LockModeType.OPTIMISTIC_FORCE_INCREMENT);
Defensive patterns

Strategy: validation

Validate before calling

EntityPersister p = session.getFactory().getMappingMetamodel()
        .getEntityDescriptor(Order.class);
if (!p.isVersioned()) {
    // entity has no @Version: use PESSIMISTIC_READ/WRITE instead of OPTIMISTIC*
    session.lock(order, LockMode.PESSIMISTIC_WRITE);
}
else {
    session.lock(order, LockMode.OPTIMISTIC_FORCE_INCREMENT);
}

Type guard

boolean lockableOptimistically(SessionFactory f, Class<?> entityType) {
    return f.getMappingMetamodel().getEntityDescriptor(entityType).isVersioned();
}

Try / catch

try {
    em.lock(order, LockModeType.OPTIMISTIC_FORCE_INCREMENT);
} catch (HibernateException e) {
    if (e.getMessage() != null && e.getMessage().contains("has no version")) {
        // entity class lacks @Version — add a version or fall back to pessimistic locking
        em.lock(order, LockModeType.PESSIMISTIC_WRITE);
    } else { throw e; }
}

Prevention

When it happens

Trigger: Calling session.lock(entity, LockMode.OPTIMISTIC/OPTIMISTIC_FORCE_INCREMENT), JPA EntityManager.lock(entity, LockModeType.OPTIMISTIC/OPTIMISTIC_FORCE_INCREMENT), or Spring Data JPA @Lock(LockModeType.OPTIMISTIC_FORCE_INCREMENT) on a query for an entity class that has no @Version field; also find(entityName, id, LockMode) with a version-requiring mode. The post-load listener fires as the entity loads, so the exception surfaces on the load/lock call itself.

Common situations: Adding optimistic locking annotations to a repository/service for a legacy entity that never got a version column; switching from PESSIMISTIC to OPTIMISTIC lock modes during a concurrency refactor without adding @Version; upgrading code that used LockMode.READ semantics (deprecated no-op in Hibernate 6+) and mapping them to OPTIMISTIC on non-versioned entities.

Related errors


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