{"record":{"id":"e24ca233f88fba3e","repo":"hibernate/hibernate-orm","slug":"could-not-determine-recommended-jdbctype-for-java","errorCode":null,"errorMessage":"Could not determine recommended JdbcType for Java type '\" + getTypeName() + \"'\"","messagePattern":"Could not determine recommended JdbcType for Java type '\" \\+ getTypeName\\(\\) \\+ \"'\"","errorType":"exception","errorClass":"JdbcTypeRecommendationException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/type/descriptor/java/spi/FormatMapperBasedJavaType.java","lineNumber":44,"sourceCode":" */\n@Incubating\npublic abstract class FormatMapperBasedJavaType<T> extends AbstractJavaType<T> implements MutabilityPlan<T> {\n\n\tprivate final TypeConfiguration typeConfiguration;\n\n\tpublic FormatMapperBasedJavaType(\n\t\t\tType type,\n\t\t\tMutabilityPlan<T> mutabilityPlan,\n\t\t\tTypeConfiguration typeConfiguration) {\n\t\tsuper( type, mutabilityPlan );\n\t\tthis.typeConfiguration = typeConfiguration;\n\t}\n\n\tprotected abstract FormatMapper getFormatMapper(TypeConfiguration typeConfiguration);\n\n\t@Override\n\tpublic JdbcType getRecommendedJdbcType(JdbcTypeIndicators context) {\n\t\tthrow new JdbcTypeRecommendationException(\n\t\t\t\t\"Could not determine recommended JdbcType for Java type '\" + getTypeName() + \"'\"\n\t\t);\n\t}\n\n\tprivate WrapperOptions getWrapperOptions() {\n\t\treturn typeConfiguration.getWrapperOptions();\n\t}\n\n\t@Override\n\tpublic String toString(T value) {\n\t\treturn getFormatMapper( typeConfiguration )\n\t\t\t\t.toString( value, this, getWrapperOptions() );\n\t}\n\n\t@Override\n\tpublic T fromString(CharSequence string) {\n\t\treturn getFormatMapper( typeConfiguration )\n\t\t\t\t.fromString( string, this, getWrapperOptions() );","sourceCodeStart":26,"sourceCodeEnd":62,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/spi/FormatMapperBasedJavaType.java#L26-L62","documentation":"FormatMapperBasedJavaType backs types Hibernate serializes through a FormatMapper (Jackson/Gson JSON), e.g. attributes mapped @JdbcTypeCode(SqlTypes.JSON). It deliberately has no default JDBC type — the storage format must be chosen per attribute. If mapping needs a recommended JdbcType (no explicit @JdbcType/@JdbcTypeCode given), bootstrap fails with JdbcTypeRecommendationException naming the Java type.","triggerScenarios":"An attribute whose JavaType extends FormatMapperBasedJavaType (custom JSON types, hibernate-types-style registrations) without any @JdbcTypeCode/@JdbcType annotation; schema validation/export requesting the default SQL type of such an attribute; registering only the JavaType half of a custom JSON mapping.","commonSituations":"Migrating Hibernate 5 UserType JSON mappings to 6.x; using @JavaType on a field without the matching JDBC annotation; third-party type contributions that assume a default JSON column type exists.","solutions":["Annotate the attribute explicitly: @JdbcTypeCode(SqlTypes.JSON) (or SqlTypes.SQLXML)","Alternatively specify @JdbcType(yourJsonJdbcType.class) directly","Override getRecommendedJdbcType() in the custom JavaType to return the intended descriptor instead of throwing","Register a complete BasicType (JavaType+JdbcType pair) via @TypeRegistration so no recommendation is ever requested"],"exampleFix":"// before\n@Entity class Event {\n    @JavaType(MyPayloadJavaType.class) // FormatMapperBasedJavaType subclass\n    Payload payload; // no JDBC annotation -> JdbcTypeRecommendationException at boot\n}\n\n// after\n@Entity class Event {\n    @JavaType(MyPayloadJavaType.class)\n    @JdbcTypeCode(SqlTypes.JSON)\n    Payload payload;\n}","handlingStrategy":"validation","validationCode":"static List<String> javaTypeFieldsMissingJdbcAnnotation(Class<?> entity) {\n    var bad = new ArrayList<String>();\n    for (var f : entity.getDeclaredFields()) {\n        boolean hasJavaType = f.isAnnotationPresent(org.hibernate.annotations.JavaType.class);\n        boolean hasJdbc = f.isAnnotationPresent(org.hibernate.annotations.JdbcType.class)\n            || f.isAnnotationPresent(org.hibernate.annotations.JdbcTypeCode.class)\n            || f.isAnnotationPresent(jakarta.persistence.Convert.class);\n        if (hasJavaType && !hasJdbc) bad.add(entity.getSimpleName() + \".\" + f.getName());\n    }\n    return bad; // assert empty before boot\n}","typeGuard":null,"tryCatchPattern":"try {\n    sessionFactory = metadata.buildSessionFactory();\n} catch (org.hibernate.type.descriptor.java.spi.JdbcTypeRecommendationException e) {\n    // message names the Java type: add @JdbcTypeCode(SqlTypes.JSON) to that attribute, rebuild\n}","preventionTips":["Always pair a custom JavaType with an explicit @JdbcType/@JdbcTypeCode","Prefer @TypeRegistration of a complete BasicType over bare JavaType registration","Run SessionFactory bootstrap in CI to catch missing JDBC-side annotations"],"tags":["hibernate","orm","json","jdbc-type","custom-type","bootstrap"],"backgroundTag":"jdbc-type-resolution-failure","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}