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();
}
}
}
为了使休眠和我的数据库之间的映射工作,我有这个映射:
<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();
}
}
}