Home > database >  Hibernate mapping: Get collection on runtime with nullSafeGet overrided method
Hibernate mapping: Get collection on runtime with nullSafeGet overrided method

Time:02-11

To make the mapping between hibernate and my database work, I have this mapping :

<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>

The idea is to directly get the Collection in my field class instead of doing the mapping each time.

For example, the field in the class could be a Set, a List, or a Queue. In the database, the value is like "enumValue1,enumValue2,enumValue3".

To do that, my class CommaSeparatedGenericEnumType is like this:

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);
      }
    }
  }
}

I would like to be able to parametrize which collection nullSafeGet and nullSafeSet are going to use, because for now, it's only working with a list.

Thank you!

CodePudding user response:

Maybe my question was asked was oddly, but here what I did:

  • For the getter, I used the factory pattern already implemented by 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;
  }
  • For the setter, I used the reflection 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();
      }
    }
  }
  • Related