First of all this is definitely a new user question.
What we are trying to do is map a code table (Errors) that has values that will never change in our database.
It has only a few fields like code, description, severity.
We wanted to use typesafe enums in our code to ensure type safety.
The examples i see of use the usertype classes for typesafe enums in hibernate 2 all only show the
simple example where we just have a single value in the database like an integer or something.
Like in here (
http://www.hibernate.org/203.html)
What we want is our entire error code object to be used and loaded so we can get the description or severity out.
Also so when an object that has an Error in the Error in the object can be compared to the typesafe value.
Hibernate version:2
Mapping documents:
Code:
<class
name="bell.horizon.persistence.horizondb.ErrorCode"
table="ERROR"
mutable="false"
>
<cache usage="read-only"/>
<id
name="errorId"
type="java.lang.Long"
column="ERROR_ID"
>
<generator class="sequence">
<param name="sequence">ERROR_SEQ</param>
</generator>
</id>
<property
name="errorCode"
type="java.lang.String"
column="ERROR_CODE"
not-null="true"
length="20"
/>
<property
name="errorSeverity"
type="java.lang.String"
column="ERROR_SEVERITY"
length="20"
/>
<property
name="description"
type="java.lang.String"
column="DESCRIPTION"
length="512"
/>
</class>
Code between sessionFactory.openSession() and session.close():
Code:
public class ErrorCode implements Serializable {
/* Section allows for the usage of extended/enumerated UserType ErrorUType */
/* Keeps track of all instances by name, for efficient lookup. */
private static final Map instancesById = new HashMap();
private static final Map instancesByCode = new HashMap();
public static final ErrorCode SAG_INVALID = new ErrorCode("SAG_INVALID", new Long(1));
public static final ErrorCode DUPLICATE_ADD = new ErrorCode("DUPLICATE_ADD", new Long(2));
public static final ErrorCode UPDATE_NO_ADD = new ErrorCode("UPDATE_NO_ADD", new Long(3));
public static final ErrorCode UPDATE_INVALID = new ErrorCode("UPDATE_INVALID", new Long(4));
public static final ErrorCode BLIF_PERSISTENCE = new ErrorCode("BLIF_PERSISTENCE", new Long(5));
public static final ErrorCode DD_CHANGE = new ErrorCode("DD_CHANGE", new Long(6));
public static final ErrorCode INFLIGHT_UPDATE = new ErrorCode("INFLIGHT_UPDATE", new Long(7));
public static final ErrorCode VERSION_INVALID = new ErrorCode("VERSION_INVALID", new Long(8));
public static final ErrorCode DELETE_NO_ADD = new ErrorCode("DELETE_NO_ADD", new Long(9));
public static final ErrorCode DELETE_INVALID = new ErrorCode("DELETE_INVALID", new Long(10));
public static final ErrorCode INFLIGHT_DELETE = new ErrorCode("INFLIGHT_DELETE", new Long(11));
private Long errorId;
private String errorCode;
private String errorSeverity;
private String description;
public ErrorCode() {
}
/* TaskType constructor */
public ErrorCode(String errorCode, Long errorId) {
this.errorCode = errorCode;
this.errorId = errorId;
// Record this instance in the collection that tracks the enumeration
instancesByCode.put(errorCode, this);
instancesById.put(errorId, this);
}
public Long getErrorId() {
return this.errorId;
}
public void setErrorId(Long errorId) {
this.errorId = errorId;
}
public String getErrorCode() {
return this.errorCode;
}
public void setErrorCode(String errorCode) {
this.errorCode = errorCode;
}
public String getErrorSeverity() {
return this.errorSeverity;
}
public void setErrorSeverity(String errorSeverity) {
this.errorSeverity = errorSeverity;
}
public String getDescription() {
return this.description;
}
public void setDescription(String description) {
this.description = description;
}
/**
* Obtain the collection of all legal enumeration values.
*
* @return all instances of this typesafe enumeration.
*/
public static Collection getAllValues() {
return Collections.unmodifiableCollection(instancesByCode.values());
}
/**
* Look up an instance by errorCode.
*
* @param errorCode the external name of an instance.
* @return the corresponding instance.
* @throws NoSuchElementException if there is no such instance.
*/
public static ErrorCode getInstanceByType(String errorCode) {
ErrorCode result = (ErrorCode) instancesByCode.get(errorCode);
if (result == null) {
throw new NoSuchElementException(errorCode);
}
return result;
}
/**
* Look up an instance by description.
*
* @param id the external name of an instance.
* @return the corresponding instance.
* @throws NoSuchElementException if there is no such instance.
*/
public static ErrorCode getInstanceById(Long id) {
ErrorCode result = (ErrorCode) instancesById.get(id);
if (result == null) {
throw new NoSuchElementException(id.toString());
}
return result;
}
/**
* Insure that deserialization preserves the signleton property.
*/
private Object readResolve() {
return getInstanceByType(errorCode);
}
public String toString() {
return "Error{" +
"errorId=" + errorId +
", errorCode='" + errorCode + "'" +
", errorSeverity='" + errorSeverity + "'" +
", description='" + description + "'" +
"}";
}
}
public class ErrorUType implements UserType {
public boolean isMutable() {
return false;
}
public Object deepCopy(Object value) {
return (ErrorCode)value;
}
public boolean equals(Object x, Object y) {
// We can compare instances, since Error are immutable singletons
return (x == y);
}
public Class returnedClass() {
return ErrorCode.class;
}
public int[] sqlTypes() {
// Allocate a new array each time to protect against callers changing
// its contents.
int[] typeList = {
Types.NUMERIC
};
return typeList;
}
public Object nullSafeGet(ResultSet rs, String[] names, Object owner)
throws HibernateException, SQLException
{
// Start by looking up the value name
Long id = (Long) Hibernate.LONG.nullSafeGet(rs, names[0]);
if (id == null || id == new Long(0)) {
return null;
}
// Then find the corresponding enumeration value
try {
// return Error.getInstanceByType(name);
return bell.horizon.persistence.horizondb.ErrorCode.getInstanceById(id);
}
catch (java.util.NoSuchElementException e) {
throw new HibernateException("Bad Error type value: " + id, e);
}
}
public void nullSafeSet(PreparedStatement st, Object value, int index)
throws HibernateException, SQLException
{
Long id = null;
if (value != null)
id = ((ErrorCode)value).getErrorId();
Hibernate.LONG.nullSafeSet(st, id, index);
}
}
Sample calling code....
taskStatus.setErrorCode(ErrorCode.DUPLICATE_ADD);
horizonService.updateTaskStatus(taskStatus);
another sample (code that we wished we had).....
if (taskStatus.getErrorCode().equals(ErrorCode.DUPLICATE_ADD)) {
// send message
}
what we really have for the above........
// ignore the ugly fact that our class is called ErrorCode and it has a field called error code as
// this was a bad choice to get away from java.lang.Error conficting with our Error (rename refactored
// Error to ErrorCode without looking at the internals)
if (taskStatus.getErrorCode().getErrorCode.equals(ErrorCode.DUPLICATE_ADD.getErrorCode()) {
// send message
}
The first sample works for us in that the taskStatus table gets the foreign key inserted that points to the proper error.
What i don't understand/like is that in order for the equality check to work in the second sample i would need
to change my static constructor to contain all the fields that are in the database. This doesn't seem
right to me to duplicate all of the information.
Maybe we shouldn't be using a typesafe enum/usertype but instead should just access the object through the DAO and reload it
from the database using one of the available caching schemes?
Can anyone point me to a more appropriate example? (We do use this similar thing in 3 different places so far)