{"record":{"id":"b9676d36f1103b2b","repo":"hibernate/hibernate-orm","slug":"unsupported-expression-type-expected-columnrefere","errorCode":null,"errorMessage":"Unsupported Expression type (expected ColumnReference) : %s","messagePattern":"Unsupported Expression type \\(expected ColumnReference\\) : (.+?)","errorType":"exception","errorClass":"UnsupportedOperationException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/sql/exec/internal/lock/LockingCreationStates.java","lineNumber":168,"sourceCode":"\tpublic SqlSelection resolveSqlSelection(\n\t\t\tExpression expression,\n\t\t\tJavaType<?> javaType,\n\t\t\tFetchParent fetchParent,\n\t\t\tTypeConfiguration typeConfiguration) {\n\t\tfinal SqlSelection sqlSelection = sqlSelectionMap.get( expression );\n\t\tif ( sqlSelection != null ) {\n\t\t\treturn sqlSelection;\n\t\t}\n\n\t\tif ( expression instanceof ColumnReference columnReference ) {\n\t\t\tfinal var selection =\n\t\t\t\t\tnew SqlSelectionImpl( columnReference, querySpec.getSelectClause().getSqlSelections().size() );\n\t\t\tsqlSelectionMap.put( expression, selection );\n\t\t\tquerySpec.getSelectClause().addSqlSelection( selection );\n\t\t\treturn selection;\n\t\t}\n\n\t\tthrow new UnsupportedOperationException( \"Unsupported Expression type (expected ColumnReference) : \" + expression );\n\t}\n\n\t@Override\n\tpublic ModelPart resolveModelPart(NavigablePath navigablePath) {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic ImmutableFetchList visitFetches(FetchParent fetchParent) {\n\t\tfinal var fetches = new ImmutableFetchList.Builder( fetchParent.getReferencedMappingContainer() );\n\t\tfinal var referencedMappingContainer = fetchParent.getReferencedMappingContainer();\n\t\tfinal int size = referencedMappingContainer.getNumberOfFetchables();\n\t\tfor ( int i = 0; i < size; i++ ) {\n\t\t\tfinal Fetchable fetchable = referencedMappingContainer.getFetchable( i );\n\t\t\tprocessFetchable( fetchParent, fetchable, fetches );\n\t\t}\n\t\treturn fetches.build();\n\t}","sourceCodeStart":150,"sourceCodeEnd":186,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/sql/exec/internal/lock/LockingCreationStates.java#L150-L186","documentation":"Follow-on locking builds a separate locking SELECT keyed on the row's columns; LockingCreationStates materializes SqlSelections for those key expressions. It only knows how to handle a ColumnReference - any other Expression (formula, SQL fragment, tuple, function result, parameter) throws UnsupportedOperationException('Unsupported Expression type (expected ColumnReference)').","triggerScenarios":"Pessimistic locking (query.setLockMode(LockModeType.PESSIMISTIC_WRITE), session.buildLockRequest/lock, find with lock options) that forces follow-on locking, where a lock-key or selected expression is not a plain column: @Formula or @ColumnTransformer attributes involved in key resolution, computed expressions in the locking result, custom key mappings.","commonSituations":"@Formula/@ColumnTransformer on attributes that feed identity/FK resolution; dialects that force follow-on locking (no FOR UPDATE with joins, e.g. legacy SQL Server/Sybase dialects); locking queries that select aggregates or functions.","solutions":["Keep formulas/computed expressions out of the locking key path - lock on plain columns only","Lock via a dedicated query selecting only real columns (e.g. 'select id from T where id = :id for update') then refresh/load the entity","Upgrade Hibernate - later versions broadened the expression types follow-on locking can materialize","If the dialect forces follow-on locking, issue a native locking statement for this case"],"exampleFix":"// before\nList<Order> orders = session.createQuery(\n    \"select o.id, count(oi) from Order o join o.items oi group by o.id\", Order.class )\n    .setLockMode( LockModeType.PESSIMISTIC_WRITE ).getResultList(); // aggregate in lock key\n\n// after\nList<Long> ids = session.createQuery(\n    \"select o.id from Order o\", Long.class )\n    .setLockMode( LockModeType.PESSIMISTIC_WRITE ).getResultList();\nList<Order> orders = session.byMultipleIds( Order.class ).multiLoad( ids );","handlingStrategy":"validation","validationCode":"// lock on plain columns only: run the locking query against ids, then load\nList<Long> ids = session.createQuery( \"select o.id from Order o where ...\", Long.class )\n        .setLockMode( LockModeType.PESSIMISTIC_WRITE )\n        .getResultList();\nList<Order> locked = session.byMultipleIds( Order.class ).multiLoad( ids );","typeGuard":null,"tryCatchPattern":"try {\n    return query.setLockMode( LockModeType.PESSIMISTIC_WRITE ).getResultList();\n} catch ( UnsupportedOperationException e ) {\n    if ( e.getMessage() != null && e.getMessage().contains( \"expected ColumnReference\" ) ) {\n        // fall back: lock ids with a plain-column query, then load entities\n    } else { throw e; }\n}","preventionTips":["Keep @Formula/@ColumnTransformer attributes out of keys used by follow-on locking","Prefer locking dedicated id-only queries and then loading entities","Know your dialect: if it forces follow-on locking, avoid locking joins/computed selects"],"tags":["hibernate","orm","locking","pessimistic-lock","formula","sql-ast"],"backgroundTag":"pessimistic-lock-unsupported","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}