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:

[java]class Person{
String name
String source
String description

def static saveOrUpdateBy = [‘name’, ‘source’]

} [/java]

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…

[java]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();[/java]

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

[java]def static saveOrUpdateBy = [‘name’, ‘source’][/java]

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


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

Leave a Reply

Your email address will not be published. Required fields are marked *