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.

[java]//GrailsDomainClass
application.domainClasses.each {
domainClass->
List saveOrUpdateBy = GrailsClassUtils.getStaticPropertyValue(domainClass.clazz, ‘saveOrUpdateBy’)
if(saveOrUpdateBy)
{
SaveOrUpdatePlugin.addSOUToDomainClass(domainClass, saveOrUpdateBy)
}
}

[/java]

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

[java]

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(delegate.properties.subMap(saveOrUpdateBy))

if (!inst) {
synchronized (domainClass) {
inst = domainClass.findWhere(delegate.properties.subMap(saveOrUpdateBy))

if (!inst) {
log.info("[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 (!delegate.save(flush: true)) {
delegate.errors.each {
log.error it
}
}
log.debug("[SAVE OR UPDATE] Saved a new object with id ${delegate.id} – ${SaveOrUpdatePlugin.getToStringForGivenFields(delegate, saveOrUpdateBy)}")
}
}
}
else {
log.info "[SAVE OR UPDATE] matching object found: ${SaveOrUpdatePlugin.getToStringForGivenFields(inst, saveOrUpdateBy)}"
long id = inst.id
//inst.properties = delegate.properties
SaveOrUpdatePlugin.copyProperties(domainClassObject, delegate, inst)
log.info "[SAVE OR UPDATE] Updating an existing object ${SaveOrUpdatePlugin.getToStringForGivenFields(inst, saveOrUpdateBy)}"
if (!inst.save(flush: true)) {
inst.errors.each {
log.error it
}
}
log.debug("[SAVE OR UPDATE] Updated existing object with id ${inst.id} – ${SaveOrUpdatePlugin.getToStringForGivenFields(inst, saveOrUpdateBy)}")
}

savedInst = domainClass.findWhere(delegate.properties.subMap(saveOrUpdateBy))

//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.append(object."$it")
}
sb.toString().replaceFirst(‘, ‘, ”)
}

static void copyProperties(GrailsDomainClass domainClassObject, def source, def target) {
domainClassObject.persistantProperties.each {prop ->
if (!(prop.name in [‘dateCreated’, ‘lastUpdated’, ‘id’])) {
target."${prop.name}" = source."${prop.name}"
//println "adding property ${prop.name}"
}
}
}
}
[/java]

Hope this would help someone in need.

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

Thanks & Regards
Mohd Farid
farid@intelligrape.com

FOUND THIS USEFUL? SHARE IT

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

Leave a Reply

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