hibernate/hibernate-orm · error · SqmHqlNumericLiteral.TypeException
Unexpected Java type [%s] for numeric literal - %s
Error message
Unexpected Java type [%s] for numeric literal - %s
What it means
When Hibernate builds an HQL numeric literal, SqmHqlNumericLiteral.determineTypeCategory maps the literal's Java class to a NumericTypeCategory (BigDecimal, double/float, BigInteger, Long, Integer for Short|Integer). Any Number implementation outside that recognized set falls through to TypeException: 'Unexpected Java type [class] for numeric literal - value'.
Source
Thrown at hibernate-core/src/main/java/org/hibernate/query/sqm/tree/spi/expression/SqmHqlNumericLiteral.java:162
if ( Float.class.equals( javaTypeClass ) ) {
return NumericTypeCategory.FLOAT;
}
if ( BigInteger.class.equals( javaTypeClass ) ) {
return NumericTypeCategory.BIG_INTEGER;
}
if ( Long.class.equals( javaTypeClass ) ) {
return NumericTypeCategory.LONG;
}
if ( Short.class.equals( javaTypeClass )
|| Integer.class.equals( javaTypeClass ) ) {
return NumericTypeCategory.INTEGER;
}
throw new TypeException( literalValue, javaTypeClass );
}
public static class TypeException extends HibernateException {
public TypeException(String literalValue, Class<?> javaType) {
super(
String.format(
Locale.ROOT,
"Unexpected Java type [%s] for numeric literal - %s",
javaType.getTypeName(),
literalValue
)
);
}
}
}
View on GitHub (pinned to fad1729dce)
Solutions
- Normalize to a standard wrapper before building the predicate: value.intValue(), longValue(), doubleValue(), or BigDecimal.valueOf(...).
- For custom Number domain types, register an AttributeConverter/JdbcType and compare against the mapped attribute instead of a raw literal.
- Widen Byte values to Short or Integer explicitly before use.
Example fix
// before AtomicInteger remaining = ...; cb.equal( root.get( "remaining" ), remaining ); // literal built from AtomicInteger -> TypeException // after cb.equal( root.get( "remaining" ), remaining.get() ); // plain int literal
Defensive patterns
Strategy: validation
Validate before calling
static boolean isSupportedLiteralNumber(Number n) {
return n instanceof Integer || n instanceof Long || n instanceof Short || n instanceof Double
|| n instanceof Float || n instanceof BigDecimal || n instanceof BigInteger;
}
Number safe = isSupportedLiteralNumber( value ) ? value : Double.valueOf( value.doubleValue() ); Prevention
- Convert custom Number types (Money, Quantity, Atomic*) to a standard wrapper before passing them as literals.
- Never pass AtomicInteger/AtomicLong/Byte directly into criteria predicates.
- Call .get()/.intValue()/.longValue() on atomic counters when binding them to queries.
When it happens
Trigger: Creating a numeric literal from a Number Hibernate does not special-case: java.util.concurrent.atomic.AtomicInteger/AtomicLong, java.lang.Byte, or a custom Number subclass - e.g. builder.literal(atomicInteger) or passing a custom Money extends Number value directly into a criteria predicate.
Common situations: Domain classes extending Number (Money, Quantity, Offset) passed directly as literals; counters held in AtomicInteger; values autoboxed to Byte from byte fields, byte[] parsing, or older APIs.
Related errors
- Couldn't determine basic type for java type: {}
- Property '" + qualify( getEntityName(), prop.getName() ) + "
- The property %s.%s uses a wrapper type Byte[]/Character[] wh
- Entity discriminator cannot be de-referenced
- Could not build SqmPathSource for entity identifier: {}
AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22).
Data as JSON: /api/errors/6614a066fd88723a.
Report an issue: GitHub.