After updating to Hibernate 6.2.4 I started seeing ArrayIndexOutOfBounds exceptions when Hibernate tries to map from table to entity.
java.lang.ArrayIndexOutOfBoundsException: Index 48 out of bounds for length 6
at org.hibernate.type.descriptor.java.EnumJavaType.fromInteger(EnumJavaType.java:194)
62 lines skipped for [org.springframework, org.hibernate]
I eventually figured out that it was because Hibernate had started interpreting the values from the DB differently. For example, a “0” stored in a VARCHAR column would be treated as 48 (char/ASCII value), and that number would then be used as the ordinal value when converting it to an Enum.
I was able to fix the issue by changing the table column type to INT, but this does still seem like a bug anyway.
Unfortunately, I’ve never been able to gain access to the Hibernate Issue tracker for some reason so I’m posting about it here instead.
Yes, but I don’t see anything relevant to my issue in them. There are some changes relating to TINYINT/SMALLINT datatypes for enums, but that’s not what I’m dealing with.
My issue is that 5.x treats a VARCHAR “0” as 0, but 6.x treats a VARCHAR “0” as 48.
I had the same problem (on Hibernate 6.2.4 and MySQL), as @Lars_Benedetto is saying Hibernate now reads a VARCHAR column database column mapping an enum field as if it was an integer. But it does it by trying to convert the byte value of "0" to an int, instead of doing something like Integer.parseInt(...)
In my case the issue was that someone (it was me…) forgot to add @Enumerated(EnumType.STRING) and Hibernate defaulted (as it should) to an ordinal mapping.
On 5.x it was fine storing the ordinal as a VARCHAR but now that breaks in 6.x
I’m not sure if that scenario is supported or maybe it was not supposed to work in 5.x
On MySQL, enums are now stored using the ENUM datatype by default
This is the relevant part. On MySQL the native enum type is used now if the configuration for a mapping resolves to EnumType.STRING, so you need to migrate your schema. I think it should be possible to fallback to the old representation by annotating @JdbcTypeCode(SqlTypes.VARCHAR)
It’s just a few alter table statements I guess so not sure why you think it’s a big deal to migrate. Either way, you can also just annotate @JdbcTypeCode(SqlTypes.VARCHAR) on the enum attributes if you really want the columns to stay of type varchar.
It here a was to avoid creating ENUM column in MySQL and stick to VARCHAR as it was done before?
Tried with @Column(columnDefinition = "VARCHAR(16)"), but it won’t work while @Enumerated(EnumType.STRING) is present.
Tried to remove @Enumerated(EnumType.STRING) and that rasied exception at runtime.
I don’t want to use INT, TINYINT, BIGINT, any of int variants because I want it to be independent of enum items order in Java and to be able to add new enum element without writing database update code.
public enum Status {
NEW, PROCESSING, INCOMPLETE, REJECTED, DONE;
}
@Enumerated(EnumType.STRING)
private Status status;
It’s just a few alter table statements I guess so not sure why you think it’s a big deal to migrate.
Well, I tried to migrate but these tables are extremely large and mission critical. Altering them causes table locks making the service unavailable for several hours.
I’ve also tried JdbcTypeCode(SqlTypes.VARCHAR) but it is not working for me. I get
Caused by: org.hibernate.HibernateException: Unknown wrap conversion requested: java.lang.Integer to java.lang.String : `org.hibernate.type.descriptor.java.StringJavaType` (java.lang.String)
at org.hibernate.type.descriptor.java.JavaTypeHelper.unknownWrap(JavaTypeHelper.java:24)
at org.hibernate.type.descriptor.java.AbstractClassJavaType.unknownWrap(AbstractClassJavaType.java:116)
at org.hibernate.type.descriptor.java.StringJavaType.wrap(StringJavaType.java:103)
at org.hibernate.type.descriptor.java.StringJavaType.wrap(StringJavaType.java:27)
at org.hibernate.type.descriptor.converter.internal.OrdinalEnumValueConverter.toRelationalValue(OrdinalEnumValueConverter.java:45)
at org.hibernate.type.descriptor.converter.internal.OrdinalEnumValueConverter.toRelationalValue(OrdinalEnumValueConverter.java:23)
So what can I do to restore the old behavior where “0” is interpreted as 0 instead of 48?
I’d prefer not to have to map the column as a regular String and then do all the conversions manually.
This model makes no sense. Why do you store enum ordinals into a varchar column? If the JDBC driver supports reading/writing integers into varchar columns, then I guess your mapping should work, but this is definitely not something that ORM supports.