hibernate/hibernate-orm · error · MappingException

Entity '${name}' has 'OptimisticLockType.${optimisticLockSty

Error message

Entity '${name}' has 'OptimisticLockType.${optimisticLockStyle}' but is not annotated '@DynamicUpdate'

What it means

At SessionFactory boot, BaseEntityPersister validates that entities using OptimisticLockType.ALL or DIRTY run with dynamic SQL updates, because column-comparing optimistic locking needs Hibernate to build an UPDATE restricted to the (all|dirty) columns. dynamicUpdate is only true when @DynamicUpdate (or dynamic-update='true') is set, or bytecode enhancement configured multiple fetch groups (hasMultipleFetchGroups(bytecodeEnhancementMetadata)). If neither holds, persister construction throws a MappingException.

Source

Thrown at hibernate-core/src/main/java/org/hibernate/persister/entity/BaseEntityPersister.java:415

		mutable = persistentClass.isMutable();
		isAbstract = isAbstract( persistentClass );

		selectBeforeUpdate = persistentClass.hasSelectBeforeUpdate();

		dynamicUpdate = persistentClass.useDynamicUpdate() || hasMultipleFetchGroups( bytecodeEnhancementMetadata );
		dynamicInsert = persistentClass.useDynamicInsert();

		polymorphic = persistentClass.isPolymorphic();
		inherited = persistentClass.isInherited();
		superclass = inherited ? persistentClass.getSuperclass().getEntityName() : null;
		hasSubclasses = persistentClass.hasSubclasses();

		optimisticLockStyle = persistentClass.getOptimisticLockStyle();
		//TODO: move these checks into the Binders
		if ( optimisticLockStyle.isAllOrDirty() ) {
			if ( !dynamicUpdate ) {
				throw new MappingException( "Entity '" + name
											+ "' has 'OptimisticLockType." + optimisticLockStyle
											+ "' but is not annotated '@DynamicUpdate'" );
			}
			if ( versionPropertyIndex != NO_VERSION_INDX ) {
				throw new MappingException( "Entity '" + name
											+ "' has 'OptimisticLockType." + optimisticLockStyle
											+ "' but declares a '@Version' field" );
			}
		}

		hasCollections = foundCollection;
		hasOwnedCollections = foundOwnedCollection;
		mutablePropertiesIndexes = mutableIndexes;

		subclassEntityNames = collectSubclassEntityNames( persistentClass );

//		HashMap<Class<?>, String> entityNameByInheritanceClassMapLocal = new HashMap<>();
//		if ( persistentClass.hasPojoRepresentation() ) {

View on GitHub (pinned to fad1729dce)

Solutions

  1. Add @DynamicUpdate to the entity (or dynamic-update='true' in XML)
  2. Or switch to version-based locking: remove @OptimisticLock (default VERSION) and add a @Version column
  3. If relying on enhancement, keep it configured with multiple fetch groups so dynamicUpdate computes true

Example fix

// before
@Entity
@OptimisticLock(type = OptimisticLockType.DIRTY)
public class Document { ... }

// after
@Entity
@DynamicUpdate
@OptimisticLock(type = OptimisticLockType.DIRTY)
public class Document { ... }
Defensive patterns

Strategy: validation

Validate before calling

static void checkOptimisticLock(Class<?> entity) {
    OptimisticLock lock = entity.getAnnotation(OptimisticLock.class);
    if ( lock != null && (lock.type() == OptimisticLockType.ALL
                       || lock.type() == OptimisticLockType.DIRTY)
            && !entity.isAnnotationPresent(DynamicUpdate.class) ) {
        throw new IllegalStateException(entity.getName()
            + " requires @DynamicUpdate for OptimisticLockType." + lock.type());
    }
}

Try / catch

try {
    sessionFactory = metadata.getSessionFactoryBuilder().build();
}
catch ( org.hibernate.MappingException e ) {
    // configuration error: fail deployment with e.getMessage()
    throw new IllegalStateException("SessionFactory boot failed: " + e.getMessage(), e);
}

Prevention

When it happens

Trigger: An @Entity class annotated @org.hibernate.annotations.OptimisticLock(type = OptimisticLockType.ALL or DIRTY) without @DynamicUpdate and without enhancement producing multiple fetch groups; equivalent XML with optimistic-lock='all|dirty' but no dynamic-update='true'.

Common situations: Adding @OptimisticLock to existing entities that never had dynamic update; porting legacy hbm.xml mappings; disabling bytecode enhancement in a project where it previously satisfied this check; code-generation templates that emit only one of the two annotations.

Related errors


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