{"record":{"id":"52cbd46a3293434b","repo":"hibernate/hibernate-orm","slug":"no-rows-were-returned-from-jdbc-query-for-versione","errorCode":null,"errorMessage":"No rows were returned from JDBC query for versioned entity","messagePattern":"No rows were returned from JDBC query for versioned entity","errorType":"exception","errorClass":"StaleObjectStateException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/dialect/lock/internal/SqlAstBasedLockingStrategy.java","lineNumber":206,"sourceCode":"\t\t\t\t\t);\n\t\t\t\t} );\n\t\t\t}\n\t\t}\n\t\tcatch (LockTimeoutException lockTimeout) {\n\t\t\tthrow new PessimisticEntityLockException(\n\t\t\t\t\tobject,\n\t\t\t\t\tString.format( Locale.ROOT, \"Lock timeout exceeded attempting to lock row(s) for %s\", object ),\n\t\t\t\t\tlockTimeout\n\t\t\t);\n\t\t}\n\t\tcatch (NoRowException noRow) {\n\t\t\tif ( !entityToLock.optimisticLockStyle().isNone() ) {\n\t\t\t\tfinal String entityName = entityToLock.getEntityName();\n\t\t\t\tfinal var statistics = session.getFactory().getStatistics();\n\t\t\t\tif ( statistics.isStatisticsEnabled() ) {\n\t\t\t\t\tstatistics.optimisticFailure( entityName );\n\t\t\t\t}\n\t\t\t\tthrow new StaleObjectStateException( entityName, id,\n\t\t\t\t\t\t\"No rows were returned from JDBC query for versioned entity\" );\n\t\t\t}\n\t\t\telse {\n\t\t\t\tthrow noRow;\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static void handleRestriction(\n\t\t\tObject value,\n\t\t\tSelectableMapping jdbcValueMapping,\n\t\t\tQuerySpec rootQuerySpec,\n\t\t\tLoaderSqlAstCreationState sqlAstCreationState,\n\t\t\tTableGroup rootTableGroup,\n\t\t\tJdbcParameterBindings jdbcParameterBindings) {\n\t\tfinal var jdbcParameter = new SqlTypedMappingJdbcParameter( jdbcValueMapping );\n\t\trootQuerySpec.applyPredicate(\n\t\t\t\tnew ComparisonPredicate(","sourceCodeStart":188,"sourceCodeEnd":224,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/dialect/lock/internal/SqlAstBasedLockingStrategy.java#L188-L224","documentation":"While applying a pessimistic lock via its locking SELECT, SqlAstBasedLockingStrategy received zero rows (org.hibernate.sql.results.spi.NoRowException): the row identified by id no longer exists - typically deleted concurrently between load and lock. For entities with an optimistic lock style other than NONE (@Version), Hibernate translates this into StaleObjectStateException with this message so callers treat it as an optimistic-lock failure; for unversioned entities the raw NoRowException propagates instead.","triggerScenarios":"session.lock(entity, LockMode.PESSIMISTIC_WRITE) or em.find(id, PESSIMISTIC_WRITE) where another transaction deleted (hard delete, or @SQLDelete) the row after it was loaded; locking a detached/stale reference whose row is gone; follow-on locking racing a concurrent remove().","commonSituations":"Two users editing the same record with one deleting it; cleanup jobs purging rows that in-flight requests still reference; queue/claim patterns where another worker deleted the item; soft-delete filters hiding rows that the locking select expects.","solutions":["Catch StaleObjectStateException / OptimisticLockException and re-load the entity (em.refresh won't work - use find again) or treat it as not-found","Re-fetch a fresh managed instance before locking instead of locking a long-held detached reference","If deletion is legitimate in your domain, branch on EntityNotFoundException-style handling instead of retrying","For soft deletes, verify row-level filters do not hide rows from the locking select"],"exampleFix":"// before\nsession.lock(order, LockMode.PESSIMISTIC_WRITE); // row may be gone -> StaleObjectStateException\n\n// after\ntry {\n    session.lock(order, LockMode.PESSIMISTIC_WRITE);\n} catch (StaleObjectStateException e) {\n    Order fresh = session.find(Order.class, order.getId());\n    if (fresh == null) {\n        // row deleted concurrently: treat as not-found\n    }\n}","handlingStrategy":"try-catch","validationCode":"// verify the row still exists before requesting the lock\nObject id = session.getIdentifier(entity);\nboolean exists = session.createQuery(\"select 1 from Order o where o.id = :id\", Integer.class)\n        .setParameter(\"id\", id).getResultList().isEmpty() == false;","typeGuard":null,"tryCatchPattern":"try {\n    session.lock(order, LockMode.PESSIMISTIC_WRITE);\n} catch (StaleObjectStateException e) {\n    // row vanished between load and lock: reload or treat as not-found\n    Order fresh = session.find(Order.class, order.getId());\n    if (fresh == null) { /* deleted concurrently */ }\n}","preventionTips":["Lock freshly loaded entities in the same transaction rather than long-held references","Handle deletes explicitly: coordinate delete and lock flows to avoid races","Treat StaleObjectStateException from locks like OptimisticLockException in retry/refresh logic","In work-queue patterns, tolerate items claimed-then-deleted by other workers"],"tags":["optimistic-locking","stale-object","concurrency","pessimistic-locking","hibernate"],"backgroundTag":"optimistic-lock-stale-object","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}