|
Template for mapping a jdk 1.5 enum with a SMALLINT column (Hib 3.0.x)Benoit Xhenseval, http://www.Appendium.com Sometimes enums may be stored as 'int' in the DB (if you have a legacy DB or want to save as much space as possible). This little example shows a way to store and retrieve JDK 1.5 enum to/from a SMALLINT column in the database. I selected a SMALLINT as, if you have more enums that the maximum value of a smallint... you got some other issues! This example uses generics so the code required from one enum to the other is very small. This is inspired from http://www.hibernate.org/265.html Beware that the enum.ordinal() relates to the ORDERING of the enum values, so, if your change it later on, all your DB values will return an incorrect value! Here is our example enum
/** DO NOT RE-ORDER THEM otherwise you will lose the
* correct mapping with the values in your DB */
public enum ContractState {
UNAUTHORISED,
OPEN,
CLOSED;
}
and the Class using it is:
public class Contract {
private int id;
...
private ContractState state;
public void setState(ContractState state) {
this.state = state;
}
public ContractState getState() {
return state;
}
}
We need to define a special Hibernate mapping for that ContractState:
/** This class is used only in the hibernate XML configuration */
public class HibContractState extends IntEnumUserType<ContractStates> {
public HibContractState() {
// we must give the values of the enum to the parent.
super(ContractState.class,ContractState.values());
}
}
and the Contract.hbm.xml file looks like this:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="bla.bla">
<class name="Contract" lazy="true" table="contract">
<id name="id">
<generator class="native"/>
</id>
.....
<property name="state"
type="net.objectlab.sandpit.hibernate.HibContractState"
not-null="true"/>
</class>
</hibernate-mapping>
Et voila... that is all you need to do for each enum-to-int mapping that you may want, provided that you have the following code available in a library, this is the real logic...
import java.io.Serializable;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import org.hibernate.HibernateException;
import org.hibernate.usertype.UserType;
/**
* Generic class to map a JDK 1.5 enum to a SMALL INT column in the DB.
* Beware that the enum.ordinal() relates to the ORDERING of the enum values, so, if
* your change it later on, all your DB values will return an incorrect value!
*
* @author Benoit Xhenseval
* @version 1
*/
public class IntEnumUserType<E extends Enum<E>> implements UserType {
private Class<E> clazz = null;
private E[] theEnumValues;
/**
* Contrary to the example mapping to a VARCHAR, this would
* @param c the class of the enum.
* @param e The values of enum (by invoking .values()).
*/
protected IntEnumUserType(Class<E> c, E[] e) {
this.clazz = c;
this.theEnumValues = e;
}
private static final int[] SQL_TYPES = {Types.SMALLINT};
/**
* simple mapping to a SMALLINT.
*/
public int[] sqlTypes() {
return SQL_TYPES;
}
public Class returnedClass() {
return clazz;
}
/**
* From the SMALLINT in the DB, get the enum. Because there is no
* Enum.valueOf(class,int) method, we have to iterate through the given enum.values()
* in order to find the correct "int".
*/
public Object nullSafeGet(ResultSet resultSet, String[] names, Object owner)
throws HibernateException, SQLException {
final int val = resultSet.getShort(names[0]);
E result = null;
if (!resultSet.wasNull()) {
try {
for(int i=0; i < theEnumValues.length && result == null; i++) {
if (theEnumValues[i].ordinal() == val) {
result = theEnumValues[i];
}
}
} catch (SecurityException e) {
result = null;
} catch (IllegalArgumentException e) {
result = null;
}
}
return result;
}
/**
* set the SMALLINT in the DB based on enum.ordinal() value, BEWARE this
* could change.
*/
public void nullSafeSet(PreparedStatement preparedStatement,
Object value, int index) throws HibernateException, SQLException {
if (null == value) {
preparedStatement.setNull(index, Types.SMALLINT);
} else {
preparedStatement.setInt(index, ((Enum)value).ordinal());
}
}
public Object deepCopy(Object value) throws HibernateException{
return value;
}
public boolean isMutable() {
return false;
}
public Object assemble(Serializable cached, Object owner)
throws HibernateException {
return cached;
}
public Serializable disassemble(Object value) throws HibernateException {
return (Serializable)value;
}
public Object replace(Object original, Object target, Object owner) throws HibernateException {
return original;
}
public int hashCode(Object x) throws HibernateException {
return x.hashCode();
}
public boolean equals(Object x, Object y) throws HibernateException {
if (x == y)
return true;
if (null == x || null == y)
return false;
return x.equals(y);
}
}
This is probably not a great deal for Hibernate experts but may be useful to some of us. Feel free to use it, modify it, improve it and comment on it...and may be something like this should be included in the release? regards Benoit
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||