{"id":32008,"date":"2016-01-30T20:52:24","date_gmt":"2016-01-30T15:22:24","guid":{"rendered":"http:\/\/www.tothenew.com\/blog\/?p=32008"},"modified":"2016-02-08T15:07:29","modified_gmt":"2016-02-08T09:37:29","slug":"hooking-into-the-instance-methods-of-the-gorm-api","status":"publish","type":"post","link":"https:\/\/www.tothenew.com\/blog\/hooking-into-the-instance-methods-of-the-gorm-api\/","title":{"rendered":"Hooking into the Instance methods of the GORM API"},"content":{"rendered":"<p>In my grails plugin I was needed to add some fields that were common to a set of domains. For eg: for some domains we wanted to store fields like createdBy and lastUpdatedBy to keep track of users who created and last updated each record in that domain.<\/p>\n<p><a title=\"Grails Development\" href=\"http:\/\/www.tothenew.com\/grails-application-development\">Grails framework<\/a> provides timestamping using which we can keep track of the time a record was created and last modified, but doesn&#8217;t provide something for user stamping. Although there is a grails plugin named <code>Audit Logging Plugin<\/code>, that provides this feature. But this plugin has its own limitations and was not a perfect choice for our case. I&#8217;ll discuss the issues with this plugin and the possible solutions in my next blog. Apart from these two fields you may want to add some other fields that are common to multiple domains and their values are populated based on some business logic.<\/p>\n<p><strong>So how to handle this:<\/strong><br \/>\nOne way to handle this scenario is by extending the <code>GormInstanceApi<\/code>. This is the class that contains all the GORM persistence methods which are available to each domain in Grails application. An instance of GormInstanceApi is injected to each domain class which is used to invoke the persistence methods. We&#8217;ll use the decorator design pattern here to override the default instance of GormInstanceApi in each domain.<\/p>\n<ul>\n<li>Create a new class <code>GormInstanceApiDecorator<\/code> that extends <code>GormInstanceApi<\/code>.<\/li>\n<li>Decorate instance of GormInstanceApi with GormInstanceApiDecorator and inject it to each domain.<\/li>\n<\/ul>\n<p>Code for GormInstanceApiDecorator:<\/p>\n<p>[code lang=&#8221;groovy&#8221;]<br \/>\npackage org.tothenew.datastore.gorm<\/p>\n<p>import org.codehaus.groovy.grails.orm.hibernate.HibernateDatastore<br \/>\nimport org.grails.datastore.gorm.GormInstanceApi<br \/>\nimport org.grails.datastore.mapping.core.Session<br \/>\nimport org.grails.datastore.mapping.core.SessionCallback<\/p>\n<p>class GormInstanceApiDecorator&lt;D&gt; extends GormInstanceApi&lt;D&gt; {<\/p>\n<p>\tprivate GormInstanceApi gormInstanceApi<\/p>\n<p>\tGormInstanceApiDecorator(GormInstanceApi gormInstanceApi) {<br \/>\n\t\tsuper(gormInstanceApi.persistentClass, gormInstanceApi.datastore as HibernateDatastore)<br \/>\n\t\tthis.gormInstanceApi = gormInstanceApi<br \/>\n\t}<\/p>\n<p>\t@Override<br \/>\n\tD save(D instance) {<br \/>\n\t\tsave(instance, Collections.emptyMap())<br \/>\n\t}<\/p>\n<p>\t@Override<br \/>\n\tD save(D instance, boolean validate) {<br \/>\n\t\tsave(instance, [validate: validate])<br \/>\n\t}<\/p>\n<p>\t@Override<br \/>\n\tD save(D instance, Map params) {<br \/>\n\t\texecute({ Session session -&gt;<br \/>\n\t\t\tdoSave instance, params, session<br \/>\n\t\t} as SessionCallback)<br \/>\n\t}<\/p>\n<p>\t@Override<br \/>\n\tprotected D doSave(D instance, Map params, Session session, boolean isInsert = false) {<br \/>\n\t\t\/\/method that updates the common properties before instance is saved<br \/>\n\t\tupdateStamp instance<br \/>\n\t\tgormInstanceApi.doSave(instance, params, session, isInsert)<br \/>\n\t}<\/p>\n<p>\tprivate updateStamp(Object instance) {<br \/>\n\t\tString actor = &quot;Sandeep Poonia&quot; \/\/fetch the user who is currently logged in<\/p>\n<p>\t\t\/\/set createdBy- first time only<br \/>\n\t\tif (!instance.id) {<br \/>\n\t\t\t\/\/check if domain has createdBy property<br \/>\n\t\t\tif(instance.hasProperty(&quot;createdBy&quot;){<br \/>\n\t\t\t\tinstance.setProperty(&quot;createdBy&quot;, actor)<br \/>\n\t\t\t}<br \/>\n\t\t}<br \/>\n\t\t\/\/check if domain has lastUpdatedBy property<br \/>\n        if(instance.hasProperty(&quot;lastUpdatedBy&quot;){<br \/>\n        \tinstance.setProperty(&quot;lastUpdatedBy&quot;, actor)<br \/>\n        }<br \/>\n\t}<br \/>\n}<br \/>\n[\/code]<\/p>\n<p>Here I&#8217;m taking the example of save method only. If required same can be done with other methods. Before updating the property, I&#8217;m checking whether that property exists in domain or not. This can be done in a different way or you can modify the dafault instance of GromInstanceApi for those domains only which has these properties.<\/p>\n<p>To inject the GormInstanceApiDecorator instance in each Domain:<br \/>\nAdd below code inside <code>doWithDynamicMethods<\/code> of the plugin-<\/p>\n<p>[code lang=&#8221;groovy&#8221;]<br \/>\ndef doWithDynamicMethods = { context -&gt;<br \/>\n\tcontext.grailsApplication.domainClasses.findAll {<br \/>\n\t\tDefaultGrailsDomainClass domainClass -&gt;<br \/>\n\t\t\t\/\/Domain class should have a db mapping<br \/>\n\t\t\tdomainClass.mappingStrategy in [GrailsDomainClass.GORM, GrailsDomainClass.ORM_MAPPING]<br \/>\n\t}.each {<br \/>\n\t\tDefaultGrailsDomainClass domainClass -&gt;<br \/>\n\t\t\tdef gormInstanceApi = domainClass.clazz.currentGormInstanceApi()<br \/>\n\t\t\tGormInstanceApiDecorator decoratedInstance = new GormInstanceApiDecorator(gormInstanceApi)<br \/>\n\t\t\tdomainClass.clazz.setInstanceGormInstanceApi(decoratedInstance)<br \/>\n\t}<br \/>\n}<br \/>\n[\/code]<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In my grails plugin I was needed to add some fields that were common to a set of domains. For eg: for some domains we wanted to store fields like createdBy and lastUpdatedBy to keep track of users who created and last updated each record in that domain. Grails framework provides timestamping using which we [&hellip;]<\/p>\n","protected":false},"author":182,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"iawp_total_views":6},"categories":[7],"tags":[29,4840,3078,3030],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/posts\/32008"}],"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\/182"}],"replies":[{"embeddable":true,"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/comments?post=32008"}],"version-history":[{"count":0,"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/posts\/32008\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/media?parent=32008"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/categories?post=32008"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/tags?post=32008"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}