{"record":{"id":"9f45095b9d343e75","repo":"hibernate/hibernate-orm","slug":"entity-persister-getentityname-has-no-versi","errorCode":null,"errorMessage":"Entity '${persister.getEntityName()}' has no version and may not be locked at level ${lockMode}","messagePattern":"Entity '(.+?)' has no version and may not be locked at level (.+?)","errorType":"exception","errorClass":"HibernateException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/event/internal/DefaultPostLoadEventListener.java","lineNumber":56,"sourceCode":"\t\tif ( lockMode.requiresVersion() ) {\n\t\t\tfinal var persister = entry.getPersister();\n\t\t\tif ( persister.isVersioned() ) {\n\t\t\t\tswitch ( lockMode ) {\n\t\t\t\t\tcase PESSIMISTIC_FORCE_INCREMENT:\n\t\t\t\t\t\tOptimisticLockHelper.forceVersionIncrement( entity, entry, session );\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase OPTIMISTIC_FORCE_INCREMENT:\n\t\t\t\t\t\tsession.getActionQueue()\n\t\t\t\t\t\t\t\t.registerCallback( new EntityIncrementVersionProcess( entity ) );\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase OPTIMISTIC:\n\t\t\t\t\t\tsession.getActionQueue()\n\t\t\t\t\t\t\t\t.registerCallback( new EntityVerifyVersionProcess( entity ) );\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse {\n\t\t\t\tthrow new HibernateException( \"Entity '\" + persister.getEntityName()\n\t\t\t\t\t\t\t+ \"' has no version and may not be locked at level \" + lockMode);\n\t\t\t}\n\t\t}\n\t}\n}\n","sourceCodeStart":38,"sourceCodeEnd":62,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultPostLoadEventListener.java#L38-L62","documentation":"Thrown from DefaultPostLoadEventListener.onPostLoad when the entity's current LockMode requires a version (lockMode.requiresVersion(), e.g. OPTIMISTIC, OPTIMISTIC_FORCE_INCREMENT, PESSIMISTIC_FORCE_INCREMENT) but the persister reports isVersioned() == false. Optimistic locking works by verifying or incrementing a @Version column at flush; with no version attribute there is nothing to verify or increment, so Hibernate refuses the lock. The message names the entity and the offending lock mode.","triggerScenarios":"Calling session.lock(entity, LockMode.OPTIMISTIC/OPTIMISTIC_FORCE_INCREMENT), JPA EntityManager.lock(entity, LockModeType.OPTIMISTIC/OPTIMISTIC_FORCE_INCREMENT), or Spring Data JPA @Lock(LockModeType.OPTIMISTIC_FORCE_INCREMENT) on a query for an entity class that has no @Version field; also find(entityName, id, LockMode) with a version-requiring mode. The post-load listener fires as the entity loads, so the exception surfaces on the load/lock call itself.","commonSituations":"Adding optimistic locking annotations to a repository/service for a legacy entity that never got a version column; switching from PESSIMISTIC to OPTIMISTIC lock modes during a concurrency refactor without adding @Version; upgrading code that used LockMode.READ semantics (deprecated no-op in Hibernate 6+) and mapping them to OPTIMISTIC on non-versioned entities.","solutions":["Add a @Version attribute to the entity (e.g. @Version private Long version;) and add the corresponding version column to the table (ALTER TABLE ... ADD COLUMN version BIGINT)","If the schema cannot take a version column, switch to a pessimistic mode: LockModeType.PESSIMISTIC_READ or LockModeType.WRITE-style SELECT ... FOR UPDATE locking","Remove the lock() call if no row-level concurrency control is actually needed, or implement manual version checking in an UPDATE ... WHERE clause"],"exampleFix":"// before\nem.lock(order, LockModeType.OPTIMISTIC_FORCE_INCREMENT); // Order has no @Version -> HibernateException at post-load\n\n// after\n@Entity\npublic class Order {\n    @Id Long id;\n    @Version Long version; // add version attribute + column\n}\nem.lock(order, LockModeType.OPTIMISTIC_FORCE_INCREMENT);","handlingStrategy":"validation","validationCode":"EntityPersister p = session.getFactory().getMappingMetamodel()\n        .getEntityDescriptor(Order.class);\nif (!p.isVersioned()) {\n    // entity has no @Version: use PESSIMISTIC_READ/WRITE instead of OPTIMISTIC*\n    session.lock(order, LockMode.PESSIMISTIC_WRITE);\n}\nelse {\n    session.lock(order, LockMode.OPTIMISTIC_FORCE_INCREMENT);\n}","typeGuard":"boolean lockableOptimistically(SessionFactory f, Class<?> entityType) {\n    return f.getMappingMetamodel().getEntityDescriptor(entityType).isVersioned();\n}","tryCatchPattern":"try {\n    em.lock(order, LockModeType.OPTIMISTIC_FORCE_INCREMENT);\n} catch (HibernateException e) {\n    if (e.getMessage() != null && e.getMessage().contains(\"has no version\")) {\n        // entity class lacks @Version — add a version or fall back to pessimistic locking\n        em.lock(order, LockModeType.PESSIMISTIC_WRITE);\n    } else { throw e; }\n}","preventionTips":["Give every entity that will be locked a @Version attribute from day one","Assert in architecture tests (ArchUnit) that entities used with @Lock(OPTIMISTIC*) have a version field","Prefer pessimistic locking for non-versioned legacy tables"],"tags":["hibernate","optimistic-locking","version","locking","jpa","orm"],"backgroundTag":"optimistic-locking-without-version","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}