我有一个带有状态列表的枚举(例如)
enum State
{
UP,
DOWN,
RETRY
};
我的数据库中的列是枚举类型。当我尝试通过使用
setParameter("keyword", State.RETRY);
设置查询中的参数来执行 Hibernate 查询时,我收到以下错误:
参数值[RETRY]与预期类型不匹配 [包.名称.状态(不适用)]在我的域的 Glassfish 4.1 server.log 中。我正在使用 Hibernate 4.3.6。
在查看 Hibernate 的源代码时,我发现错误是由于
org.hibernate.jpa.spi.BaseQueryImpl
中的 958-960 行
发生的:
private static boolean isValidBindValue(Class expectedType, Object value, TemporalType temporalType) {
if ( expectedType.isInstance( value ) ) {
return true;
}
...
return false;
}
isValidBindValue
返回 false,因此我收到了消息。由于这一行,它打印出
String
等价于
enum
值:
String.format("Parameter value [%s] did not match expected type [%s (%s)]",
bind,
parameterType.getName(),
extractName( temporalType )
)
通过调用代表
bind
的
toString
上的
Object
方法,将
enum State.RETRY
对象隐式转换为 String 值。那么我如何才能让 Hibernate 相信
State.RETRY
是
State
的实例?看起来 Hibernate 更新到了 JPA 2.1 规范,该规范在 2013 年 4 月的提交中更加严格:
https://github.com/hibernate/hibernate-orm/commit/84520cd6e36e9207c41528cf9311cae905a86425
实体注释如下:
@Basic(optional = false)
@Column(name = "state")
@Enumerated(EnumType.String)
private State state;
编辑:
我的
RetryState
枚举由
EarLibClassLoader
加载。而
Query
由 URLClassLoader 加载,而
EntityManager
由不同的类加载器加载。
此答案。
进一步,带有注释
@Enumerated(EnumType.String)
您是说您明确希望将该值保存为
作为字符串在数据库中。如果真正的列类型是某种枚举,我预计这会失败。也许 Hibernate 代码更改试图通过强制您使用 varchar 或 integer 列来防止这些问题。
可能的解决方案:
A)
使用带@Enumerated(EnumType.String)
的 varchar 列或带
@Enumerated
的 int 列
B)
您可以尝试通过注释指定枚举列
@Basic(optional = false)
@Column(name = "state", columnDefinition = "enum('UP','DOWN','RETRY')")
@Enumerated(EnumType.String)
private State state;
C)
您可以尝试通过 hibernate XML 映射文件指定您的枚举类:
<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>
另请参阅:
domain-dir/lib/applib 目录来修复此问题。您必须将这些类放入 domain-dir/lib/applib 目录中的 JAR 中,然后使用 --libraries jar1,jar2,etc
在 asadmin 的部署命令上指定 jar。列出多个 JAR 文件时,请勿在逗号后添加空格。此外,我有一个具有 EJB 远程接口的通用 JAR,因此我必须将 JPA 类分解为一个新 JAR,并将它们也放在我的
applibs 目录中。然后,我将带有 EJB 远程接口的 JAR 放入我的 EAR\lib 目录中。
lib/applibs目录中的JAR由URLClassLoader加载。 EAR\lib 中的 JAR 由 EARLibClassLoader 加载。
我在构建查询条件之前更新了逻辑,以便将我搜索的字符串转换为其相对枚举值。
我可以访问列的类路径,并使用反射来转换值,然后再将其放入 criteriaBuilder。
try {
Class<?> enumClass = Class.forName(classPath);
Method valueOfMethod = enumClass.getMethod("valueOf", String.class);
return valueOfMethod.invoke(null, value);
} catch (
ClassNotFoundException | NoSuchMethodException | InvocationTargetException | IllegalAccessException e
) {
throw new IllegalArgumentException(
"Enumerated column does not currently support search: " + classPath
);
}
最后
cond = cb.and(cond, cb.equal(equalPath, enumValueFromAbove));