2008年3月29日土曜日

[Groovy>Grails] Customization, how to execute Native SQL statement on Grails environment

I created a new plugin, please refer to http://taapps-javalibs.blogspot.com/2009/04/grailsupgraded-plugin-for-grails-to.html

Description

Grails is an open-source web application framework that leverages the Groovy language and complements Java Web development. Grails has the database access layer like the RubyOnRails's Active Record, it is very usefull and powerful, but sometimes we need to execute Native SQL Statement. We sometimes need to use the fulltext search statement(match/against statement) if we use mysql database as the backend server. We sometimes need to tune the sql performance and enter the QueryHint into the sql statenent.In case of ORACLE Database, we can use query hint like "select /*index hint */ .....", in case of using mysql, we can use "use/force index" statement.
But now, we can use these native sql statement on Grails framework, Grails System doesn't allow to use native statement.(We can use HQL Query Language by using executeQuery, findAll or other methods, but HQL doesn't allow to use native sql via these methods). I customized grails and added the "executeNativeQuery" method to run the native sql statement from the Grails framework.

Installation
(I tested this customization only on mysql database environment.)
  1. Download grails-1.0.1-dist-b4.tar file, this file is customized based on Grails-1.0.1 source code.
    jar files: http://groups.google.com/group/taapps-sourcecode-libraries/web/grails-1.0.1-dist-b4.tar
  2. Extract downloaded tar file
    source files: http://taapps-sourcecode-libraries.googlegroups.com/web/grails-1.0.1-b4-src.tar
  3. Copy 8 *.jar files existing under dist directory to the GRAILS_HOME/dist directory
  4. Restart application server, defailt server is jetty
How to use this customization program
While restarting the application server, System adds the executeNativeQuery method into the Domain Class object dynamically. The executeNativeQuery method has some parameters as follows and return the List object including some DomainClass objects fetched from the database.
Examples how to use this method are as follows.(the name "IndTest" means my Domain Test class name for the sample code)
  • Type1, put the raw SQL statement into the grails hibernate layer.
    Table alias name should be "tbl", the "tbl" alias will be mapped to the Domain Class.

    def objList=IndTest.executeNativeQuery(
    "select {tbl.*} from ind_test as {tbl} where user_id=10000 and match(description) against('aaaaaaa')")

    objList.each{obj-> //executeNativeQuery will return the List object including the fetched domain objects
    .....
    }

  • Type2 executeNativeQuery has 3 arguments, SQL Statement String, Map object for mapping table alias, List object including the bind parameter values.

    def objList=IndTest.executeNativeQuery(
    "select {alias.*} from ind_test as {alias} where user_id=? and match(description) against(?)",
    [alias:IndTest], --> table alias "alias" is mapped to the IndTest Domain Class.
    [10000,"bbbbbb"]) --> 10000 is replaced with user_id's, bbbbbb is replaced with description's

  • Type3 executeNativeQuery has 3 arguments, SQL Statement String, Map object for mapping table alias, Map object including the bind parameter values.

    def objList=IndTest.executeNativeQuery(
    "select {alias.*} from ind_test as {alias} where user_id=:p1 and match(description) against(:p2)",
    [alias:IndTest],
    [p1:10000, p2:"cccccc"])

  • Type4 executeNativeQuery has 4 arguments, SQL Statement String, Map object for mapping table alias, Map object including the bind parameter values, Map object for using pagination

    def objList=IndTest.executeNativeQuery(
    "select {alias.*} from ind_test as {alias} where user_id=:p1 and match(description) against(:p2)",
    [alias:IndTest],
    [p1:10000,p2:"dddddd"],
    [max:10,offset:1])


  • executeNativeQuery method has other parameter patterns as follows.
    Query means the sql query statement string.
    EntityMap means the Map object including the table alias mapping(alias name and Domain class name).
    ParamList means the List object including the parameter values for the bind parameters.
    NamedMap means the Map object including the parameter values for the bind parameters.
    PageMap means the Map object for using pagination parameter.

    Arg Number :0 1 2 3
    Type 1 :String :Query
    Type 2 :String Map :Query,EntityMap
    Type 3 :String Map List :Query,EntityMap,ParamList
    Type 4 :String Map Map :Query,EntityMap,NamedMap
    Type 5 :String Map List Map :Query,EntityMap,ParamList,PageMap
    Type 6 :String Map Map Map :Query,EntityMap,NamedMap,PageMap


2008年3月8日土曜日

[Groovy>Grails] Error "failed to create task or type native2ascii" occures when running grails war file on Tomcat environment

Description
When running grails war file on Tomcat environment, following error message occures and fail to execute the program.

Error Message
 : Problem: failed to create task or type native2ascii
Cause: the class org.apache.tools.ant.taskdefs.optional.Native2Ascii was not found.
This looks like one of Ant's optional components.
Solution
System can not find the ant-nodeps.jar file in the classpath.
Get ant-nodeps.jar file and put this file into the $TOMCAT_HOME/webapps/[app name]/WEB-INF/lib/ and restart Tomcat process. ant-nodeps.jar file is including Apache ant file.

[Groovy>Grails]Database Dialect

Description
Grails Framework uses the Hibernate object/relational persistence and query service Tools between Database and Application. We sometimes have to compatible with various kind of Database. For example, We need to add the "create table" statement with Engine Type Dialect in case of using MySQL DB. It can handle the different dialects by using "dialect" variable in the DataSource.groovy configuration file.

References
  • Grails Home
    http://grails.codehaus.org/
  • Hibernate
    http://www.hibernate.org/
  • http://hartsock.blogspot.com/2007/11/grails-datasource-legacy-database-and.html
Solution
We can specify the hibernate database dialect class into the "dialect" variable in the grails-app/conf/DataSource.groovy Configuration file to handle the Database dialects. In case of using MySQL, we can add
"dialect=org.hibernate.dialect.MySQLInnoDBDialect" or "dialect=org.hibernate.dialect.MySQLMyISAMDialect"
into this file to handle the storage engines of database tables.
Please check the Dialect classes if you need to handle other database dialects.
http://www.hibernate.org/hib_docs/v3/api/org/hibernate/dialect/Dialect.html

Example of DataSource.groovy file under the grails-app/conf directory
  dbCreate = "create-drop" // one of 'create', 'create-drop','update'
url = "jdbc:mysql://localhost:5000/test_database
driverClassName = "com.mysql.jdbc.Driver"
username = "epic_admin"
password = "tskr0511epic"
//dialect = org.hibernate.dialect.MySQLInnoDBDialect
dialect = org.hibernate.dialect.MySQLMyISAMDialect