我已经用Hibernate在Java中搜索了State模式的实现,并发现了一些使用Enums的解决方案的引用,以便提供一种灵活的方式来添加新状态。
我喜欢这里的解决方案,其中反射用于根据保存ConcreteState类名称的表字段“state”的值创建状态对象:http://nerdboys.com/2007/06/08/state-pattern-persistence-with-hibernate/
但不建议使用此解决方案,因为在DB中保存类型为com.myCompany.myProject.asdasd.ConcreteState的String值会浪费空间,与保持整数值相反。所以我想知道是否有办法在表格中保存可能的状态:
customer_states(PK id INT,className VARCHAR)
并修改我的customers表以使FK具有状态,例如:
客户(PK id INT,名称VARCHAR,FK state INT)
所以我不会使用比所需更多的磁盘空间,并且我会保持客户状态的一致性,因此很容易为这种情况添加新的状态......但是,您将如何在UserType中实现这一点? ??
谢谢!
答案 0 :(得分:0)
hibernate映射可以是:
<property name="_Status">
<column name="STATUS" sql-type="NUMBER" not-null="true"/>
<type name="GenericEnumUserType">
<param name="enumClass">Status</param>
<param name="identifierMethod">getCode</param>
<param name="valueOfMethod">fromString</param>
</type>
</property>
状态枚举
public static enum Status {
ACTIVE(1, "Active"),
DELETED(2, "Deleted"),
INACTIVE(3, "Inactive"),
PASSWORD_EXPIRED(4, "Password Expired");
/** Formal representation (single character code). */
private int code;
/** Textual, human-readable description. */
private String description;
// Needed by Hibernate to map column values to enum values
public static Status fromString(String code) {
for (Status status : Status.values()) {
if (status.getCode().equals(code.toUpperCase())) {
return status;
}
}
throw new IllegalArgumentException("Unknown user status: " + code);
}
Status(int code, String description) {
this.code = code;
this.description = description;
}
public int getCode() {
return code;
}
public String getDescription() {
return description;
}
@Override
public String toString() {
return getDescription();
}
}
普通班:
public class GenericEnumUserType implements UserType, ParameterizedType {
private static final String DEFAULT_IDENTIFIER_METHOD_NAME = "name";
private static final String DEFAULT_VALUE_OF_METHOD_NAME = "valueOf";
private Class<? extends Enum> enumClass;
private Method identifierMethod;
private Method valueOfMethod;
private NullableType type;
private int[] sqlTypes;
public void setParameterValues(Properties parameters) {
String enumClassName = parameters.getProperty("enumClass");
try {
enumClass = Class.forName(enumClassName).asSubclass(Enum.class);
} catch (ClassNotFoundException cfne) {
throw new HibernateException("Enum class not found", cfne);
}
String identifierMethodName = parameters.getProperty("identifierMethod", DEFAULT_IDENTIFIER_METHOD_NAME);
Class<?> identifierType;
try {
identifierMethod = enumClass.getMethod(identifierMethodName);
identifierType = identifierMethod.getReturnType();
} catch (Exception e) {
throw new HibernateException("Failed to obtain identifier method", e);
}
type = (NullableType) TypeFactory.basic(identifierType.getName());
if (type == null)
throw new HibernateException("Unsupported identifier type " + identifierType.getName());
sqlTypes = new int[] { type.sqlType() };
String valueOfMethodName = parameters.getProperty("valueOfMethod", DEFAULT_VALUE_OF_METHOD_NAME);
try {
valueOfMethod = enumClass.getMethod(valueOfMethodName, identifierType);
} catch (Exception e) {
throw new HibernateException("Failed to obtain valueOf method", e);
}
}
public Class returnedClass() {
return enumClass;
}
public Object nullSafeGet(ResultSet rs, String[] names, Object owner) throws HibernateException, SQLException {
Object identifier = type.get(rs, names[0]);
if (rs.wasNull()) {
return null;
}
try {
return valueOfMethod.invoke(enumClass, identifier);
} catch (Exception e) {
throw new HibernateException(
"Exception while invoking valueOf method '" + valueOfMethod.getName() + "' of " +
"enumeration class '" + enumClass + "'", e);
}
}
public void nullSafeSet(PreparedStatement st, Object value, int index) throws HibernateException, SQLException {
try {
if (value == null) {
st.setNull(index, type.sqlType());
} else {
Object identifier = identifierMethod.invoke(value);
type.set(st, identifier, index);
}
} catch (Exception e) {
throw new HibernateException(
"Exception while invoking identifierMethod '" + identifierMethod.getName() + "' of " +
"enumeration class '" + enumClass + "'", e);
}
}
public int[] sqlTypes() {
return sqlTypes;
}
public Object assemble(Serializable cached, Object owner) throws HibernateException {
return cached;
}
public Object deepCopy(Object value) throws HibernateException {
return value;
}
public Serializable disassemble(Object value) throws HibernateException {
return (Serializable) value;
}
public boolean equals(Object x, Object y) throws HibernateException {
return x == y;
}
public int hashCode(Object x) throws HibernateException {
return x.hashCode();
}
public boolean isMutable() {
return false;
}
public Object replace(Object original, Object target, Object owner) throws HibernateException {
return original;
}
}
答案 1 :(得分:0)
假设您使用MySQL作为存储枚举的数据库,枚举值将存储为数字而不是字符串。因此,使用枚举与使用整数FK不会占用空间。见How Does MySQL Store Enums?
枚举优雅,因为它们易于使用,而不是创建另一个表,然后通过FK引用它。如果它们引用应用程序逻辑,最好将它们作为模式/程序的一部分而不是作为数据的一部分(即表中的行)。见http://www.databasesandlife.com/mysqls-enum-datatype-is-a-good-thing/
请点击此处了解如何在Hibernate中使用枚举。 (我希望这更容易,我不明白为什么Hibernate不支持Enums开箱即用)。 http://community.jboss.org/wiki/UserTypeforpersistingaTypesafeEnumerationwithaVARCHARcolumn