I should have mentioned... There is also this "type" parameter that
EnumType accepts. I'd also look at that in terms of deciding defaults. It
accepts JDBC type codes. EnumType already knows which type codes
constitute ordinal types and named types. So in psuedo code what I am
proposing is:
if ( useNamed != null ) {
if ( useNamed == true ) {
treatAsNamed();
}
else {
treatAsOrdinal();
}
}
else {
if ( type != null ) {
if ( isOrdinal(type) ) {
treatAsOrdinal();
}
else if ( isNamed(type) ) {
treatAsNamed();
}
}
else {
// todo : need to decide fallback...
treatAsNamed();
}
}
throw new SomePoignantException("blah blah blah");
The other thing to notice is that there is no more "waiting" in this
proposal. No more deferring until the first nullSafeSet/nullSafeGet call.
On Sun, Jul 19, 2015 at 9:53 AM Steve Ebersole <steve(a)hibernate.org> wrote:
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....
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