Skip to main content

Handling Type Codes in Your Domain Models

Posted by jhook on December 14, 2005 at 9:43 AM PST

With complex enterprise systems, we often times find ourselves with lots of 'flags' or 'types' within our database tables. Utilizing Hibernate's UserType facility, we can handle these types in such a way that will carry extra behavior and information within your domain models.

You probably all have them, those VARCHAR(2) or VARCHAR(1) fields in your tables that describe the type or source of the data. One approach would be to explicitly map that character data as a String or char property to your domain models. I've also seen a lot of discussion around using enums within JSE 5 to handle these values. But often, you need to translate that code into additional behavior or just human-readable format.

The approach we've taken is to create simple Objects that have an identity to match the 'type' in the database and then include extra information. An example of handling purchase orders, where each line item has a status:

public class LineStatus {

    // matches db 'type field
    private final String id;

    // used in business logic
    private final boolean accepted;

    // human readable for invoice
    private final String summary;

    // human readable for web app
    private final String detail;

    public LineStatus(String id, boolean accepted, ...) {
       this.id = id;
       this.accepted = accepted;
       ...
    }

    // getters only

    // implement hashCode, equals, and toString

    public final static LineStatus BACKORDERED = ...;
    public final static LineStatus ACCEPTED = ....;
    public final static LineStatus REJECTED = ....;
    public final static LineStatus RESTRICTED = ....;

   
    private final static Map statuses;

    static {
       // initialize our set of possible statuses
    }

    public static LineStatus get(String id) {
        LineStatus s;
        if (id != null) {
            s = statuses.get(id);
        }
        // default if s is null?
        return s;
    }
}

Now we have a basic Domain Type that we can program off of in relation to our OrderLines. This Domain Type will also include additional (static) data that can be carried into your application, based on that one two character field from the database. The addition of 'summary' and 'detail' properties also provides our applications with consistent, human-readable, information as it relates to that status.

The next step is configuring this Type for Hibernate by creating a LineStatusType.

public class LineStatusType implements UserType {

    private static final int[] SqlTypes = new int[] { Types.VARCHAR };

    public LineStatusType() {
        super();
    }

    public int[] sqlTypes() {
        return SqlTypes;
    }

    public Class returnedClass() {
        return LineStatus.class;
    }

    public boolean equals(Object x, Object y) throws HibernateException {
        return (x == y) || (x != null && y != null && (x.equals(y)));
    }

    public int hashCode(Object value) throws HibernateException {
        return value.hashCode();
    }

    public Object nullSafeGet(ResultSet rs, String[] names, Object owner)
            throws HibernateException, SQLException {
        String id = rs.getString(names[0]);
       return LineStatus.get(id);
    }

    public void nullSafeSet(PreparedStatement st, Object value, int index)
            throws HibernateException, SQLException {
        if (value != null) {
            st.setString(index, ((LineStatus) value).getId());
        } else {
            st.setNull(index, Types.VARCHAR);
        }
    }

    public Object deepCopy(Object value) throws HibernateException {
        return value;
    }

    public boolean isMutable() {
        return false;
    }

    public Serializable disassemble(Object value) throws HibernateException {
        return (Serializable) value;
    }

    public Object assemble(Serializable state, Object owner)
            throws HibernateException {
        return state;
    }

    public Object replace(Object original, Object target, Object owner)
            throws HibernateException {
        return original;
    }
}

That's it. Some more complex objects could have a few of these Domain Types allotted. Are there other approaches to handling type flags in your databases?

Related Topics >>