hibernate/hibernate-orm · error · UnsupportedOperationException
Follow-on collection-table locking with composite keys is no
Error message
Follow-on collection-table locking with composite keys is not supported for Dialects which do not support tuples (row constructor syntax) as part of an in-list
What it means
When a collection with a composite key must be locked in the collection table via follow-on locking, LockingHelper builds an IN-list of tuple row constructors ((k1,k2) IN ((?,?),(?,?))). That syntax is only emitted when Dialect.supportsRowValueConstructorSyntaxInInList() returns true; otherwise applyCompositeCollectionKeyTableLockRestrictions (the ownerDetailsMap overload) throws UnsupportedOperationException - a known platform limitation, marked 'for now' in the source.
Source
Thrown at hibernate-core/src/main/java/org/hibernate/sql/exec/internal/lock/LockingHelper.java:268
restriction.addExpression( jdbcParameter );
parameterBindings.addBinding( jdbcParameter,
new JdbcParameterBindingImpl( jdbcMapping, value ) );
},
session
);
} );
}
private static InListPredicate applyCompositeCollectionKeyTableLockRestrictions(
PluralAttributeMapping attributeMapping,
ForeignKeyDescriptor keyDescriptor,
TableReference tableReference,
JdbcParameterBindingsImpl parameterBindings,
Map<Object, EntityDetails> ownerDetailsMap,
SharedSessionContractImplementor session) {
if ( !session.getDialect().supportsRowValueConstructorSyntaxInInList() ) {
// for now...
throw new UnsupportedOperationException(
"Follow-on collection-table locking with composite keys is not supported for Dialects"
+ " which do not support tuples (row constructor syntax) as part of an in-list"
);
}
final int jdbcTypeCount = keyDescriptor.getJdbcTypeCount();
final List<ColumnReference> columnReferences = new ArrayList<>( jdbcTypeCount );
keyDescriptor.forEachSelectable( (selectionIndex, selectableMapping) -> {
columnReferences.add( new ColumnReference( tableReference, selectableMapping ) );
} );
final InListPredicate inListPredicate = new InListPredicate( new SqlTuple( columnReferences, keyDescriptor ) );
ownerDetailsMap.forEach( (o, entityDetails) -> {
final var collectionInstance =
(PersistentCollection<?>)
entityDetails.entry().getLoadedState()[attributeMapping.getStateArrayPosition()];
final Object collectionKeyValue = collectionInstance.getKey();
View on GitHub (pinned to fad1729dce)
Solutions
- Lock the owner row instead of the collection, avoiding follow-on collection-table locking
- Rewrite the lock as explicit SQL with AND-ed per-column predicates via a native query
- Use a dialect whose database supports row-value constructors in IN-lists (Oracle, DB2, PostgreSQL), or override supportsRowValueConstructorSyntaxInInList() if your database actually supports it
- Remap the collection with a single-column key (collection id / surrogate join-table key) instead of a composite FK
Example fix
-- before (Hibernate-issued tuple IN, unsupported on this dialect) -- where (k1, k2) in ((?, ?), (?, ?)) for update -- after (native locking query, dialect-safe) select id from CollectionTable where ownerId = :id and ownerKey2 = :k2 for update
Defensive patterns
Strategy: fallback
Validate before calling
boolean tupleInListSupported(SharedSessionContractImplementor session) {
return session.getDialect().supportsRowValueConstructorSyntaxInInList();
} Prevention
- Lock owner rows rather than collection tables when collections have composite keys on tuple-IN-less dialects
- Check dialect.supportsRowValueConstructorSyntaxInInList() before requesting collection-table locks
- Prefer single-column collection keys (surrogate/collection id) on SQL Server/MySQL
When it happens
Trigger: Pessimistic locking of a collection whose owner FK is composite (owner entity uses @EmbeddedId/@IdClass) - e.g. session.buildLockRequest(...).lock() on an initialized collection or a query with LockMode.PESSIMISTIC_WRITE fetching it - on a dialect where supportsRowValueConstructorSyntaxInInList() is false (SQL Server, MySQL).
Common situations: Entities with composite identifiers migrated to databases whose dialect lacks tuple IN-list support; softwares relying on collection-table locking with composite FKs; tests passing on PostgreSQL/DB2 but failing on SQL Server/MySQL.
Related errors
- Attribute '%s' of entity '%s' is mapped by association '%s'
- Entity '${persister.getEntityName()}' has no version and may
- Foreign key ({}:{} [{}])) must have same number of columns a
- identifier mapping has wrong number of columns: " + getEntit
- Identity generation requires exactly one column
AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22).
Data as JSON: /api/errors/f200b12ac1c263f0.
Report an issue: GitHub.