hibernate/hibernate-orm · error · IllegalArgumentException

Illegal attempt to treat 'java.sql.Date' as 'java.sql.Time'

Error message

Illegal attempt to treat 'java.sql.Date' as 'java.sql.Time'

What it means

JdbcDateJavaType is Hibernate's descriptor for DATE-precision values (java.sql.Date). Its unwrap() supports conversion to many targets (java.util.Date, Calendar, Timestamp, LocalDate), but it deliberately refuses java.sql.Time because turning a date-only value into a time-only value is not a meaningful conversion. The IllegalArgumentException fires whenever a code path (parameter binding, result extraction, dialect or user-type unwrap) asks Hibernate to convert a Date-precision value into java.sql.Time.

Source

Thrown at hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JdbcDateJavaType.java:169

			return type.cast( toDateEpoch( value ) );
		}

		if ( String.class.isAssignableFrom( type ) ) {
			return type.cast( toString( value ) );
		}

		if ( Calendar.class.isAssignableFrom( type ) ) {
			final var gregorianCalendar = new GregorianCalendar();
			gregorianCalendar.setTimeInMillis( toDateEpoch( value ) );
			return type.cast( gregorianCalendar );
		}

		if ( java.sql.Timestamp.class.isAssignableFrom( type ) ) {
			return type.cast( new java.sql.Timestamp( toDateEpoch( value ) ) );
		}

		if ( java.sql.Time.class.isAssignableFrom( type ) ) {
			throw new IllegalArgumentException( "Illegal attempt to treat 'java.sql.Date' as 'java.sql.Time'" );
		}

		throw unknownUnwrap( type );
	}

	private LocalDate unwrapLocalDate(java.util.Date value) {
		return value instanceof java.sql.Date date
				? date.toLocalDate()
				: new java.sql.Date( toDateEpoch( value ) ).toLocalDate();
	}

	@Override
	public @Nullable Date wrap(@Nullable Object value, WrapperOptions options) {
		if ( value == null ) {
			return null;
		}
		final var wrapped = wrapOrNull( value );
		if ( wrapped == null ) {

View on GitHub (pinned to fad1729dce)

Solutions

  1. Make the temporal precision consistent: use the same @Temporal value on the entity field and on every query.setParameter(...) call for that attribute
  2. Replace legacy java.util.Date/java.sql.* fields with java.time types (LocalDate, LocalTime, LocalDateTime) - Hibernate 6 maps these precisely and this class of cross-unwrap cannot happen
  3. If you really need a Time from a Date value, convert it yourself (new java.sql.Time(date.getTime())) instead of relying on Hibernate's unwrap
  4. Audit custom UserType/AttributeConverter implementations for unwrap() calls that request java.sql.Time.class while handling date values

Example fix

// before
@Temporal(TemporalType.DATE)
private java.util.Date birthDate;
...
query.setParameter("p", entity.getBirthDate(), TemporalType.TIME); // IllegalArgumentException

// after
@Temporal(TemporalType.DATE)
private java.util.Date birthDate;
...
query.setParameter("p", entity.getBirthDate(), TemporalType.DATE);
// better: private LocalDate birthDate; and no @Temporal at all
Defensive patterns

Strategy: type-guard

Validate before calling

if (value instanceof java.sql.Date && !(value instanceof java.sql.Timestamp)) {
    // DATE-precision value: bind only with TemporalType.DATE, never TemporalType.TIME
}

Type guard

static boolean isDatePrecision(Object v) {
    return v instanceof java.util.Date && !(v instanceof java.sql.Time);
}

Try / catch

try {
    query.setParameter("p", value, TemporalType.DATE);
} catch (IllegalArgumentException e) {
    throw new IllegalStateException("Temporal precision mismatch between field and parameter: " + e.getMessage(), e);
}

Prevention

When it happens

Trigger: unwrap(date, java.sql.Time.class, options) is reached when: an entity attribute or query parameter is bound with @Temporal(TemporalType.TIME) while the value is managed by the DATE descriptor (e.g. query.setParameter(name, javaSqlDateValue, TemporalType.TIME)); a java.sql.Date value is bound to a parameter whose JavaType is JdbcTimeJavaType; or a custom UserType/AttributeConverter calls unwrap with java.sql.Date/Time.class on the wrong descriptor.

Common situations: Mixing legacy java.util.Date/java.sql.Date field types with TIME-precision parameters; porting code from Hibernate 5 to 6 where temporal descriptor resolution became stricter; entity fields declared java.util.Date with @Temporal(TemporalType.DATE) but queried with a different TemporalType.

Related errors


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