{"record":{"id":"fe13d9ca853971bf","repo":"hibernate/hibernate-orm","slug":"could-not-obtain-pessimistic-lock","errorCode":null,"errorMessage":"Could not obtain pessimistic lock","messagePattern":"Could not obtain pessimistic lock","errorType":"exception","errorClass":"PessimisticEntityLockException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/dialect/lock/AbstractPessimisticUpdateLockingStrategy.java","lineNumber":58,"sourceCode":"\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}\n\n\tvoid doLock(Object id, Object version, SharedSessionContractImplementor session) {\n\t\ttry {\n\t\t\tfinal var factory = session.getFactory();\n\t\t\tfinal var jdbcCoordinator = session.getJdbcCoordinator();\n\t\t\tfinal var preparedStatement = jdbcCoordinator.getStatementPreparer().prepareStatement( sql );\n\t\t\ttry {\n\t\t\t\tfinal var versionType = lockable.getVersionType();\n\t\t\t\tfinal var identifierType = lockable.getIdentifierType();\n\n\t\t\t\tversionType.nullSafeSet( preparedStatement, version, 1, session );\n\t\t\t\tint offset = 2;\n\n\t\t\t\tidentifierType.nullSafeSet( preparedStatement, id, offset, session );\n\t\t\t\toffset += identifierType.getColumnSpan( factory.getRuntimeMetamodels() );\n","sourceCodeStart":40,"sourceCodeEnd":76,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/dialect/lock/AbstractPessimisticUpdateLockingStrategy.java#L40-L76","documentation":"AbstractPessimisticUpdateLockingStrategy.lock() executes the locking UPDATE through JDBC; any SQLException surfaced as a JDBCException is wrapped in PessimisticEntityLockException with message 'Could not obtain pessimistic lock', carrying the original exception as cause. It signals the database refused or failed the lock acquisition - lock wait timeout, deadlock victim, permission or connection failure - rather than a mapping problem. The original SQLState/error code stays available via getCause().","triggerScenarios":"session.lock(entity, LockMode.PESSIMISTIC_WRITE) or buildLockRequest(...).lock() while another transaction holds a conflicting row lock past innodb_lock_wait_timeout / lock_timeout; being chosen deadlock victim by the database; the locking UPDATE violating permissions or the connection dying mid-statement. All surface as JDBCException inside doLock and are rethrown as PessimisticEntityLockException.","commonSituations":"Long-running transactions that lock popular rows (order processing, scheduler jobs); lock ordering inconsistencies across services causing deadlocks; tight retry loops that immediately re-acquire locks; connection pool exhaustion causing failures during the lock statement.","solutions":["Inspect getCause() (and its SQLState/error code) to classify: retry deadlocks and timeouts with backoff, fix permissions or connection issues instead of retrying","Shorten the transaction that holds conflicting locks, and always lock rows in a consistent order to avoid deadlocks","Set a lock timeout hint (e.g. LockOptions.setTimeout(Timeout.seconds(n)) or jakarta.persistence.lock.timeout) so waiting fails fast instead of the driver default","If contention is structural, switch to optimistic locking for that use case"],"exampleFix":"// before\nsession.lock(person, LockMode.PESSIMISTIC_WRITE);\n\n// after\ntry {\n    session.lock(person, LockMode.PESSIMISTIC_WRITE);\n}\ncatch (PessimisticEntityLockException e) {\n    if (isDeadlockOrTimeout(e.getCause())) {\n        // back off and retry with jitter, then reload the entity\n    }\n    else {\n        throw e;\n    }\n}","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n    session.buildLockRequest(new LockOptions(LockMode.PESSIMISTIC_WRITE)\n            .setTimeout(Timeout.seconds(5))).lock(person);\n}\ncatch (PessimisticEntityLockException e) { // org.hibernate.dialect.lock\n    Throwable cause = e.getCause();\n    if (cause instanceof PessimisticLockException\n            || (cause instanceof JDBCException jdbc && isDeadlockOrTimeout(jdbc.getSQLException()))) {\n        backoffAndRetry(); // exponential backoff + jitter, reload entity after retry\n    }\n    else {\n        throw e;\n    }\n}","preventionTips":["Always set an explicit lock timeout so contention fails fast instead of hitting driver defaults","Acquire locks in a globally consistent order (e.g. ordered by primary key) to prevent deadlocks","Keep lock-holding transactions short: lock, act, commit - no remote calls while holding a lock","Classify the cause (SQLState 40001 deadlock / 55P03 lock_not_available) before deciding to retry"],"tags":["hibernate","locking","pessimistic-lock","deadlock","concurrency"],"backgroundTag":"lock-acquisition-failed","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}