{"record":{"id":"e1509ed6c0a7a210","repo":"hibernate/hibernate-orm","slug":"illegal-attempt-to-treat-java-sql-date-as-java","errorCode":null,"errorMessage":"Illegal attempt to treat 'java.sql.Date' as 'java.sql.Time'","messagePattern":"Illegal attempt to treat 'java\\.sql\\.Date' as 'java\\.sql\\.Time'","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JdbcDateJavaType.java","lineNumber":169,"sourceCode":"\t\t\treturn type.cast( toDateEpoch( value ) );\n\t\t}\n\n\t\tif ( String.class.isAssignableFrom( type ) ) {\n\t\t\treturn type.cast( toString( value ) );\n\t\t}\n\n\t\tif ( Calendar.class.isAssignableFrom( type ) ) {\n\t\t\tfinal var gregorianCalendar = new GregorianCalendar();\n\t\t\tgregorianCalendar.setTimeInMillis( toDateEpoch( value ) );\n\t\t\treturn type.cast( gregorianCalendar );\n\t\t}\n\n\t\tif ( java.sql.Timestamp.class.isAssignableFrom( type ) ) {\n\t\t\treturn type.cast( new java.sql.Timestamp( toDateEpoch( value ) ) );\n\t\t}\n\n\t\tif ( java.sql.Time.class.isAssignableFrom( type ) ) {\n\t\t\tthrow new IllegalArgumentException( \"Illegal attempt to treat 'java.sql.Date' as 'java.sql.Time'\" );\n\t\t}\n\n\t\tthrow unknownUnwrap( type );\n\t}\n\n\tprivate LocalDate unwrapLocalDate(java.util.Date value) {\n\t\treturn value instanceof java.sql.Date date\n\t\t\t\t? date.toLocalDate()\n\t\t\t\t: new java.sql.Date( toDateEpoch( value ) ).toLocalDate();\n\t}\n\n\t@Override\n\tpublic @Nullable Date wrap(@Nullable Object value, WrapperOptions options) {\n\t\tif ( value == null ) {\n\t\t\treturn null;\n\t\t}\n\t\tfinal var wrapped = wrapOrNull( value );\n\t\tif ( wrapped == null ) {","sourceCodeStart":151,"sourceCodeEnd":187,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JdbcDateJavaType.java#L151-L187","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Make the temporal precision consistent: use the same @Temporal value on the entity field and on every query.setParameter(...) call for that attribute","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","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","Audit custom UserType/AttributeConverter implementations for unwrap() calls that request java.sql.Time.class while handling date values"],"exampleFix":"// before\n@Temporal(TemporalType.DATE)\nprivate java.util.Date birthDate;\n...\nquery.setParameter(\"p\", entity.getBirthDate(), TemporalType.TIME); // IllegalArgumentException\n\n// after\n@Temporal(TemporalType.DATE)\nprivate java.util.Date birthDate;\n...\nquery.setParameter(\"p\", entity.getBirthDate(), TemporalType.DATE);\n// better: private LocalDate birthDate; and no @Temporal at all","handlingStrategy":"type-guard","validationCode":"if (value instanceof java.sql.Date && !(value instanceof java.sql.Timestamp)) {\n    // DATE-precision value: bind only with TemporalType.DATE, never TemporalType.TIME\n}","typeGuard":"static boolean isDatePrecision(Object v) {\n    return v instanceof java.util.Date && !(v instanceof java.sql.Time);\n}","tryCatchPattern":"try {\n    query.setParameter(\"p\", value, TemporalType.DATE);\n} catch (IllegalArgumentException e) {\n    throw new IllegalStateException(\"Temporal precision mismatch between field and parameter: \" + e.getMessage(), e);\n}","preventionTips":["Use java.time types (LocalDate/LocalTime/LocalDateTime) for all new mappings","Keep @Temporal identical on the entity field and every parameter binding","Never bind a java.sql.Date value to a TIME parameter or vice versa"],"tags":["hibernate","type-conversion","java-sql-date","java-sql-time","unwrap","temporal"],"backgroundTag":"temporal-type-mismatch","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}