Implementing saveOrUpdate() for domain classes

15 / Mar / 2011 by Mohd Farid 1 comments

Some times in our applications, while saving a domain object we may desire to have a save or update behavior on the basis of certain fieldSupp

Suppose we have a domain class Personwith following definition:

class Person{
String name
String source
String description

def static saveOrUpdateBy = ['name', 'source']


Details of a person are coming from multiple sources at multiple intervals of time. We want to maintain the latest records from each source at any given time.

We want to saveOrUpdate a person if the name and source are same. So, If we happen to execute following three lines of code…

new Person(name: 'Alex', source: 'source-1', 'Hello').saveOrUpdate();
 new Person(name: 'Alex', source: 'source-1', 'Hi').saveOrUpdate();
 new Person(name: 'Alex', source: 'source-1', 'Wow').saveOrUpdate();

By the end of last line we would like to have only one row in the table corresponding to Person domain. Because we want to saveOrUpdate() on the basis of name , source combination.

We can have  a metaclass method injected in all our domain classes to have this method. The properties to be considered while doing saveOrUpdate can be specified through a static field in class.. Some thing like

def static saveOrUpdateBy = ['name', 'source']

The following is the piece of code for achieving the same:

Fetching the static property of the domain class.

 application.domainClasses.each {
 List saveOrUpdateBy = GrailsClassUtils.getStaticPropertyValue(domainClass.clazz, 'saveOrUpdateBy')
 SaveOrUpdatePlugin.addSOUToDomainClass(domainClass, saveOrUpdateBy)

A method to add a meta-method saveOrUpdate to given domain classes.

class SaveOrUpdatePlugin {

 static void addSOUToDomainClass(GrailsDomainClass domainClassObject, List saveOrUpdateBy) {
 println "Adding saveOrUpdate method to $domainClassObject.clazz"
 Class domainClass = domainClassObject.clazz
 domainClass.metaClass.saveOrUpdate = {
 def savedInst
 try {
 log.debug "[SAVE OR UPDATE] Trying to do saveOrUpdate for ${SaveOrUpdatePlugin.getToStringForGivenFields(delegate, saveOrUpdateBy)}"

 def inst = domainClass.findWhere(

 if (!inst) {
 synchronized (domainClass) {
 inst = domainClass.findWhere(

 if (!inst) {"[SAVE OR UPDATE] Saving a new object ${SaveOrUpdatePlugin.getToStringForGivenFields(delegate, saveOrUpdateBy)}")
 if (!delegate.validate()) {
 delegate.errors.each {
 log.error "Error while saving scrapedMovie  $it"

 if (! true)) {
 delegate.errors.each {
 log.error it
 log.debug("[SAVE OR UPDATE] Saved a new object with id ${} - ${SaveOrUpdatePlugin.getToStringForGivenFields(delegate, saveOrUpdateBy)}")
 else { "[SAVE OR UPDATE] matching object found: ${SaveOrUpdatePlugin.getToStringForGivenFields(inst, saveOrUpdateBy)}"
 long id =
 // =
 SaveOrUpdatePlugin.copyProperties(domainClassObject, delegate, inst) "[SAVE OR UPDATE] Updating an existing object ${SaveOrUpdatePlugin.getToStringForGivenFields(inst, saveOrUpdateBy)}"
 if (! true)) {
 inst.errors.each {
 log.error it
 log.debug("[SAVE OR UPDATE] Updated existing object with id ${} - ${SaveOrUpdatePlugin.getToStringForGivenFields(inst, saveOrUpdateBy)}")

 savedInst = domainClass.findWhere(

 //returning the saved instance...

 } catch (Exception ex) {
 log.error("Failed to saveOrUpdate the object ${delegate} ", ex)
 return savedInst

 static String getToStringForGivenFields(def object, List fields) {
 StringBuilder sb = new StringBuilder()
 sb.append(object.toString() + " SaveOrUpdateBy: ")
 fields.each {
 sb.append(", ${it}=")
 sb.toString().replaceFirst(', ', '')

 static void copyProperties(GrailsDomainClass domainClassObject, def source, def target) {
 domainClassObject.persistantProperties.each {prop ->
 if (!( in ['dateCreated', 'lastUpdated', 'id'])) {
 target."${}" = source."${}"
 //println "adding property ${}"
Hope this would help someone in need.

Any comments / suggestion for improvement of the same are most welcome.

Thanks & Regards
Mohd Farid

Tag -


comments (1 “Implementing saveOrUpdate() for domain classes”)

Leave a comment -