{"record":{"id":"aabf96c93a722707","repo":"hibernate/hibernate-orm","slug":"lock-mode-lockmode-not-valid-for-locking-via-u","errorCode":null,"errorMessage":"Lock mode ${lockMode} not valid for locking via 'update' statement","messagePattern":"Lock mode (.+?) not valid for locking via 'update' statement","errorType":"exception","errorClass":"HibernateException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/dialect/lock/AbstractPessimisticUpdateLockingStrategy.java","lineNumber":42,"sourceCode":" */\npublic abstract class AbstractPessimisticUpdateLockingStrategy implements LockingStrategy {\n\n\tprivate final EntityPersister lockable;\n\tprivate final LockMode lockMode;\n\tprivate final String sql;\n\n\t/**\n\t * Construct a locking strategy based on SQL UPDATE statements.\n\t *\n\t * @param lockable The metadata for the entity to be locked.\n\t * @param lockMode Indicates the type of lock to be acquired.  Note that\n\t * read-locks are not valid for this strategy.\n\t */\n\tpublic AbstractPessimisticUpdateLockingStrategy(EntityPersister lockable, LockMode lockMode) {\n\t\tthis.lockable = lockable;\n\t\tthis.lockMode = lockMode;\n\t\tif ( lockMode.lessThan( LockMode.PESSIMISTIC_READ ) ) {\n\t\t\tthrow new HibernateException( \"Lock mode \" + lockMode\n\t\t\t\t\t\t+ \" not valid for locking via 'update' statement\" );\n\t\t}\n\t\tif ( !lockable.isVersioned() ) {\n\t\t\tthrow new HibernateException( \"Entity '\" + lockable.getEntityName()\n\t\t\t\t\t\t+ \"' has no version and may not be locked via 'update' statement\" );\n\t\t}\n\t\tthis.sql = generateLockString();\n\t}\n\n\t@Override\n\tpublic void lock(Object id, Object version, Object object, int timeout, SharedSessionContractImplementor session) {\n\t\ttry {\n\t\t\tdoLock( id, version, session );\n\t\t}\n\t\tcatch (JDBCException e) {\n\t\t\tthrow new PessimisticEntityLockException( object, \"Could not obtain pessimistic lock\", e );\n\t\t}\n\t}","sourceCodeStart":24,"sourceCodeEnd":60,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/dialect/lock/AbstractPessimisticUpdateLockingStrategy.java#L24-L60","documentation":"AbstractPessimisticUpdateLockingStrategy implements pessimistic locking by issuing an UPDATE ... where id=? and version=? against the row. Such an update can only express a write-level lock, so the constructor rejects any LockMode weaker than PESSIMISTIC_READ with HibernateException 'Lock mode <mode> not valid for locking via 'update' statement'. The strategy is built by dialects whose getLockingStrategy maps lock modes to update-based locking.","triggerScenarios":"A dialect returns an update-based locking strategy for a weak lock mode and Hibernate constructs the strategy - e.g. session.buildLockRequest(new LockOptions(LockMode.READ)).lock(entity) or session.lock(entity, LockMode.OPTIMISTIC) - while the active dialect uses AbstractPessimisticUpdateLockingStrategy for that mode (lockMode.lessThan(PESSIMISTIC_READ) is true). Custom dialects overriding getLockingStrategy too broadly hit this immediately at strategy construction.","commonSituations":"Custom dialect implementations that return UpdateLockingStrategy for every requested LockMode; legacy code using old LockMode constants (READ/UPGRADE, now NONE/OPTIMISTIC) on databases where Hibernate emulates locking via UPDATE; upgrading Hibernate versions where deprecated lock modes map differently.","solutions":["Request PESSIMISTIC_READ or stronger (usually PESSIMISTIC_WRITE) when the dialect uses update-based locking","If you maintain a custom dialect, return the update strategy only for PESSIMISTIC_READ/PESSIMISTIC_WRITE/PESSIMISTIC_FORCE_INCREMENT and a select-based strategy otherwise","Replace deprecated LockMode.READ/UPGRADE usages with their modern equivalents that the dialect maps correctly","Prefer optimistic (@Version-based) locking for read intents instead of forcing weak pessimistic modes"],"exampleFix":"// before\nsession.buildLockRequest(new LockOptions(LockMode.READ)).lock(person);\n\n// after\nsession.buildLockRequest(new LockOptions(LockMode.PESSIMISTIC_WRITE)).lock(person);","handlingStrategy":"validation","validationCode":"// Only request modes the strategy accepts\nstatic LockMode sanitizeForUpdateLocking(LockMode requested) {\n    return requested.lessThan(LockMode.PESSIMISTIC_READ) ? LockMode.PESSIMISTIC_WRITE : requested;\n}\n\nsession.buildLockRequest(new LockOptions(sanitizeForUpdateLocking(LockMode.READ))).lock(person);","typeGuard":null,"tryCatchPattern":"try {\n    session.buildLockRequest(new LockOptions(mode)).lock(person);\n}\ncatch (HibernateException e) {\n    if (e.getMessage().contains(\"not valid for locking via 'update' statement\")) {\n        throw new IllegalArgumentException(\"Use PESSIMISTIC_READ or stronger with update-based locking (got \" + mode + \")\", e);\n    }\n    throw e;\n}","preventionTips":["Standardize on PESSIMISTIC_WRITE for pessimistic lock requests instead of legacy READ/UPGRADE modes","In custom dialects, branch getLockingStrategy on the mode threshold before returning an update-based strategy","Ban LockMode.NONE/READ/OPTIMISTIC in code paths that go through session.lock on lock-emulating dialects"],"tags":["hibernate","locking","pessimistic-lock","lock-mode","dialect"],"backgroundTag":"invalid-lock-mode","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}