When setting up a domain model in GORM, it is often handy to create a abstract base class to inherit common table attributes. For those familiar with JPA, @MappedSuperclass provides this capability. With grails, this can be achieved simply by inheriting from an abstract base domain class such as the following:

abstract class Base {
   Date dateCreated
   Date lastUpdated
   String updatedApplication = 'app'
   String updatedBy
   String codeVersion = '$Revision: 1.2 $'

   static constraints = {
      dateCreated(nullable:true)
      lastUpdated(nullable:true)
      updatedApplication(maxSize:64, nullable:true)
      updatedBy(maxSize:32, nullable:true)
      codeVersion(maxSize:64, nullable:true)
   }

   static mapping = {
      columns {
         addColumnMappings(delegate)
      }
   }

   static addColumnMappings = { Object d ->
      delegate = d
      dateCreated column: 'CRT_DTTM'
      lastUpdated column: 'LST_UPD_DTTM'
      updatedApplication column: 'LST_UPD_APP'
      updatedBy column: 'LST_UPD_USR'
      codeVersion column: 'CD_VRSN'
   }
}

The class above shows how it is possible to override the default conventions in grails with its ORM Mapping DSL. However, grails currently does not support the merging of parent and subclass column mappings as it does with constraints. This leads to having to redefine parent column mappings in each of the subclasses. In an effort to be more DRY, there is a better way. By creating a static closure called addColumnMappings, subclasses can then invoke this method inside of their static mapping / columns closure:

class Country extends Base {
	String name
	String isoCode
	Integer sortOrder

	static constraints = {
		name(maxSize:32, nullable:true)
		isoCode(maxSize:2, nullable:true)
		sortOrder(nullable:true)
	}
	
	static mapping = {
		table 'COUNTRY'
		version column: 'RW_VRSN'
		sort 'sortOrder'
		columns {
			id column: 'C_ID'
			name column: 'NM'
			isoCode column: 'ISO_CD'
			sortOrder column: 'SRT_ORDR'
			
			Base.addColumnMappings(delegate)
		}
	}
}

Note the passing of the closure’s delegate. This allows the addColumnMappings closure to adjust its delegate internally and the code to execute as if it were inlined in calling class.

In conclusion, this method allows one to centralize common column mappings to a common domain base class. Although it requires manual intervention to invoke a helper method, it’s much better than the alternative approach of repeating yourself.

Advertisements