{"record":{"id":"24146932b4e9528b","repo":"hibernate/hibernate-orm","slug":"locking-with-having-is-not-supported","errorCode":null,"errorMessage":"Locking with HAVING is not supported","messagePattern":"Locking with HAVING is not supported","errorType":"exception","errorClass":"IllegalQueryOperationException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstTranslator.java","lineNumber":1942,"sourceCode":"\t\tif ( !querySpec.isRoot() ) {\n\t\t\tfollowOnStrategy = Locking.FollowOn.ALLOW;\n\t\t}\n\n\t\tLockStrategy strategy = LockStrategy.CLAUSE;\n\n\t\tif ( !querySpec.getGroupByClauseExpressions().isEmpty() ) {\n\t\t\tif ( followOnStrategy == Locking.FollowOn.DISALLOW ) {\n\t\t\t\tthrow new IllegalQueryOperationException( \"Locking with GROUP BY is not supported\" );\n\t\t\t}\n\t\t\telse if ( followOnStrategy == Locking.FollowOn.IGNORE ) {\n\t\t\t\treturn LockStrategy.NONE;\n\t\t\t}\n\t\t\tstrategy = LockStrategy.FOLLOW_ON;\n\t\t}\n\n\t\tif ( querySpec.getHavingClauseRestrictions() != null ) {\n\t\t\tif ( followOnStrategy == Locking.FollowOn.DISALLOW ) {\n\t\t\t\tthrow new IllegalQueryOperationException( \"Locking with HAVING is not supported\" );\n\t\t\t}\n\t\t\telse if ( followOnStrategy == Locking.FollowOn.IGNORE ) {\n\t\t\t\treturn LockStrategy.NONE;\n\t\t\t}\n\t\t\tstrategy = LockStrategy.FOLLOW_ON;\n\t\t}\n\n\t\tif ( querySpec.getSelectClause().isDistinct() ) {\n\t\t\tif ( followOnStrategy == Locking.FollowOn.DISALLOW ) {\n\t\t\t\tthrow new IllegalQueryOperationException( \"Locking with DISTINCT is not supported\" );\n\t\t\t}\n\t\t\telse if ( followOnStrategy == Locking.FollowOn.IGNORE ) {\n\t\t\t\treturn LockStrategy.NONE;\n\t\t\t}\n\t\t\tstrategy = LockStrategy.FOLLOW_ON;\n\t\t}\n\n\t\tif ( !dialect.supportsOuterJoinForUpdate() ) {","sourceCodeStart":1924,"sourceCodeEnd":1960,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstTranslator.java#L1924-L1960","documentation":"Same decision tree in determineLockingStrategy, next branch: a query with HAVING restrictions cannot use a native locking clause, so it needs follow-on locking; when the follow-on strategy is DISALLOW (JPA pessimistic lock default) Hibernate throws IllegalQueryOperationException(\"Locking with HAVING is not supported\") rather than silently locking nothing.","triggerScenarios":"Pessimistic lock mode with follow-on disallowed on a query containing a HAVING clause, e.g., 'select ... group by ... having count(*) > :min' with setLockMode(LockModeType.PESSIMISTIC_WRITE).","commonSituations":"Locking filtered aggregate/reports; repository methods with @Lock plus dynamic having clauses; migrating from Hibernate 5 where follow-on behavior defaulted differently.","solutions":["Enable follow-on locking for the statement (LockOptions.setFollowOnLocking(true)) so Hibernate uses a post-select locking query.","Remove the lock from the HAVING query and lock targeted ids in a dedicated FOR UPDATE query.","Rewrite so the HAVING filter runs unlocked and the locked part is a simple root select."],"exampleFix":"// before\nList<Object[]> r = em.createQuery(\n    \"select o.customer, sum(o.total) from Order o group by o.customer having sum(o.total) > :min\", Object[].class)\n    .setLockMode(LockModeType.PESSIMISTIC_WRITE).getResultList();\n\n// after — allow follow-on locking\nList<Object[]> r = em.createQuery(\n    \"select o.customer, sum(o.total) from Order o group by o.customer having sum(o.total) > :min\", Object[].class)\n    .unwrap(org.hibernate.query.Query.class)\n    .setLockOptions(new org.hibernate.LockOptions(org.hibernate.LockMode.PESSIMISTIC_WRITE).setFollowOnLocking(true))\n    .getResultList();","handlingStrategy":"fallback","validationCode":"boolean hasHaving = hql.toLowerCase().contains(\" having\");\nif (hasHaving && lockMode != null && lockMode.isPessimistic()) {\n    lockOptions.setFollowOnLocking(true); // allow the follow-on strategy instead of failing\n}","typeGuard":null,"tryCatchPattern":"try { em.createQuery(hql).setLockMode(LockModeType.PESSIMISTIC_WRITE).getResultList(); }\ncatch (org.hibernate.query.IllegalQueryOperationException e) {\n    if (e.getMessage().equals(\"Locking with HAVING is not supported\")) {\n        em.createQuery(hql).unwrap(org.hibernate.query.Query.class)\n          .setLockOptions(new org.hibernate.LockOptions(org.hibernate.LockMode.PESSIMISTIC_WRITE).setFollowOnLocking(true))\n          .getResultList();\n    } else { throw e; }\n}","preventionTips":["Keep locking and HAVING filters in separate statements.","Validate lock-mode usage in a lint/test rule for repository methods with dynamic filters."],"tags":["hibernate","orm","pessimistic-locking","having","jpa"],"backgroundTag":"pessimistic-locking-unsupported","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}