Momentan bin ich dabei eine Webanwendung mit Grails zu bauen. In der dieser Webanwendung zu grunde liegenden Datenbank (PostgreSQL), habe ich eigene Datentypen in Form von Enums erstellt. Zum Beispiel den folgenden:
CREATE TYPE public.payment_method AS
ENUM ('CASH','CASH_CARD');
Nachdem der Enum auch in der Webanwendung erstellt wurde,
public enum PaymentMethods {
cash, cash_card
}
wollte ich diesen dann in Grails wie folgt einbinden.
class Invoice {
static mapping = {
..
paymentMethod column: 'payment_method'
..
}
PaymentMethods paymentMethod
}
Doch ich bekam folgende Fehlermeldung:
FEHLER: Spalte »payment_method« hat Typ payment_method, aber der Ausdruck hat Typ character varying Hinweis: Sie müssen den Ausdruck umschreiben oder eine Typumwandlung vornehmen.
Mit den standard Typen wie String, Integer oder Date kann Grails umgehen, mit einem Enum anscheinend jedoch nicht. Ein Blick in die Grails-Dokumentation offenbart folgendes:
GORM supports configuration of Hibernate types with the DSL using the type attribute. This includes specifing user types that implement the Hibernate org.hibernate.usertype.UserType interface, which allows complete customization of how a type is persisted.
Nach kurzer Zeit stieß ich auf ein Beispiel für die Implementierung eines eigenen UserTypes. Doch da ich mehrere Enums benutze und nicht für jeden Enum eine extra UserType-Klasse definieren wollte, suchte ich weiter. Die Antwort fand ich dann auf diesem Blog. Dort wird erklärt wie ein parametrisierten UserType erstellt wird. Durch die Mischung beider Beispiele entstand die folgende Klasse, die es ermöglicht in Grails mit verschiedenen Enums zu arbeiten.
public class PGEnumUserType implements UserType, ParameterizedType {
private static final SQL_TYPES = [Types.VARCHAR] as int[]
private Class enumClass;
@Override
public void setParameterValues(Properties parameters) {
String enumClassName = parameters.getProperty("enumClassName")
try { enumClass = (Class) Class.forName(enumClassName) }
catch (ClassNotFoundException cnfe) { throw new HibernateException("Enum class not found", cnfe) }
}
@Override
public Object assemble(Serializable cached, Object owner) throws HibernateException { return cached }
@Override
public Object deepCopy(Object value) throws HibernateException { return value }
@Override
public Serializable disassemble(Object value) throws HibernateException { return value }
@Override
public boolean equals(Object x, Object y) throws HibernateException { return x.equals(y) }
@Override
public int hashCode(Object x) throws HibernateException { return x.hashCode() }
@Override
public boolean isMutable() { return false }
@Override
public Object nullSafeGet(ResultSet rs, String[] names, Object owner) throws HibernateException, SQLException {
def result = rs.getObject(names[0]);
if (result) { return Enum.valueOf(enumClass, (String) result) }
else { return null }
}
@Override
public void nullSafeSet(PreparedStatement st, Object value, int index) throws HibernateException, SQLException {
if (value) { st.setObject(index, ((Enum)value), 1111); }
else { st.setNull(index, Types.VARCHAR) }
}
@Override
public Object replace(Object original, Object target, Object owner) throws HibernateException { return original }
@Override
public Class returnedClass() { return enumClass }
@Override
public int[] sqlTypes() { return SQL_TYPES }
}
Jetzt muss diese Klasse nur noch in Grails eingebunden und das zu verwendende Enum übergeben werden:
class Invoice {
static mapping = {
..
paymentMethod column: 'payment_method', type: PGEnumUserType, params : [ enumClassName: "package.PaymentMethods"]
..
}
PaymentMethods paymentMethod
}
Jetzt kann Grails mit Enums arbeiten.