Grails findOrCreate and findOrSave Methods

Grails 2.0 introduced four new dynamic methods that are available on domain objects.  From the Grails documentation:

Domain classes have support for the findOrCreateWhere, findOrSaveWhere, findOrCreateBy and findOrSaveBy query methods which behave just like findWhere and findBy methods except that they should never return null. If a matching instance cannot be found in the database then a new instance is created, populated with values represented in the query parameters and returned. In the case of findOrSaveWhere and findOrSaveBy, the instance is saved before being returned.

These methods are pretty handy for streamlining certain situations.  To illustrate one such situation, this post will illustrate how we might use these methods in a blog application to assign tags to blog posts.

Let’s say we have the following two domain objects:

package blog
 
class Post {
 
    String body
    String title
 
    static belongsTo = [user:User]
    static hasMany = [tags:Tag]
 
    static constraints = {
        body blank:false, widget:'textarea'
        title blank: false, size:0..150
    }
}
package blog
 
class Tag {
    String name
 
    static belongsTo = Post
    static hasMany = [posts:Post]
 
    static constraints = {
        name blank:false
    }
}

Blog posts are an example where tags are generally free form and it’s not uncommon to add new tags on the fly when adding a post. This is a good use for the new findOrCreate method. Imagine that the user submits a comma delimited set of tags that they would like to assign to a new post. Then in our controller we can use the following logic to simplify the process of saving the post.

def save() {
        def postInstance = new Post(params)
        def tags = params?.tagStr?.split(',')
        tags.each {
            def t = Tag.findOrCreateWhere(name: it.trim())
            postInstance.addToTags(t)
        }
        if (!postInstance.save(flush: true)) {
            render(view: "create", 
                   model: [postInstance: postInstance])
            return
        }
 
	flash.message = message(code: 'default.created.message', 
                        args: [message(code: 'post.label', default: 'Post'), 
                        postInstance.id])
        redirect(action: "show", id: postInstance.id)
    }

About Chris

Chris Latimer is an IT professional and Grails enthusiast.
This entry was posted in grails and tagged , , . Bookmark the permalink.