Hibernate 映射:使用 nullSafeGet 覆盖方法在运行时获取集合

Hibernate mapping: Get collection on runtime with nullSafeGet overrided method

为了使休眠和我的数据库之间的映射工作,我有这个映射:

<property name="userRolesV2" column="user_roles_v2">
  <type name="io.markethero.repository.CommaSeparatedGenericEnumType">
    <param name="enumClassName">io.markethero.model.UserLoginRoleV2</param>
    <param name="collectionClassName">java.util.Set</param>
  </type>
</property>

思路是直接获取我领域的Collectionclass而不是每次都做映射

例如,class中的字段可以是集合、列表或队列。 在数据库中,该值类似于“enumValue1,enumValue2,enumValue3”。

为此,我的 class CommaSeparatedGenericEnumType 是这样的:

public class CommaSeparatedGenericEnumType implements UserType, ParameterizedType {

  private Class enumClass = null;
  private Class targetCollection = null;

  public void setParameterValues(Properties params) {
    String enumClassName = params.getProperty("enumClassName");
    String collectionClassName = params.getProperty("collectionClassName");
    if (enumClassName == null) {
      throw new MappingException("enumClassName parameter not specified");
    }
    if (collectionClassName == null) {
      throw new MappingException("collectionClassName parameter not specified");
    }

    try {
      this.enumClass = Class.forName(enumClassName);
    } catch (ClassNotFoundException e) {
      throw new MappingException("enumClass " + enumClassName + " not found", e);
    }
    try {
      this.targetCollection = Class.forName(collectionClassName);
    } catch (ClassNotFoundException e) {
      throw new MappingException("targetCollection " + collectionClassName + " not found", e);
    }
  }
  
  @Override
  public Object nullSafeGet(ResultSet rs, String[] names, SharedSessionContractImplementor session, Object owner) throws HibernateException, SQLException {
    String commaSeparatedValues = rs.getString(names[0]);
    List<Object> result = new ArrayList<>();
    if (!rs.wasNull()) {
      String[] enums = commaSeparatedValues.split(",");
      for (String string : enums) {
        result.add(Enum.valueOf(enumClass, string));
      }
    }
    return result;
  }

  @Override
  @SuppressWarnings("unchecked")
  public void nullSafeSet(PreparedStatement st, Object value, int index, SharedSessionContractImplementor session) throws HibernateException, SQLException {
    if (null == value) {
      st.setNull(index, Types.VARCHAR);
    } else {
      List<Object> enums = (List) value;

      StringBuilder sb = new StringBuilder("");
      for (Object each : enums) {
        sb.append(each.toString()).append(",");
      }

      if (sb.toString().isEmpty()) {
        st.setNull(index, Types.VARCHAR);
      } else {
        String commaSeparatedIds = sb.toString().substring(0, sb.toString().length() - 1);
        st.setString(index, commaSeparatedIds);
      }
    }
  }
}

我希望能够参数化 nullSafeGet 和 nullSafeSet 将要使用的集合,因为目前,它只能使用列表。

谢谢!

也许有人问我的问题很奇怪,但我是这样做的:

  • 对于 getter,我使用了 Spring 已经实现的工厂模式:CollectionFactory.
  @Override
  public Object nullSafeGet(ResultSet rs, String[] names, SharedSessionContractImplementor session, Object owner) throws HibernateException, SQLException {
    String commaSeparatedValues = rs.getString(names[0]);
    Collection<Object> result = CollectionFactory.createCollection(this.targetCollection, 50);
    if (!rs.wasNull()) {
      String[] enums = commaSeparatedValues.split(",");
      for (String string : enums) {
        try {
          result.add(Enum.valueOf(enumClass, string));
        } catch (IllegalArgumentException e) {
          throw new MappingException("[CommaSeparatedGenericEnumType::nullSafeGet] No such enum value"+ string +"for enum : " + enumClass, e);
        }
      }
    }
    return result;
  }
  • 对于setter,我使用了反射API。
  public void nullSafeSet(PreparedStatement st, Object value, int index, SharedSessionContractImplementor session) throws HibernateException, SQLException {
    if (value == null) {
      st.setNull(index, Types.VARCHAR);
    } else {
      if (value instanceof Collection) {
        try {
          StringBuilder sb = new StringBuilder("");

          Constructor<?> c = value.getClass().getConstructor(Collection.class);

          Collection<Object> enums = (Collection<Object>) c.newInstance((Collection<Object>) value);

          for (Object each : enums) {
            sb.append(each.toString()).append(",");
          }
          String commaSeparatedIds = sb.substring(0, sb.toString().length() - 1);

          if (sb.length() > 0) {
            st.setString(index, commaSeparatedIds);
          } else {
            st.setNull(index, Types.VARCHAR);
          }
        } catch (NoSuchMethodException e) {
          throw new MappingException("[CommaSeparatedGenericEnumType::nullSafeSet] No such constructor found for class : " + value.getClass(), e);
        } catch (InstantiationException e) {
          throw new MappingException("[CommaSeparatedGenericEnumType::nullSafeSet] Class : " + value.getClass() + " cannot be instantiate ", e);
        } catch (IllegalAccessException e) {
          throw new MappingException("[CommaSeparatedGenericEnumType::nullSafeSet] You cannot access to constructor of class : " + value.getClass(), e);
        } catch (InvocationTargetException e) {
          throw new MappingException("[CommaSeparatedGenericEnumType::nullSafeSet] InvocationTargetException for class : " + value.getClass(), e);
        }
      } else {
        st.setNull(index, Types.VARCHAR);
        throw new IllegalArgumentException();
      }
    }
  }