{"record":{"id":"96c881f3c57dcef4","repo":"hibernate/hibernate-orm","slug":"entity-name-has-optimisticlocktype-optimisti","errorCode":null,"errorMessage":"Entity '{name}' has 'OptimisticLockType.{optimisticLockStyle}' but declares a '@Version' field","messagePattern":"Entity '(.+?)' has 'OptimisticLockType\\.(.+?)' but declares a '@Version' field","errorType":"exception","errorClass":"MappingException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/persister/entity/BaseEntityPersister.java","lineNumber":420,"sourceCode":"\n\t\tdynamicUpdate = persistentClass.useDynamicUpdate() || hasMultipleFetchGroups( bytecodeEnhancementMetadata );\n\t\tdynamicInsert = persistentClass.useDynamicInsert();\n\n\t\tpolymorphic = persistentClass.isPolymorphic();\n\t\tinherited = persistentClass.isInherited();\n\t\tsuperclass = inherited ? persistentClass.getSuperclass().getEntityName() : null;\n\t\thasSubclasses = persistentClass.hasSubclasses();\n\n\t\toptimisticLockStyle = persistentClass.getOptimisticLockStyle();\n\t\t//TODO: move these checks into the Binders\n\t\tif ( optimisticLockStyle.isAllOrDirty() ) {\n\t\t\tif ( !dynamicUpdate ) {\n\t\t\t\tthrow new MappingException( \"Entity '\" + name\n\t\t\t\t\t\t\t\t\t\t\t+ \"' has 'OptimisticLockType.\" + optimisticLockStyle\n\t\t\t\t\t\t\t\t\t\t\t+ \"' but is not annotated '@DynamicUpdate'\" );\n\t\t\t}\n\t\t\tif ( versionPropertyIndex != NO_VERSION_INDX ) {\n\t\t\t\tthrow new MappingException( \"Entity '\" + name\n\t\t\t\t\t\t\t\t\t\t\t+ \"' has 'OptimisticLockType.\" + optimisticLockStyle\n\t\t\t\t\t\t\t\t\t\t\t+ \"' but declares a '@Version' field\" );\n\t\t\t}\n\t\t}\n\n\t\thasCollections = foundCollection;\n\t\thasOwnedCollections = foundOwnedCollection;\n\t\tmutablePropertiesIndexes = mutableIndexes;\n\n\t\tsubclassEntityNames = collectSubclassEntityNames( persistentClass );\n\n//\t\tHashMap<Class<?>, String> entityNameByInheritanceClassMapLocal = new HashMap<>();\n//\t\tif ( persistentClass.hasPojoRepresentation() ) {\n//\t\t\tentityNameByInheritanceClassMapLocal.put( persistentClass.getMappedClass(), persistentClass.getEntityName() );\n//\t\t\tfor ( Subclass subclass : persistentClass.getSubclasses() ) {\n//\t\t\t\tentityNameByInheritanceClassMapLocal.put( subclass.getMappedClass(), subclass.getEntityName() );\n//\t\t\t}\n//\t\t}","sourceCodeStart":402,"sourceCodeEnd":438,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/persister/entity/BaseEntityPersister.java#L402-L438","documentation":"Same boot-time validation block as the @DynamicUpdate check: OptimisticLockType.ALL/DIRTY lock by comparing columns in the UPDATE's WHERE clause instead of using a version number, so combining them with an explicit @Version property is contradictory and BaseEntityPersister rejects it whenever versionPropertyIndex != NO_VERSION_INDX. The dynamic-update check runs first, so the typical failing combination is @DynamicUpdate + @OptimisticLock(ALL|DIRTY) + @Version. Thrown as MappingException while the persister is constructed.","triggerScenarios":"Entity with @DynamicUpdate + @OptimisticLock(type = ALL or DIRTY) + a @Version field; XML mapping with <version> plus optimistic-lock='all|dirty' and dynamic-update='true'.","commonSituations":"Leaving @Version in place when switching a legacy entity to all/dirty locking; copy-pasting annotation sets between entities; inheritance hierarchies where the @Version property sits on the root but a subclass sets the lock style.","solutions":["Prefer removing @OptimisticLock and keeping standard @Version-based locking (OptimisticLockType.VERSION, the default)","Or remove the @Version property and rely fully on all/dirty column comparison with @DynamicUpdate","Audit every entity where the lock style was changed and delete the now-contradictory @Version field"],"exampleFix":"// before\n@Entity @DynamicUpdate\n@OptimisticLock(type = OptimisticLockType.ALL)\npublic class Order {\n    @Version private int version; // contradicts ALL/DIRTY locking\n}\n\n// after\n@Entity @DynamicUpdate\npublic class Order {\n    @Version private int version; // version locking (default style)\n}","handlingStrategy":"validation","validationCode":"static void checkVersionConflict(Class<?> entity) {\n    OptimisticLock lock = entity.getAnnotation(OptimisticLock.class);\n    boolean versioned = java.util.stream.Stream.of(entity.getDeclaredFields())\n            .anyMatch(f -> f.isAnnotationPresent(Version.class));\n    if ( lock != null && (lock.type() == OptimisticLockType.ALL\n                       || lock.type() == OptimisticLockType.DIRTY) && versioned ) {\n        throw new IllegalStateException(entity.getName()\n            + \" mixes OptimisticLockType.\" + lock.type() + \" with @Version\");\n    }\n}","typeGuard":null,"tryCatchPattern":"try {\n    sessionFactory = metadata.getSessionFactoryBuilder().build();\n}\ncatch ( org.hibernate.MappingException e ) {\n    throw new IllegalStateException(\"SessionFactory boot failed: \" + e.getMessage(), e);\n}","preventionTips":["Pick one optimistic-locking strategy per entity and document it in the code review checklist","When switching from @Version to ALL/DIRTY, delete the @Version field in the same commit","Scan the whole persistence unit for @OptimisticLock during upgrades"],"tags":["hibernate","orm","optimistic-locking","mapping","annotations","versioning"],"backgroundTag":"optimistic-lock-misconfiguration","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}