【问题标题】:Mapping enum to a table with hibernate annotation使用休眠注释将枚举映射到表
【发布时间】:2010-10-18 15:44:01
【问题描述】:

我有一个表 DEAL 和一个表 DEAL_TYPE。我想映射这段代码:

public class Deal {
   DealType type;
}

public enum DealType {
   BASE("Base"), EXTRA("Extra");
}

问题是数据已经存在于数据库中。而且我很难将类映射到数据库。

数据库看起来像这样:

   TABLE DEAL {
      Long id;
      Long typeId;
   }

   TABLE DEAL_TYPE {
       Long id;
       String text;
   }

我知道我可以在交易类型之间使用简单的@OneToMany 关系,但我更喜欢使用枚举。这可能吗?

我几乎可以通过使用 EnumType.ORDINAL 类型来实现它。但不幸的是,我的交易类型表中的 ID 不是连续的,并且不是从 1 开始的。

有什么建议吗?

【问题讨论】:

    标签: java hibernate enums orm


    【解决方案1】:

    Hibernate 在 Enums 中有点糟糕。这是一个非常好的 ORM 的奇怪失败。解决它的“最简单”的方法是将您的 Enum 声明为自定义休眠类型。幸运的是,Hibernate 编写了一个示例实现,您可以将它逐字写入您的应用程序中:

    http://www.hibernate.org/265.html

    它们甚至包括如何使用它的说明。这是我在最终需要持久化枚举时使用的模式。

    【讨论】:

    • 是的,我一直不明白为什么他们没有更好的解决方案。
    • 但是枚举的id列和text列是在哪里定义的呢?
    • 我不明白您如何将 Enum 映射到带有 id 和描述的单独表。你能给出一个代码示例如何在 Hibernate 注释中做到这一点吗?这是@Naor 所要求的。
    • 这个答案已经过时了,我们现在可以使用docs.oracle.com/javaee/7/api/javax/persistence/Enumerated.html
    【解决方案2】:

    如果您想以只读方式使用实体,则可以使用@Formula@Enumerated。尝试类似:

    @Entity
    public class Deal {
       @Formula("(select text from DEAL_TYPE dt where dt.id = typeId)")
       @Enumerated(EnumType.STRING)
       DealType type;
    }
    

    【讨论】:

      【解决方案3】:

      您可以使用@Entity 注释枚举并使用自定义tuplizer 创建带有Enum.valueOf 的枚举实例

      枚举声明如下所示:

      @Entity
      @Table(name = "node_interface_type")
      @Tuplizer(impl = EnumTuplizer.class)
      public enum Type {
          WIRED, WIRELESS, WIRELESS_SENSOR_NODE;
          @Id
          public String name = toString();
      }
      

      Tuplizer 是:

      public class EnumTuplizer extends PojoEntityTuplizer {
          public EnumTuplizer(EntityMetamodel entityMetamodel, PersistentClass mappedEntity) {
              super(entityMetamodel, mappedEntity);
          }
      
          @Override
          protected Instantiator buildInstantiator(final PersistentClass persistentClass) {
              return new Instantiator() {
                  @Override
                  public Object instantiate(Serializable id) {
                      try {
                          return Enum.valueOf(
                                  (Class) persistentClass.getClass().getClassLoader().loadClass(persistentClass.getClassName()),
                                  (String) id
                          );
                      } catch (ClassNotFoundException e) {
                          throw new AssertionError(e);
                      }
                  }
      
                  @Override
                  public Object instantiate() {
                      throw new UnsupportedOperationException();
                  }
      
                  @Override
                  public boolean isInstance(Object object) {
                      throw new UnsupportedOperationException();
                  }
              };
          }
      }
      

      【讨论】:

      • 我认为您现在可以使用 @Enumerated 摆脱 Tuplizer
      • 你是救生员。有了这个,我可以将休眠对象实例化为具有自定义值的枚举并在存储库中使用它们
      【解决方案4】:

      我创建了一个类似 hibernate 建议的类,只是它是可配置的,不需要仅为这种持久性创建新类型。

      可以像这样使用

      @Type(type = "ro.raisercostin.hibernate.EnumUserType", parameters = @Parameter(name = "type", value = "DealType"))
      DealType dealType;
      

      我添加了 ParameterizedType 的实现来支持传递的参数。

      public class EnumUserType implements UserType, ParameterizedType {
      
          private static final int[] SQL_TYPES = { Types.VARCHAR };
          private Class clazz = null;
      
          public EnumUserType() {
          }
      
          @Override
          public void setParameterValues(Properties parameters) {
              String className = (String) parameters.get("type");
              try {
                  this.clazz = Class.forName(className);
              } catch (ClassNotFoundException e) {
                  throw new RuntimeException("Couldn't get the class for name [" + className + "].", e);
              }
          }
      
          public int[] sqlTypes() {
              return SQL_TYPES;
          }
      
          public Class returnedClass() {
              return clazz;
          }
      
          public Object nullSafeGet(ResultSet resultSet, String[] names, Object owner) throws HibernateException,
                  SQLException {
              String name = resultSet.getString(names[0]);
              Object result = null;
              if (!resultSet.wasNull()) {
                  result = Enum.valueOf(clazz, name);
              }
              return result;
          }
      
          public void nullSafeSet(PreparedStatement preparedStatement, Object value, int index) throws HibernateException,
                  SQLException {
              if (null == value) {
                  preparedStatement.setNull(index, Types.VARCHAR);
              } else {
                  preparedStatement.setString(index, ((Enum) value).name());
              }
          }
      
          public Object deepCopy(Object value) throws HibernateException {
              return value;
          }
      
          public boolean isMutable() {
              return false;
          }
      
          public Object assemble(Serializable cached, Object owner) throws HibernateException {
              return cached;
          }
      
          public Serializable disassemble(Object value) throws HibernateException {
              return (Serializable) value;
          }
      
          public Object replace(Object original, Object target, Object owner) throws HibernateException {
              return original;
          }
      
          public int hashCode(Object x) throws HibernateException {
              return x.hashCode();
          }
      
          public boolean equals(Object x, Object y) throws HibernateException {
              if (x == y) {
                  return true;
              }
              if ((null == x) || (null == y)) {
                  return false;
              }
              return x.equals(y);
          }
      }
      

      【讨论】:

      • Hibernate 似乎在两种方法上扩展了签名,但除此之外这对我来说很好。是否考虑过将其贡献给 Hibernate 项目?
      • @raisercostin,我正在尝试您在上面解释的内容。我不确定注释中的参数值是什么。您能否详细说明一下或指出我可以获取更多信息的来源。
      • 现在我明白了。参数类似于 type:DealType 其中 type 是使用的 EnumUserType 的键,value 是类(使用完整的类名,包括包)。
      【解决方案5】:

      虽然远非理想,但我对这个问题的解决方案是使用 EnumStringType 和非规范化的可更新视图。

      【讨论】:

      • 不错的解决方案,因为您也考虑过插入和更新。这里的一些其他解决方案可能只是为了阅读。虽然这仅适用于少数数据库,例如 Oracle。
      • 虽然我在 MSSQL 上实现了这一点,可更新视图非常简单,但应该可以通过在视图上放置“代替”触发器来修改底层表,从而在大多数 RDBMS 系统上进行模拟。跨度>
      猜你喜欢
      • 1970-01-01
      • 2010-12-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-03-27
      • 2013-04-05
      • 2019-06-10
      • 2010-10-06
      相关资源
      最近更新 更多