{"record":{"id":"5262d2ad11273be2","repo":"hibernate/hibernate-orm","slug":"couldn-t-find-subclass-index-for-joined-table-refe","errorCode":null,"errorMessage":"Couldn't find subclass index for joined table reference ","messagePattern":"Couldn't find subclass index for joined table reference ","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/sql/ast/internal/StandardLockingClauseStrategy.java","lineNumber":274,"sourceCode":"\t\t\t}\n\t\t}\n\t}\n\n\tprivate TableMapping determineTableMapping(EntityPersister entityPersister, TableReferenceJoin tableReferenceJoin) {\n\t\tfinal NamedTableReference joinedTableReference = tableReferenceJoin.getJoinedTableReference();\n\t\tfor ( EntityTableMapping tableMapping : entityPersister.getTableMappings() ) {\n\t\t\tif ( joinedTableReference.containsAffectedTableName( tableMapping.getTableName() ) ) {\n\t\t\t\treturn tableMapping;\n\t\t\t}\n\t\t}\n\t\tfor ( EntityMappingType subMappingType : entityPersister.getSubMappingTypes() ) {\n\t\t\tfor ( EntityTableMapping tableMapping : subMappingType.getEntityPersister().getTableMappings() ) {\n\t\t\t\tif ( joinedTableReference.containsAffectedTableName( tableMapping.getTableName() ) ) {\n\t\t\t\t\treturn tableMapping;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tthrow new IllegalArgumentException( \"Couldn't find subclass index for joined table reference \" + joinedTableReference );\n\t}\n\n\tprivate EntityPersister determineEntityPersister(ModelPartContainer modelPart) {\n\t\tif ( modelPart instanceof EntityPersister entityPersister ) {\n\t\t\treturn entityPersister;\n\t\t}\n\t\telse if ( modelPart instanceof PluralAttributeMapping pluralAttributeMapping ) {\n\t\t\treturn pluralAttributeMapping.getCollectionDescriptor().getElementPersister();\n\t\t}\n\t\telse if ( modelPart instanceof EntityAssociationMapping entityAssociationMapping ) {\n\t\t\treturn entityAssociationMapping.getAssociatedEntityMappingType().getEntityPersister();\n\t\t}\n\t\telse {\n\t\t\tthrow new IllegalArgumentException( \"Expected table group with table joins to have an entity typed model part but got: \" + modelPart );\n\t\t}\n\t}\n\n\tprivate String[] determineKeyColumnNames(TableGroup tableGroup) {","sourceCodeStart":256,"sourceCodeEnd":292,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/sql/ast/internal/StandardLockingClauseStrategy.java#L256-L292","documentation":"When translating a pessimistic locking clause of the form FOR UPDATE OF <alias/table>, StandardLockingClauseStrategy maps each joined table reference to an EntityTableMapping — first against the entity's own tables, then against its subclass mappings. If no mapping's table name matches the reference (wrong alias, unquoted/case mismatch, or a table that belongs to a join/CTE rather than the entity hierarchy), it throws IllegalArgumentException naming the unmatched reference.","triggerScenarios":"HQL/JPQL 'select ... for update of x' where x is an alias of a joined collection table, a CTE, or an unrelated entity; using a subclass table alias in a polymorphic query; database identifier case/quoting differences between the alias and the mapped table name.","commonSituations":"Ported native FOR UPDATE OF hints into HQL; pessimistic locking on inheritance hierarchies with joined subclass tables; case-sensitive schemas (Oracle upper-cases unquoted identifiers) where the alias casing does not match mapping metadata.","solutions":["Reference only aliases of entity-root tables of the locked hierarchy in the OF list","Drop the explicit OF list — plain 'for update' locks all tables and avoids alias resolution","Check table-name quoting/case in @Table against the alias used in the query","Upgrade Hibernate — the locking clause strategy is internal and actively fixed between 6.x releases"],"exampleFix":"// before (HQL)\nselect o from Order o join o.lines l where o.due < :d\n    order by o.id for update of l  // 'l' is a collection table join -> no mapping\n\n// after\nselect o from Order o join fetch o.lines l where o.due < :d\n    order by o.id for update  // lock the entity root(s)","handlingStrategy":"validation","validationCode":"// validate aliases before issuing a FOR UPDATE OF query\nSet<String> tableNames = new HashSet<>();\nEntityPersister p = (EntityPersister) sessionFactory.getMetamodel()\n        .entityPersister(Order.class);\nfor (EntityTableMapping t : p.getTableMappings()) tableNames.add(t.getTableName());\nif (!tableNames.contains(aliasTableName)) {\n    throw new IllegalArgumentException(\"alias \" + alias + \" is not a mapped table of the entity\");\n}","typeGuard":null,"tryCatchPattern":"try {\n    return query.getResultList();\n}\ncatch (IllegalArgumentException e) {\n    if (e.getMessage() != null && e.getMessage().contains(\"Couldn't find subclass index\")) {\n        // rewrite without the explicit FOR UPDATE OF list and retry\n        return queryWithoutOfList().getResultList();\n    }\n    throw e;\n}","preventionTips":["Only reference entity-root table aliases in the FOR UPDATE OF list","Prefer plain 'for update' over 'for update of <alias>' unless required","Verify table name quoting/case matches between @Table and query aliases"],"tags":["locking","hql","inheritance","sql-generation"],"backgroundTag":"invalid-query-restriction","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}