没有Enum的状态模式和Hibernate

时间:2010-11-08 13:28:20

标签: java hibernate design-patterns reflection state

我已经用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中实现这一点? ??

谢谢!

2 个答案:

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