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

  1. Normalize to a standard wrapper before building the predicate: value.intValue(), longValue(), doubleValue(), or BigDecimal.valueOf(...).
  2. For custom Number domain types, register an AttributeConverter/JdbcType and compare against the mapped attribute instead of a raw literal.
  3. 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

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


AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22). Data as JSON: /api/errors/6614a066fd88723a. Report an issue: GitHub.