Hibernate dispone de un mecanismo propio para mapear enumeraciones y relacionarlas directamente con las columnas de una tabla de una base de datos. Este mecanismo permite a su vez un mantenimiento del software más correcto y adecuado.Para explicar el funcionamiento de este mecanismo estableceremos un caso práctico.
Lo primero sería definir la enumeración,
public enum Roles implements PersistentEnum {
ADMINISTRATOR("Administrator"), USER("User");
private final String role;
Roles(String role) {
this.role = role;
}
@Override
public String getRole() {
return role;
}
}
Hemos creado dos roles, el administrador para el backend de la página web y los usuarios para el frontend de la misma.
La interfaz PersistentEnum quedaría como sigue,
public interface PersistentEnum {
public String getRole();
}
que en el caso de la clase enumeración implementaría el método getRole().
Ahora definimos el Hibernate User Type por medio de una clase abstracta que queda de la siguiente manera,
public abstract class PersistentEnumUserType<T extends PersistentEnum> implements UserType {
private Class<T> clazz = null;
private static final int[] SQL_TYPES = { Types.VARCHAR };
protected PersistentEnumUserType(Class<T> c) {
this.clazz = c;
}
@Override
public int[] sqlTypes() {
return SQL_TYPES;
}
@Override
public Class returnedClass() {
return clazz;
}
@Override
public Object nullSafeGet(ResultSet resultSet, String[] names, SessionImplementor si, Object owner)
throws HibernateException, SQLException {
String name = resultSet.getString(names[0]);
if (resultSet.wasNull())
return null;
for(Object value : returnedClass().getEnumConstants()) {
if(name.equals(((PersistentEnum)value).getRole())) {
return value;
}
}
throw new IllegalStateException("Unknown " + returnedClass().getSimpleName() + " id");
}
@Override
public void nullSafeSet(PreparedStatement preparedStatement, Object value,
int index, SessionImplementor si) throws HibernateException, SQLException {
if (null == value) {
preparedStatement.setNull(index, Types.VARCHAR);
} else {
preparedStatement.setString(index, ((PersistentEnum)value).getRole());
}
}
@Override
public Object deepCopy(Object value) throws HibernateException {
return value;
}
@Override
public boolean isMutable() {
return false;
}
@Override
public Object assemble(Serializable cached, Object owner) throws HibernateException {
return cached;
}
@Override
public Serializable disassemble(Object value) throws HibernateException {
return (Serializable) value;
}
@Override
public Object replace(Object original, Object target, Object owner)
throws HibernateException {
return original;
}
public String objectToSQLString(Object value) {
return '\'' + ((Enum) value).name() + '\'';
}
public String toXMLString(Object value) {
return ((Enum) value).name();
}
@Override
public int hashCode(Object x) throws HibernateException {
return x.hashCode();
}
@Override
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);
}
}
En este patrón lo principal son los métodos nullSafeGet() y nullSafeSet(). El primero es llamado cuando el resulset de la base de datos es mapeado a un Object, el segundo se llama para mapear los campos de un Object a los parámetros de una sentencia SQL (insert/update/delete). Finalmente el método returnedClass() será sobreescrito en las subclases para llamar a la enumeración,
public class RolesUserType extends PersistentEnumUserType<Roles> {
public RolesUserType() {
super(Roles.class);
}
@Override
public Class<Roles> returnedClass() {
return Roles.class;
}
}