Hibernate getter and setter for enum value
I have encountered a bug that user report they can't get the value, where returned the error message: "message": "Unknown name value for enum class XXX: 1
I checked the project and find no where so I guess this error is thrown by framework.
So:
-
jakartaee/persistence -> an interface sets
-
Hibernate-core -> search enumerated
-
Got
EnumeratedValueResolution.java
-
Continue explore got
InferredBasicValueResolver.java
-
Find a method called
ordinalJdbcType
private static JdbcType ordinalJdbcType( JdbcType explicitJdbcType, EnumJavaType<?> enumJavaType, MetadataBuildingContext context) { return explicitJdbcType != null ? explicitJdbcType : context.getMetadataCollector().getTypeConfiguration().getJdbcTypeRegistry().getDescriptor( enumJavaType.hasManyValues() ? SMALLINT : TINYINT ); }
-
Here from code we could see it has
JdbcType
andJavaType
so there should be some convertor/resolver to do the mapping. -
Go to a specific type
SMALLINT
,(find in previous code) and explore, check the file comments and doc, find* A type code is often used as a key to obtain a * {@link org.hibernate.type.descriptor.jdbc.JdbcType}, by implementors of * {@link org.hibernate.type.descriptor.java.JavaType#getRecommendedJdbcType}
-
Go check
getRecommendedJdbcType
, the implementation for enum value isEnumJavaType.class
-
Then we find the usage to the
EnumType.java
, check some variables and functions.
I also checked the database, the value was saved as varchar
, while in model the filed is an enum type.
So I guess the reason is that:
- When saving to database, the hibernate checking the annotation, finding it's null, so default is
Enumerated.Ordinal
, saving the ordinal value to the database. - While user is querying the value, hibernate finds database metadata, finding its db type is
varchar
, its java type is enum value, so the framework will take this db value as the name of the enum type and query, where1
is not a valid name, though. So here is problem.
-
I search the
varchar
in theEnumType.java
and findgetConverterForType
,private EnumValueConverter<T,?> getConverterForType( EnumJavaType<T> enumJavaType, LocalJdbcTypeIndicators localIndicators, int type) { if ( isNumericType(type) ) { final JavaType<? extends Number> relationalJavaType = resolveRelationalJavaType( localIndicators, enumJavaType ); return new OrdinalEnumValueConverter<>( enumJavaType, relationalJavaType.getRecommendedJdbcType( localIndicators ), relationalJavaType ); } else if ( isCharacterType(type) ) { return new NamedEnumValueConverter<>( enumJavaType, getStringType().getRecommendedJdbcType( localIndicators ), getStringType() ); } else { throw new HibernateException( String.format( "Passed JDBC type code [%s] not recognized as numeric nor character", type ) ); } }
Lin 14, continue dig we got: NamedEnumValueConverter
, and the doc for the class:
/**
* BasicValueConverter handling the conversion of an enum based on
* JPA {@link jakarta.persistence.EnumType#STRING} strategy (storing the name)
*
* @author Steve Ebersole
*/
It will treat the string value as name.
Based on this, we did the code fix and data fix. Enjoy.