Colin Harrington

GORM :: override a setter on a Grails Domain

by on Jan.08, 2009, under Groovy-Grails

GrailsRather than implement properties in the Java Language, we have a convention called a Java Bean.  This basically means that properties are implemented with getter and setter methods with a PascalCase property name in the method among a few other simple conventions

Grails makes it incredibly easy to manage domain classes since it is inherently domain-centric.  When you need to enhance your Domain classes at the core of your application, you have an option of implementing your own getters and setters.   I love that by default you do not have to implement your own setters, but you have the power to do so if you wish;  the principle of sensible defaults.  Here is an example of a Book Object with two properties, author and isbn.

class Book {
    String author
    String isbn
}

Consider the case where a Book is in your Database with the ISBN of 978-1430219262 and a user tries to search for "9781430219262".  Unless you do some searchable magic, the user will not find the book.  A simple solution fo the issue would be to never store ‘-‘ in the databse.  to make this happen you could easily remove the dash in the setter.  So your domain would look like this:

class Book {
    String author
    String isbn

    void setIsbn(String i) {
        isbn = i.replace('-','')
    }

}

When I first tried this, I failed a few times before I got it right.  Maybe I was just spoiled with Groovy’s groovyness, but I started out writing
def setIsbn(i){ ... }  and when that didn’t work: void setIsbn(i){ ... } which was being called, but never actually set the property.  Then I had a forehead smacking moment where I realized that the method signature must precisely match the signature of a JavaBean setter like public void setPropertyName(Type propVal) { ... }  In my case I had to make sure that the method was public (public by default), has a return type of void (which is not the default behavior of a closure), and the parameter passed in was of the same type as the property (String)

And then I blogged about it — the fourth day.

:,

9 Comments for this entry

  • Mike

    Colin,
    Great post – I’ve struggled with the same thing in the past. One way you can make sure you get the correct method signature is to run javap on a compiled groovy class (before you override the method, of course):

    groovyc Book.groovy
    javap Book
    Compiled from “Book.groovy”
    public class Book extends java.lang.Object implements groovy.lang.GroovyObject{

    public void setIsbn(java.lang.String);
    ….
    }

    You can then copy that method signature directly into your code to and override it to your hearts content.

    (hat tip to http://naleid.com/blog/2008/12/24/groovy-spread-operator-optional-for-properties-plus-a-peek-into-the-sausage-factory/ for getting me interested in javap)

  • adwin

    oh great. i just know that we can override the setter in GORM. THAT’s really usefull since I need to format the date from String to java.util.date (i am not using g:datepicker, but using my own javascript date picker).

    thank you for sharing your knowledge…

  • Paras Jain

    That certainly helps. Thanks for your suggestion. Thanks to Mike too for his insight

  • Marc

    I’m curious, does GORM use the property set when retrieving from the database?

    on a credential domain class I wanting to guard against a password field being set to a non-hashed value… So my initial thinking was to create:

    String password
    void setPassword(String pw) {
    password = getHash(pw,salt)
    }

    but if the hashed password was retrieved from the database wouldn’t it end up going through another cycle of hashing?

  • danny

    Thanks! This just saved me time. I, like you, tried def setIsbn(i){ … } to find nothing doing.

  • matt

    Ditto danny’s comment. Thanks for saving me a big headache.

  • John

    Thanks Colin!

    A quick Google search and I found this post and the answer to my problem too. To answer Marc’s question, YES – the setters are called when the domain class is being populated so your idea won’t work. What I used as a workaround was to create a service class and add an updateEvent() method which is called from the beforeInsert and beforeUpdate. In the event, I check to see if the value is dirty and only manipulate it if it has.

    if ( user.isDirty(‘password’) && task.getPersistentValue(‘password’) != user.password) { … }

    I check that it is dirty and also that the newly set value is different from the persisted value as a safety precaution.

    -John

  • Jeff

    Solved my problem! (Doh!!!)

  • las vegas nightclub

    I’m really enjoying the design and layout of your site.

    It’s a very easy on the eyes which makes it much more pleasant for me to come here and visit
    more often. Did you hire out a designer to create your theme?
    Excellent work!

    Visit my blog :: las vegas nightclub

1 Trackback or Pingback for this entry

Leave a Reply

Looking for something?

Use the form below to search the site:

Still not finding what you're looking for? Drop a comment on a post or contact us so we can take care of it!