I have an enum with a list of states (e.g.)
enum State
{
UP,
DOWN,
RETRY
};
The column in my database is of type enum. When I try to execute a Hibernate query by setting the parameter in the query using setParameter("keyword", State.RETRY);
, I receive the error of
Parameter value [RETRY] did not match expected type [package.name.State (n/a)]
In my Glassfish 4.1 server.log for my domain. I am using Hibernate 4.3.6.
In looking at the source code for Hibernate, I see the error occurs because of lines 958-960 in org.hibernate.jpa.spi.BaseQueryImpl
:
private static boolean isValidBindValue(Class expectedType, Object value, TemporalType temporalType) {
if ( expectedType.isInstance( value ) ) {
return true;
}
...
return false;
}
isValidBindValue
returns false and thus I get the message.
It prints out the String
equivalent of the enum
value because of this line:
String.format("Parameter value [%s] did not match expected type [%s (%s)]",
bind,
parameterType.getName(),
extractName( temporalType )
)
The bind
object is implicitly converted to the String value by calling the toString
method on the Object
which represents the enum State.RETRY
.
So how I can I convince Hibernate that State.RETRY
is an instance of State
?
It looks like Hibernate updated to the JPA 2.1 spec which is more strict in this commit from April 2013:
https://github.com/hibernate/hibernate-orm/commit/84520cd6e36e9207c41528cf9311cae905a86425
The entity is annotated as follows:
@Basic(optional = false)
@Column(name = "state")
@Enumerated(EnumType.String)
private State state;
Edit:
My RetryState
enum is loaded by the EarLibClassLoader
. Whereas Query
is loaded by the URLClassLoader and the EntityManager
is loaded by a different class loader.
I guess the main problem is that you are trying to use an enum datatype on the database side. This is not recommended because it often requires an proprietary enum type which may not be well supported by the JPA implementation (e.g. Hibernate). See this answer on a similar question for some details.
Further, with the annotation
@Enumerated(EnumType.String)
you are saying that you explicitly want, that the value is saved as a String in the database. I would expect that this fails if the real column type is some enum. Maybe the Hibernate code changes are trying to prevent these problems by forcing you to use either varchar or integer columns.
Possible solutions:
A)
Use a varchar column with @Enumerated(EnumType.String)
or an int column with @Enumerated
B)
You can try to specify the enum column via the annotation
@Basic(optional = false)
@Column(name = "state", columnDefinition = "enum('UP','DOWN','RETRY')")
@Enumerated(EnumType.String)
private State state;
C)
You can try to specify your enum class via an hibernate XML mapping file:
<property name="type" column="type" not-null="true">
<type name="org.hibernate.type.EnumType">
<param name="enumClass">package.name.State</param>
<param name="type">12</param>
<!-- 12 is java.sql.Types.VARCHAR -->
</type>
</property>
See also: