{"record":{"id":"5a92bc1e699da304","repo":"hibernate/hibernate-orm","slug":"optimistic-lock-all-dirty-not-supported-for-joined","errorCode":null,"errorMessage":"optimistic-lock=all|dirty not supported for joined-subclass mappings [{entityName}]","messagePattern":"optimistic-lock=all\\|dirty not supported for joined-subclass mappings \\[(.+?)\\]","errorType":"exception","errorClass":"MappingException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/persister/entity/JoinedSubclassEntityPersister.java","lineNumber":215,"sourceCode":"\t\t\t\t\tdiscriminatorValue = new DiscriminatorValue.Literal( persistentClass.getSubclassId() );\n\t\t\t\t\tdiscriminatorSQLString = Integer.toString( persistentClass.getSubclassId() );\n\t\t\t\t}\n\t\t\t\tcatch ( Exception e ) {\n\t\t\t\t\tthrow new MappingException( \"Could not format discriminator value to SQL string\", e );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\texplicitDiscriminatorColumnName = null;\n\t\t\tdiscriminatorAlias = IMPLICIT_DISCRIMINATOR_ALIAS;\n\t\t\tdiscriminatorType = basicTypeRegistry.resolve( StandardBasicTypes.INTEGER );\n\t\t\tdiscriminatorValue = null;\n\t\t\tdiscriminatorSQLString = null;\n\t\t\tforceDiscriminator = false;\n\t\t}\n\n\t\tif ( optimisticLockStyle().isAllOrDirty() ) {\n\t\t\tthrow new MappingException( \"optimistic-lock=all|dirty not supported for joined-subclass mappings [\" + getEntityName() + \"]\" );\n\t\t}\n\n\t\t//MULTITABLES\n\n\t\tfinal int idColumnSpan = getIdentifierColumnSpan();\n\n\t\tfinal ArrayList<String> tableNames = new ArrayList<>();\n\t\tfinal ArrayList<String[]> keyColumns = new ArrayList<>();\n//\t\tfinal ArrayList<String[]> keyColumnReaders = new ArrayList<>();\n//\t\tfinal ArrayList<String[]> keyColumnReaderTemplates = new ArrayList<>();\n\t\tfinal ArrayList<Boolean> cascadeDeletes = new ArrayList<>();\n\t\tfinal var tableClosure = persistentClass.getTableClosure();\n\t\tfinal var keyClosure = persistentClass.getKeyClosure();\n\t\tfor ( int i = 0; i < tableClosure.size() && i < keyClosure.size(); i++ ) {\n\t\t\ttableNames.add( determineTableName( tableClosure.get(i) ) );\n\n\t\t\tfinal var key = keyClosure.get(i);\n\t\t\tfinal String[] keyCols = new String[idColumnSpan];","sourceCodeStart":197,"sourceCodeEnd":233,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/persister/entity/JoinedSubclassEntityPersister.java#L197-L233","documentation":"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.","triggerScenarios":"@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.","commonSituations":"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.","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"],"exampleFix":"// before\n@Entity\n@Inheritance(strategy = InheritanceType.JOINED)\n@DynamicUpdate\n@OptimisticLock(type = OptimisticLockType.DIRTY)\npublic abstract class Account { ... }\n\n// after\n@Entity\n@Inheritance(strategy = InheritanceType.JOINED)\npublic abstract class Account {\n    @Version private long version; // version-based optimistic locking\n}","handlingStrategy":"validation","validationCode":"Inheritance inh = root.getAnnotation(Inheritance.class);\nOptimisticLock lock = root.getAnnotation(OptimisticLock.class);\nif ( inh != null && inh.strategy() == InheritanceType.JOINED && lock != null\n        && (lock.type() == OptimisticLockType.ALL\n         || lock.type() == OptimisticLockType.DIRTY) ) {\n    throw new IllegalStateException(\n        root.getName() + \": optimistic-lock ALL/DIRTY is unsupported with JOINED inheritance\");\n}","typeGuard":null,"tryCatchPattern":"try {\n    SessionFactory sf = metadata.getSessionFactoryBuilder().build();\n}\ncatch ( org.hibernate.MappingException e ) {\n    throw new IllegalStateException(\"SessionFactory boot failed: \" + e.getMessage(), e);\n}","preventionTips":["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"],"tags":["hibernate","orm","optimistic-locking","joined-inheritance","mapping"],"backgroundTag":"optimistic-lock-misconfiguration","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}