{"id":3365,"date":"2011-03-15T01:06:33","date_gmt":"2011-03-14T19:36:33","guid":{"rendered":"http:\/\/www.tothenew.com\/blog\/?p=3365"},"modified":"2022-01-12T19:01:45","modified_gmt":"2022-01-12T13:31:45","slug":"implementing-saveorupdate-for-domain-classes","status":"publish","type":"post","link":"https:\/\/www.tothenew.com\/blog\/implementing-saveorupdate-for-domain-classes\/","title":{"rendered":"Implementing saveOrUpdate() for domain classes"},"content":{"rendered":"<p>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<\/p>\n<p>Suppose we have a domain class Personwith following definition:<\/p>\n<p>[java]class Person{<br \/>\nString name<br \/>\nString source<br \/>\nString description<\/p>\n<p>def static saveOrUpdateBy = [&#8216;name&#8217;, &#8216;source&#8217;]<\/p>\n<p>} [\/java]<\/p>\n<p>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.<\/p>\n<p>We want to saveOrUpdate a person if the name and source are same. So, If we happen to execute following three lines of code&#8230;<\/p>\n<p>[java]new Person(name: &#8216;Alex&#8217;, source: &#8216;source-1&#8217;, &#8216;Hello&#8217;).saveOrUpdate();<br \/>\n new Person(name: &#8216;Alex&#8217;, source: &#8216;source-1&#8217;, &#8216;Hi&#8217;).saveOrUpdate();<br \/>\n new Person(name: &#8216;Alex&#8217;, source: &#8216;source-1&#8217;, &#8216;Wow&#8217;).saveOrUpdate();[\/java]<\/p>\n<p>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.<\/p>\n<p>We can have\u00a0 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<\/p>\n<p>[java]def static saveOrUpdateBy = [&#8216;name&#8217;, &#8216;source&#8217;][\/java]<\/p>\n<p>The following is the piece of code for achieving the same:<\/p>\n<p>Fetching the static property of the domain class.<\/p>\n<p>[java]\/\/GrailsDomainClass<br \/>\n application.domainClasses.each {<br \/>\n domainClass-&gt;<br \/>\n List saveOrUpdateBy = GrailsClassUtils.getStaticPropertyValue(domainClass.clazz, &#8216;saveOrUpdateBy&#8217;)<br \/>\n if(saveOrUpdateBy)<br \/>\n {<br \/>\n SaveOrUpdatePlugin.addSOUToDomainClass(domainClass, saveOrUpdateBy)<br \/>\n }<br \/>\n }<\/p>\n<p>[\/java]<\/p>\n<p>A method to add a meta-method saveOrUpdate to given domain classes.<\/p>\n<p>[java]<\/p>\n<p>class SaveOrUpdatePlugin {<\/p>\n<p> static void addSOUToDomainClass(GrailsDomainClass domainClassObject, List saveOrUpdateBy) {<br \/>\n println &quot;Adding saveOrUpdate method to $domainClassObject.clazz&quot;<br \/>\n Class domainClass = domainClassObject.clazz<br \/>\n domainClass.metaClass.saveOrUpdate = {<br \/>\n def savedInst<br \/>\n try {<br \/>\n log.debug &quot;[SAVE OR UPDATE] Trying to do saveOrUpdate for ${SaveOrUpdatePlugin.getToStringForGivenFields(delegate, saveOrUpdateBy)}&quot;<\/p>\n<p> def inst = domainClass.findWhere(delegate.properties.subMap(saveOrUpdateBy))<\/p>\n<p> if (!inst) {<br \/>\n synchronized (domainClass) {<br \/>\n inst = domainClass.findWhere(delegate.properties.subMap(saveOrUpdateBy))<\/p>\n<p> if (!inst) {<br \/>\n log.info(&quot;[SAVE OR UPDATE] Saving a new object ${SaveOrUpdatePlugin.getToStringForGivenFields(delegate, saveOrUpdateBy)}&quot;)<br \/>\n if (!delegate.validate()) {<br \/>\n delegate.errors.each {<br \/>\n log.error &quot;Error while saving scrapedMovie\u00a0 $it&quot;<br \/>\n }<br \/>\n }<\/p>\n<p> if (!delegate.save(flush: true)) {<br \/>\n delegate.errors.each {<br \/>\n log.error it<br \/>\n }<br \/>\n }<br \/>\n log.debug(&quot;[SAVE OR UPDATE] Saved a new object with id ${delegate.id} &#8211; ${SaveOrUpdatePlugin.getToStringForGivenFields(delegate, saveOrUpdateBy)}&quot;)<br \/>\n }<br \/>\n }<br \/>\n }<br \/>\n else {<br \/>\n log.info &quot;[SAVE OR UPDATE] matching object found: ${SaveOrUpdatePlugin.getToStringForGivenFields(inst, saveOrUpdateBy)}&quot;<br \/>\n long id = inst.id<br \/>\n \/\/inst.properties = delegate.properties<br \/>\n SaveOrUpdatePlugin.copyProperties(domainClassObject, delegate, inst)<br \/>\n log.info &quot;[SAVE OR UPDATE] Updating an existing object ${SaveOrUpdatePlugin.getToStringForGivenFields(inst, saveOrUpdateBy)}&quot;<br \/>\n if (!inst.save(flush: true)) {<br \/>\n inst.errors.each {<br \/>\n log.error it<br \/>\n }<br \/>\n }<br \/>\n log.debug(&quot;[SAVE OR UPDATE] Updated existing object with id ${inst.id} &#8211; ${SaveOrUpdatePlugin.getToStringForGivenFields(inst, saveOrUpdateBy)}&quot;)<br \/>\n }<\/p>\n<p> savedInst = domainClass.findWhere(delegate.properties.subMap(saveOrUpdateBy))<\/p>\n<p> \/\/returning the saved instance&#8230;<\/p>\n<p> } catch (Exception ex) {<br \/>\n log.error(&quot;Failed to saveOrUpdate the object ${delegate} &quot;, ex)<br \/>\n }<br \/>\n return savedInst<br \/>\n }<br \/>\n }<\/p>\n<p> static String getToStringForGivenFields(def object, List fields) {<br \/>\n StringBuilder sb = new StringBuilder()<br \/>\n sb.append(object.toString() + &quot; SaveOrUpdateBy: &quot;)<br \/>\n fields.each {<br \/>\n sb.append(&quot;, ${it}=&quot;)<br \/>\n sb.append(object.&quot;$it&quot;)<br \/>\n }<br \/>\n sb.toString().replaceFirst(&#8216;, &#8216;, &#8221;)<br \/>\n }<\/p>\n<p> static void copyProperties(GrailsDomainClass domainClassObject, def source, def target) {<br \/>\n domainClassObject.persistantProperties.each {prop -&gt;<br \/>\n if (!(prop.name in [&#8216;dateCreated&#8217;, &#8216;lastUpdated&#8217;, &#8216;id&#8217;])) {<br \/>\n target.&quot;${prop.name}&quot; = source.&quot;${prop.name}&quot;<br \/>\n \/\/println &quot;adding property ${prop.name}&quot;<br \/>\n }<br \/>\n }<br \/>\n }<br \/>\n}<br \/>\n[\/java]<\/p>\n<pre>Hope this would help someone in need.<\/pre>\n<p>Any comments \/ suggestion for improvement of the same are most welcome.<\/p>\n<p>Thanks &amp; Regards<br \/>\nMohd Farid<br \/>\nfarid@intelligrape.com<\/p>\n","protected":false},"excerpt":{"rendered":"<p>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 = [&#8216;name&#8217;, &#8216;source&#8217;] } [\/java] Details of a person [&hellip;]<\/p>\n","protected":false},"author":26,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"iawp_total_views":1},"categories":[7],"tags":[],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/posts\/3365"}],"collection":[{"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/users\/26"}],"replies":[{"embeddable":true,"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/comments?post=3365"}],"version-history":[{"count":1,"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/posts\/3365\/revisions"}],"predecessor-version":[{"id":54611,"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/posts\/3365\/revisions\/54611"}],"wp:attachment":[{"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/media?parent=3365"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/categories?post=3365"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/tags?post=3365"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}