[hibernate-dev] Enum mapping in hbm.xml
Steve Ebersole
steve at hibernate.org
Sun Jul 19 10:53:52 EDT 2015
This came up in getting the testsuite set up against MySQL. When an enum
is defined in hbm.xml we do not inherently know whether to treat it as
ordinal or named. We try a few different things to make this
determination, but ultimately if we do not have enough information up front
we prefer to defer the decision until later when we have access to some
form of JDBC metadata (generally nullSafeSet/nullSafeGet) to decide.
It seems there was initially a problem in that deference code that led
to HHH-8153[1]. However, the fix done for HHH-8153 is not enough.
Ultimately the problem is that the driver may not have the proper
information to answer java.sql.ParameterMetaData#getParameterType
correctly. HHH-8153 handles the case where the driver flat out does not
support that call and throws an exception. However MySQL illustrates
another case where the driver simply reports VARCHAR as the broadest
acceptable type. In other words it is not looking at the underlying
datatypes in the database. So even if the parameter is matched to the enum
column that we defined as numeric, the MySQL driver is still going to
return ParameterMetaData#getParameterType == VARCHAR.
I cannot say how many drivers implement this correctly. H2 does this as we
expect it. MySQL at least does not.
So the question becomes how we want to support enum types in hbm.xml. For
illustration, this can be seen in the org.hibernate.test.enums.EnumTypeTest
added for HHH-8153. The mapping is
https://github.com/hibernate/hibernate-orm/blob/master/hibernate-core/src/test/resources/org/hibernate/test/enums/Person.hbm.xml.
Neither enum mapping works particularly "correctly", but the test failure
comes from the HairColor mapping. We expect that to be an ordinal, we
create the table with its column being a numeric, but then the driver says
the parameter type is VARCHAR and we try to treat it as named.
For what it is worth, I had quite a long time ago added the parameter
needed to properly control this in hbm.xml. The "proper" way to map these
would have been to use the "useNamed" type parameter:
<property name="gender" not-null="true">
<type name="org.hibernate.type.EnumType">
<param name="enumClass">org.hibernate.test.enums.Gender</param>
<param name="useNamed">false</param>
</type>
</property>
<property name="hairColor" not-null="true">
<type name="org.hibernate.type.EnumType">
<param name="enumClass">org.hibernate.test.enums.HairColor</param>
<param name="useNamed">true</param>
</type>
</property>
The rub is that to cater for legacy mappings, I do not "require" this, like
annotations do. This would be a non-issue if I could, during
configuration, say that an enum is ordinal if "useNamed" is not set or is
set to false and that it is named if useNamed is set to true. What is
missing/different is the not-set case. At them moment that falls back to
the JDBC metadata approach discussed above.
What I propose is that we change this. I am kind of torn as to the default
tbh. I think JPA's default of ORDINAL is the wrong choice. I think NAMED
is the better choice. Well technically I think an independent mapping code
it best. But strictly between ORDINAL/NAMED, I think NAMED is better. So
if everyone agrees that we change this to definitively determine the enum
mapping up front, which style do we choose as the default. Obviously the
big argument for choosing ORDINAL is consistency with annotations.
[1] https://hibernate.atlassian.net/browse/HHH-8153
More information about the hibernate-dev
mailing list