register
other register

Wednesday, July 29, 2009

GORM Lazy

If we have the domain relations (one Order has many Items, and one Item can appear in many Orders) modelled below:

Order --* LineItem *-- Item

If we want the toString() method of the LineItem class to return its relation classes attributes, then that's where lazy strategy comes in handy.


class LineItem {

// relation
Order order
Item item

static mapping = {
columns {
order lazy: false
item lazy: false
}
}

String toString() {
return "Order no: $this.order.id, Item: $this.item.name"
}
}

Tuesday, July 21, 2009

Get Session in Grails Service

In Grails, session variable is not available in service. But

import org.springframework.web.context.request.RequestContextHolder

def user = RequestContextHolder.currentRequestAttributes().getSession()?.user


Note: the RequestContextHolder is genuinely a normal web request. If the request is a scheduled job (using Quartz plutin), not a web request, then there will be exceptions like below:

=============================
2009-07-21 00:00:00,269 INFO [core.JobRunShell] Job GRAILS_JOBS.ExpireJob threw a JobExecutionException:
org.quartz.JobExecutionException: No thread-bound request found: Are you referring to request attributes outside of an actual
web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web
request and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this
case, use RequestContextListener or RequestContextFilter to expose the current request. [See nested exception: java.lang.Ill
egalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request,
or processing a request outside of the originally receiving thread? If you are actually operating within a web request and st
ill receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this case, use Req
uestContextListener or RequestContextFilter to expose the current request.]
at org.codehaus.groovy.grails.plugins.quartz.GrailsJobFactory$GrailsTaskClassJob.execute(GrailsJobFactory.java:81)
at org.quartz.core.JobRunShell.run(JobRunShell.java:202)
at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:525)
Caused by: java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of
an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating wit
hin a web request and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortle
t: In this case, use RequestContextListener or RequestContextFilter to expose the current request.
at org.springframework.web.context.request.RequestContextHolder.currentRequestAttributes(RequestContextHolder.java:12
2)
at org.springframework.web.context.request.RequestContextHolder$currentRequestAttributes.call(Unknown Source)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:43)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:116)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:120)
at EmailerService.sendEmails(EmailerService.groovy:64)
at EmailerService$sendEmails$0.call(Unknown Source)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:43)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:116)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:124)
at ExpireJob$_execute_closure1.doCall(ExpireJob.groovy:38)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:86)
at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:234)
at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1061)
at groovy.lang.ExpandoMetaClass.invokeMethod(ExpandoMetaClass.java:910)
at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:892)
at groovy.lang.Closure.call(Closure.java:279)
at groovy.lang.Closure.call(Closure.java:292)
at org.codehaus.groovy.runtime.DefaultGroovyMethods.each(DefaultGroovyMethods.java:1165)
at org.codehaus.groovy.runtime.DefaultGroovyMethods.each(DefaultGroovyMethods.java:1141)
at org.codehaus.groovy.runtime.dgm$87.invoke(Unknown Source)
at org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite$PojoMetaMethodSiteNoUnwrapNoCoerce.invoke(PojoMetaMethodSi
te.java:270)
at org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite.call(PojoMetaMethodSite.java:52)
============================


If you are logging this scheduled job, then do:

try {
def user = RequestContextHolder.currentRequestAttributes().getSession()?.user
if (user != null) {
log.info("Email: ${mail.dump()}, action: sendEmails by ${user} on ${new Date()}")
}
else {
log.info("Email: ${mail.dump()}, action: sendEmails by system on ${new Date()}")
}
}
catch (java.lang.IllegalStateException e) {
log.info("Email: ${mail.dump()}, action: sendEmails by system on ${new Date()}")
}

Friday, July 03, 2009

Select Column Name with Space

If the column name has space in it, then use backtick operator like this `column name`

Migrate Access to MySQL to Fit in Grails

1. Access Tables Preparation

Open the Access database and modify the table names and column names with the following convention:
  • Modify the table names to table_name (e.g. account_manager) format if necessary. GRAG will generate a domain class named: TableName.
  • Modify column names to column_name (e.g. contact_address) format if necessary. GRAG will generate a field named: columnName.
  • Create an autoNumber id as the primary key if the table doesn't have one
  • If the table's primary key is not named id, then change it to id. GRAG will generate an id field with the mapping like: id generator:'identity', column:'id'
  • Modify foreign keys in other tables as table_name_id. GRAG will create a relation for this.

2. Migrate Access to MySQL


MySQL Migration Toolkit to migrate MS Access to MySQL (http://dev.mysql.com/downloads/gui-tools/5.0.html). It is an automatic process with step by step instructions. The tool will generate dbCreate.sql and dbInserts.sql, remember to save them. Then:
  • Modify the dbCreate.sql to add foreign keys which GRAG will create relations for the generated domain class, and add version int(10) field, otherwise a 'version false' statement will be generated in the mapping, because this isn't available by default for legacy databases
  • Re run the dbCreate.sql script to re-generate the tables.
  • Re run the dbInserts.sql script to populate the data.

3. Generate the Domain Classes

Then you can start using Grails Application Generator (GRAG) (http://grag.sourceforge.net/) to generate the domain models. It is not perfect, but at least it saves some manul typings.

Open the generated domain classes to:
  • check the generated field to see wheter it comforms to the lowerCaseCapital format (e.g. contactAddress)
  • If the id is an auto_increment field, then comment out the id field, and comment out id generator:'identity', column:'id' statement.
  • If the id is a user assigned value field (e.g. characters), then don't comment out the id field. Also, change the id generator in the mapping to: id generator:'assigned', column:'id'
  • modify relations if necessary (i.e. hasMany, belongsTo, or association like: OtherDomain otherDomain)
  • comment out constraint for a start
  • modify the toString method to return some meaningful value.

4. Configuration

Change dbCreate to 'update' in the DataSource.groovy so that the data

5. Testing

runt he "grails console", then for each domain class, test it with the following script:

def a = DomainClass.get('KK')
a.dump()

6. Generate Skeletons Code

grails generate-all *

This will generate the controllers and gsp view pages for all the domain classes.

7. Modify assigned id field for controller and gsp

If the id is a user assigned id rather than an auto incremented id, then for the controller, explicitly assign the id:



def save = {
def nameInstance = new Name(params)
nameInstance.id = params.id
if(! nameInstance.hasErrors() && nameInstance.save()) {
...
}
}
For the create.gsp page, add the id input field:

            
<tr class='prop'>
<td valign='top' style='text-align:left;' width='20%'>
<label for='id'>ID:</label>
</td>
<td valign='top' style='text-align:left;' width='80%'
class='${hasErrors(bean:nameInstance,field:'id','errors')}'>
<input type='text' name='id' value='${nameInstance?.id}' />
</td>
</tr>

There is also a very good post regarding Hosting Grails to your Legacy Database