hibernate/hibernate-orm · error · MappingException
optimistic-lock=all|dirty not supported for joined-subclass
Error message
optimistic-lock=all|dirty not supported for joined-subclass mappings [{entityName}] What it means
JoinedSubclassEntityPersister rejects OptimisticLockType.ALL/DIRTY at boot with MappingException('optimistic-lock=all|dirty not supported for joined-subclass mappings'). In JOINED inheritance the entity's columns span several tables, so a single UPDATE cannot compare all/dirty columns in its WHERE clause; version-based optimistic locking is the only supported style, and @DynamicUpdate does not change this.
Source
Thrown at hibernate-core/src/main/java/org/hibernate/persister/entity/JoinedSubclassEntityPersister.java:215
discriminatorValue = new DiscriminatorValue.Literal( persistentClass.getSubclassId() );
discriminatorSQLString = Integer.toString( persistentClass.getSubclassId() );
}
catch ( Exception e ) {
throw new MappingException( "Could not format discriminator value to SQL string", e );
}
}
}
else {
explicitDiscriminatorColumnName = null;
discriminatorAlias = IMPLICIT_DISCRIMINATOR_ALIAS;
discriminatorType = basicTypeRegistry.resolve( StandardBasicTypes.INTEGER );
discriminatorValue = null;
discriminatorSQLString = null;
forceDiscriminator = false;
}
if ( optimisticLockStyle().isAllOrDirty() ) {
throw new MappingException( "optimistic-lock=all|dirty not supported for joined-subclass mappings [" + getEntityName() + "]" );
}
//MULTITABLES
final int idColumnSpan = getIdentifierColumnSpan();
final ArrayList<String> tableNames = new ArrayList<>();
final ArrayList<String[]> keyColumns = new ArrayList<>();
// final ArrayList<String[]> keyColumnReaders = new ArrayList<>();
// final ArrayList<String[]> keyColumnReaderTemplates = new ArrayList<>();
final ArrayList<Boolean> cascadeDeletes = new ArrayList<>();
final var tableClosure = persistentClass.getTableClosure();
final var keyClosure = persistentClass.getKeyClosure();
for ( int i = 0; i < tableClosure.size() && i < keyClosure.size(); i++ ) {
tableNames.add( determineTableName( tableClosure.get(i) ) );
final var key = keyClosure.get(i);
final String[] keyCols = new String[idColumnSpan];View on GitHub (pinned to fad1729dce)
Solutions
- Use @Version-based locking on JOINED hierarchies (remove @OptimisticLock — VERSION is the default)
- If all/dirty locking is mandatory, switch the hierarchy to SINGLE_TABLE where it is supported
- Keep @DynamicUpdate only for SQL trimming; it never enables all/dirty locking on JOINED mappings
Example fix
// before
@Entity
@Inheritance(strategy = InheritanceType.JOINED)
@DynamicUpdate
@OptimisticLock(type = OptimisticLockType.DIRTY)
public abstract class Account { ... }
// after
@Entity
@Inheritance(strategy = InheritanceType.JOINED)
public abstract class Account {
@Version private long version; // version-based optimistic locking
} Defensive patterns
Strategy: validation
Validate before calling
Inheritance inh = root.getAnnotation(Inheritance.class);
OptimisticLock lock = root.getAnnotation(OptimisticLock.class);
if ( inh != null && inh.strategy() == InheritanceType.JOINED && lock != null
&& (lock.type() == OptimisticLockType.ALL
|| lock.type() == OptimisticLockType.DIRTY) ) {
throw new IllegalStateException(
root.getName() + ": optimistic-lock ALL/DIRTY is unsupported with JOINED inheritance");
} Try / catch
try {
SessionFactory sf = metadata.getSessionFactoryBuilder().build();
}
catch ( org.hibernate.MappingException e ) {
throw new IllegalStateException("SessionFactory boot failed: " + e.getMessage(), e);
} Prevention
- Use @Version locking on JOINED hierarchies; @DynamicUpdate does not rescue ALL/DIRTY there
- When changing an inheritance strategy, review optimistic-lock settings on the root in the same change
- Encode the strategy/lock-style compatibility rule in a mapping-lint test that scans annotated entities
When it happens
Trigger: @Inheritance(strategy = JOINED) root annotated @OptimisticLock(type = ALL or DIRTY) (or hbm.xml optimistic-lock="all|dirty") — fails during SessionFactory boot even when @DynamicUpdate is present.
Common situations: Switching an inheritance hierarchy from SINGLE_TABLE to JOINED while keeping the lock style; copying optimistic-lock configuration across hierarchy roots; porting legacy mappings that relied on all/dirty locking.
Related errors
- Entity '${name}' has 'OptimisticLockType.${optimisticLockSty
- Entity '{name}' has 'OptimisticLockType.{optimisticLockStyle
- Discriminator formulas on joined inheritance hierarchies not
- Encountered 'subclass table index' [%s] was outside expected
- Was unable to locate subclass table [%s] in 'subclassTableNa
AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22).
Data as JSON: /api/errors/5a92bc1e699da304.
Report an issue: GitHub.