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 numeric expression [%s] without precision or scale
What it means
wrapRowComponentAsOrderPreservingConcatArgument converts each search/cycle key to a string whose lexicographic order matches the value's numeric order; for that it must left-pad fixed-point numbers to a known width, which requires precision and scale from a SqlTypedMapping. When the expression's cast type is FIXED (numeric/decimal) but its mapping exposes no precision or scale — typically a computed expression, function result, or untyped cast — Hibernate cannot build the padding and throws IllegalArgumentException.
Source
Thrown at hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstTranslator.java:3318
* This is because the translation from the types to strings is not guaranteed to result in the same ordering.
*/
private SqlAstNode wrapRowComponentAsOrderPreservingConcatArgument(Expression expression) {
final JdbcMapping jdbcMapping = expression.getExpressionType().getSingleJdbcMapping();
return switch ( jdbcMapping.getCastType() ) {
case STRING -> expression;
case BOOLEAN, INTEGER_BOOLEAN, TF_BOOLEAN, YN_BOOLEAN -> castToString( expression );
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();View on GitHub (pinned to fad1729dce)
Solutions
- Declare precision and scale on the numeric mapping used in the search specification: @Column(precision = 19, scale = 2) — and regenerate/verify the schema matches.
- Search by a plain, mapped numeric column (or its integral/string projection) instead of a computed expression like a+b or function results.
- Cast the computed key to a concrete typed literal via a typed parameter or tuple-cast so the mapping retains precision/scale.
- Run the query as native SQL, or switch to a dialect with row/array emulation where the padded-string encoding is unnecessary.
Example fix
// before: BigDecimal attribute without precision/scale used as search key @Column(name = "sort_key") private BigDecimal sortKey; // ... // "with recursive t as (...) search depth first by sortKey set ord" // after: give the mapping precision and scale so the order-preserving // string encoding can left-pad correctly @Column(name = "sort_key", precision = 19, scale = 4) private BigDecimal sortKey;
Defensive patterns
Strategy: validation
Validate before calling
// Before running the query: verify the search-by mapping can be order-preservingly encoded
for (SingularAttribute<?, ?> attr : searchByAttributes) {
if ( attr.getJavaType() == BigDecimal.class ) {
org.hibernate.mapping.Property p = sessionFactory.getMetamodel()
.entityPersister(attr.getDeclaringType().getJavaType()).getProperty(attr.getName());
// simpler: check the column directly
}
}
// Practical check on the JDBC metadata side:
DatabaseMetaData md = connection.getMetaData();
// ensure columns used in SEARCH BY have NUMERIC(p,s) not bare DECIMAL/DOUBLE without precision
if ( !dialect.supportsRecursiveSearchClause() && usesDecimalSearchKey && !hasPrecisionAndScale ) {
throw new IllegalStateException("Decimal SEARCH BY key needs precision/scale or a different key");
} Try / catch
try {
return session.createQuery(hql, ResultDto.class).getResultList();
} catch (IllegalArgumentException e) {
if ( e.getMessage() != null && e.getMessage().contains("without precision or scale") ) {
// switch the search key to a plain mapped column or a cast with explicit type, or use native SQL
return session.createNativeQuery(nativeSql, ResultDto.class).getResultList();
}
throw e;
} Prevention
- Always declare precision and scale on BigDecimal mappings used in recursive CTE SEARCH/CYCLE clauses.
- Search by plain mapped columns, not computed expressions (a+b, function results) whose types lose precision/scale metadata.
- Prefer integral or string keys for search ordering; they encode without precision metadata.
- Verify the physical column type (NUMERIC(p,s)) matches the mapping, since Hibernate reads precision from the mapping/SqlTypedMapping.
When it happens
Trigger: A recursive CTE SEARCH (or CYCLE) specification that searches by a decimal/numeric expression whose type carries no precision+scale — e.g. an arithmetic expression like (a+b), a function return like coalesce/avg, or an attribute mapped without @Column(precision/scale) — rendered through emulateSearchClauseOrderWithString's order-preserving concat on a dialect without row/array emulation.
Common situations: MySQL-family targets (string-concat path) where the search key is a computed BigDecimal; entities with numeric attributes missing precision/scale in the mapping (relying on database defaults Hibernate never sees); queries ported from PostgreSQL (row/array emulation path) that never exposed the missing metadata; dynamically built cast() expressions dropping the SqlTypedMapping.
Related errors
- Could not determine a depth column name after 5 tries!
- Can't emulate search clause for descending search specificat
- Can't emulate search clause for search specifications with e
- Property '" + qualify( getEntityName(), prop.getName() ) + "
- The property %s.%s uses a wrapper type Byte[]/Character[] wh
AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22).
Data as JSON: /api/errors/f234aaf5b4ca2102.
Report an issue: GitHub.