hibernate/hibernate-orm · error · IllegalArgumentException
Can't emulate order preserving row constructor through strin
Error message
Can't emulate order preserving row constructor through string concatenation for expression [%s] which is of type [%s]
What it means
Hibernate emulates row value constructors (tuples like (a,b)) on databases lacking native row-constructor syntax by concatenating the tuple components into a single sortable string. Each component must be wrapped so that string order matches the original value order; the wrapper switch only knows how to handle STRING cast types and numeric types with known precision/scale. This IllegalArgumentException is thrown from the default branch when a tuple component's JDBC cast type (e.g. BINARY, OTHER, temporal, JSON, UUID) has no order-preserving string encoding.
Source
Thrown at hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstTranslator.java:3325
case INTEGER, LONG -> castNumberToString( expression, 19, 0 );
case FIXED -> {
if ( expression.getExpressionType() instanceof SqlTypedMapping sqlTypedMapping ) {
if ( sqlTypedMapping.getPrecision() != null && sqlTypedMapping.getScale() != null ) {
yield castNumberToString(
expression,
sqlTypedMapping.getPrecision(),
sqlTypedMapping.getScale()
);
}
}
throw new IllegalArgumentException(
String.format(
"Can't emulate order preserving row constructor through string concatenation for numeric expression [%s] without precision or scale",
expression
)
);
}
default -> throw new IllegalArgumentException(
String.format(
"Can't emulate order preserving row constructor through string concatenation for expression [%s] which is of type [%s]",
expression,
jdbcMapping.getCastType()
)
);
};
}
private int wrapRowComponentAsOrderPreservingConcatArgumentSizeEstimate(Expression expression) {
final JdbcMapping jdbcMapping = expression.getExpressionType().getSingleJdbcMapping();
switch ( jdbcMapping.getCastType() ) {
case STRING:
if ( expression.getExpressionType() instanceof SqlTypedMapping sqlTypedMapping ) {
final Long length = sqlTypedMapping.getLength();
if ( length != null ) {
return length.intValue();
}View on GitHub (pinned to fad1729dce)
Solutions
- Rewrite the query to order by the individual columns instead of the tuple (order by e.a, e.b)
- Cast the offending component to a string in the query (e.g. cast(e.x as string)) so the STRING branch applies
- Use a database/dialect with native row value constructor support (PostgreSQL, MySQL >= 8.0.19) so emulation is not needed
- Ensure numeric attributes map with explicit precision/scale (@Column(precision=..., scale=...)) so the numeric branch applies
- Upgrade Hibernate - cast-type coverage of the emulation improves across versions
Example fix
// before (SQL Server dialect)
List<Order> orders = session.createQuery("from Order o order by (o.id.customerId, o.id.seqNo)", Order.class).list();
// after
List<Order> orders = session.createQuery("from Order o order by o.id.customerId, o.id.seqNo", Order.class).list(); Defensive patterns
Strategy: fallback
Validate before calling
// Before running tuple ordering, check the dialect and component cast types
boolean nativeRowCtor = dialect.supportsRowValueConstructorSyntax();
boolean emulable = orderComponents.stream().allMatch(e -> {
CastType t = e.getExpressionType().getSingleJdbcMapping().getCastType();
return t == CastType.STRING || t == CastType.NUMERIC || t == CastType.EXACT_NUMERIC;
});
if (!nativeRowCtor && !emulable) { /* use per-column ordering instead */ } Try / catch
try {
query.list();
} catch (IllegalArgumentException e) {
if (e.getMessage() != null && e.getMessage().startsWith("Can't emulate order preserving row constructor")) {
// fall back to ordering by individual columns
} else throw e;
} Prevention
- Prefer ordering by individual columns over tuple/row-constructor ordering
- On SQL Server / MySQL < 8.0.19, avoid order by (a, b) and composite embeddable ordering
- Keep numeric mappings annotated with explicit precision/scale
When it happens
Trigger: An HQL/Criteria query orders by a tuple or row constructor (e.g. order by (e.a, e.b), ordering by an embeddable/id-class, key(e) ordering, tuple-based pagination) on a dialect where supportsRowValueConstructorSyntax() is false (e.g. SQL Server, MySQL before 8.0.19, older DB2), and at least one tuple component's jdbcMapping.getCastType() is neither STRING nor a numeric type with precision and scale.
Common situations: Sorting or paging by composite keys/embeddables on SQL Server; entity id-class tuple comparisons; @OrderBy on composite/id attributes; upgrading Hibernate where tuple emulation paths became stricter for types without precision/scale (e.g. numeric mappings missing precision metadata); custom BasicType registrations returning an unusual CastType.
Related errors
- Can't emulate equality preserving row constructor through st
- Can't emulate relational tuple subquery predicate with limit
- Can't emulate quantified tuple subquery predicate with limit
- Summarization is not supported by DBMS!
- SingleStore doesn't support ANY clause
AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22).
Data as JSON: /api/errors/5011edeafac55436.
Report an issue: GitHub.