Tuesday, 15 July 2008

Tricking Grails into persisting Enum properties

Although Grails 1.0.3 implements persistence of java.lang.Enum fields in GORM, unfortunately it sticks with the misguided Hibernate convention of persisting the Enum ordinal rather than the String value. In Hibernate at least you can override this default, in Grails currently you can't.

In the meantime, here's a workaround pattern that should be amenable to a low-impact refactor once Grails 1.0.4 drops.

    enum Flavr { /* some enum instances */ }

class Lolcat {
String flavrName

static transients = ['flavr']

static constraints = {
flavrName(nullable: true, inList: Flavr.values().collect{it.name()})
}

static mapping = {
columns {
flavrName(name: 'flavr')
}
}

Flavr getFlavr() {
return flavrName ? Flavr.valueOf(flavrName) : null
}

void setFlavr(Flavr flavr) {
flavrName = flavr?.name()
}
}


What we've done is create a transient Enum property backed by a String that GORM uses to persist the value. The constraint prevents any erroneous values being set directly to the String property. The column name mapping is an attempt at future proofing, so the column name won't need to be changed once 'flavr' becomes a full-blown persistent Enum.

As and when that's possible you simply replace 'String flavrName' with a 'Flavr flavr' and remove the transients, constraints and mapping blocks and the getter and setter.

Two downsides are 1) that dynamic finders and criteria queries must operate on real properties and not transient ones and 2) that you can pretty much guarantee some muppet will directly access 'flavrName' somewhere - something you can guard against much more effectively in Java than you can in Groovy.

1 comment:

Dan said...

This is apparently fixed in 1.1 Beta 1:

http://www.grails.org/1.1+Beta1+Release+Notes#Upgrade notes