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
- Add @DynamicUpdate to the entity (or dynamic-update='true' in XML)
- Or switch to version-based locking: remove @OptimisticLock (default VERSION) and add a @Version column
- 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
- Treat @OptimisticLock(ALL/DIRTY) and @DynamicUpdate as an inseparable pair in code review
- Put the pair in a shared annotation bundle or template so they are never added separately
- Run a boot test per persistence unit in CI so mapping errors surface before deployment
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
- Entity '{name}' has 'OptimisticLockType.{optimisticLockStyle
- Association '{propertyName}' marked as '@NaturalId' is also
- Could not parse discriminator value '{discriminatorValue}' a
- optimistic-lock=all|dirty not supported for joined-subclass
- Identifier property '" + getPath( holder, data ) + "' cannot
AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22).
Data as JSON: /api/errors/b09966bf9e4a0519.
Report an issue: GitHub.