【问题标题】:Hibernate SQL transformation fails for Enum field type枚举字段类型的休眠 SQL 转换失败
【发布时间】:2012-03-26 20:40:27
【问题描述】:

我正在使用 SQL 查询,然后使用 Hibernates 的 Transformers.aliasToBean() 转换结果。 我的查询中的一列是枚举。枚举的转换以某种方式失败。我该怎么办?我应该使用哪种数据类型?我想要超过 1 个字符将结果转换为我的枚举类型。

这就是我的查询/代码的简化版本的样子(b 是表配置文件中的枚举):

session.createSQLQuery("select a, b from profiles").setResultTransformer(Transformers.aliasToBean(Profile.class))
                    .list();

例外:expected type: Foo.ProfileStateEnum, actual value: java.lang.Character

【问题讨论】:

  • 你想只使用 Transformers.aliasToBean 还是 HQL 就可以了?
  • 我正在编写原生 SQL 查询,因此我猜 HQL 还不够。

标签: sql hibernate enums hql transformation


【解决方案1】:

假设对应于 b 列的 java 枚举类型是 Foo.ProfileStateEnum,下面的代码 sn-p 应该适合你。 (我用 Hibernate 4.1.6 测试过)

import java.util.Properties;
import org.hibernate.type.Type;
import org.hibernate.type.IntegerType;
import org.hibernate.internal.TypeLocatorImpl.TypeLocatorImpl;
import org.hibernate.type.TypeResolver.TypeResolver;
import org.hibernate.type.EnumType;

Properties params = new Properties();
params.put("enumClass", "Foo.ProfileStateEnum");
params.put("type", "12"); /*type 12 instructs to use the String representation of enum value*/
/*If you are using Hibernate 5.x then try:
params.put("useNamed", true);*/
Type myEnumType = new TypeLocatorImpl(new TypeResolver()).custom(EnumType.class, params);

List<Profile> profileList= getSession().createSQLQuery("select a as ID, b from profiles")
            .addScalar("ID", IntegerType.INSTANCE)
            .addScalar("b", myEnumType )
            .setResultTransformer(Transformers.aliasToBean(Profile.class))
            .list();

【讨论】:

  • 这看起来很有趣。我会试试看。谢谢。
  • 在休眠 5.x 中不工作。我正在为 hibernate 5.x 寻找一个快速的解决方案。
  • 对于像我这样心不在焉的人:.custom(EnumType.class, params); 部分必须按原样输入,因为它是 org.hibernate.type.EnumType,而不是您自定义的 com.company.MyEnumClass
  • 使用常量代替字符串是个好主意:EnumType.ENUM代替"enumClass"EnumType.NAMED代替"useNamed"
  • @IliaNedoluzhko 只使用 Hibernate 5。魔术常数 12 仅适用于 Hibernate 4。
【解决方案2】:

我找到了两种方法来实现它。

  1. 使用org.hibernate.type.CustomTypeorg.hibernate.type.EnumType(输入EnumType.NAMEDEnumType.TYPE,参见EnumType#interpretParameters)。如下:

    Properties parameters = new Properties();
    parameters.put(EnumType.ENUM, MyEnum.class.getName());
    // boolean or string type of true/false; declare database type
    parameters.put(EnumType.NAMED, true);
    // string only; declare database type
    parameters.put(EnumType.TYPE, String.valueOf(Types.VARCHAR));
    EnumType<MyEnum> enumType = new EnumType<>();
    enumType.setTypeConfiguration(new TypeConfiguration());
    enumType.setParameterValues(parameters);
    CustomType customEnumType = new CustomType(enumType);
    
  2. 另一种简单的方法。将org.hibernate.type.StandardBasicTypeTemplateorg.hibernate.type.descriptor.sql.*TypeDescriptor 一起使用。如下:

    StandardBasicTypeTemplate<MyEnum> enumType =
            new StandardBasicTypeTemplate<>(VarcharTypeDescriptor.INSTANCE,
                    new EnumJavaTypeDescriptor<>(MyEnum.class));
    

【讨论】:

  • 选项 2 似乎是最简单的,并且对我有用。
  • 选项 1 适用于 Hibernate 5.4.32.Final。
【解决方案3】:

让我们看看为什么会出现此异常。

从问题很明显,您在模型类中为字段 'b' 使用了 @Enumerated(EnumType.STRING) 注释。所以该字段是一个模型类的枚举和一个数据库的varchar。本机 SQL 不关心您的模型类,并按原样返回数据库表中的任何内容。因此,在您的情况下,您使用的 SQLQuery 将为 'b' 返回 String 而不是 ProfileStateEnum 类型。但是 Profile 类中 'b' 的 setter 方法采用 ProfileStateEnum 类型参数。

因此你得到异常“预期类型:Foo.ProfileStateEnum,实际值:java.lang.Character”

您可以使用 Aliasing 来解决问题。

我的建议是,使用您想要的任何名称为您的列命名,并在您的模型/dto 中为该别名创建一个 setter 方法。

例如,让您的列别名为'enumStr'。

然后您的查询将如下所示:“select a, b as enumStr from profiles

现在,在 Profile 类中为该别名创建一个 setter 方法。

(假设枚举ProfileStateEnum可以有STATE1STATE2这两个值中的任何一个)

public void setEnumStr(String str){
    /*Convert the string to enum and set the field 'b'*/
    if(str.equals(ProfileStateEnum.STATE1.toString())){
        b = ProfileStateEnum.STATE1;
    } else {
        b = ProfileStateEnum.STATE2;
    }
}

现在在转换时,将调用别名 setEnumStr(String) 的 setter,而不是字段 setB(ProfileStateEnum) 的 setter,字符串将被转换并保存为您想要的类型,没有任何异常。

我是 Hibernate 的初学者,该解决方案对我有用。我正在使用 PostgreSQL。但我相信它也适用于其他数据库。

【讨论】:

    猜你喜欢
    • 2012-01-16
    • 2012-06-01
    • 1970-01-01
    • 2022-08-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-11-16
    • 1970-01-01
    相关资源
    最近更新 更多