Monday, October 21, 2013

Grails GORM creating foreign key on runtime

GORM mappings let you configure pretty much anything you need in your Grails applications, but occasionally there are more obscure tweaks that aren’t directly supported, and in this case a custom Configuration class is often the solution.

By default Grails uses an instance of GrailsAnnotationConfiguration and the standard approach is to subclass that to retain its functionality and override the secondPassCompile() method.

Create this class (with an appropriate name and package for your application) in src/java or src/groovy:


package com.pritom

import org.codehaus.groovy.grails.orm.hibernate.cfg.GrailsAnnotationConfiguration
import org.hibernate.mapping.Column
import org.hibernate.mapping.ForeignKey
import org.hibernate.mapping.PersistentClass

/**
 * Created with IntelliJ IDEA.
 * User: pritom
 * Date: 21/10/13
 * Time: 10:41 AM
 * To change this template use File | Settings | File Templates.
 */
class GormConfiguration extends GrailsAnnotationConfiguration {
    boolean renamedForeignKeys = false

    @Override
    protected void secondPassCompile() {
        super.secondPassCompile()

        if (renamedForeignKeys) {
            return
        }

        renameForeignKeys()
        renamedForeignKeys = true
    }

    void renameForeignKeys() {
        classes.values().each { PersistentClass persistentClass ->
            /**
             * There must be a domain name 'SportUser' which has a field 'user_id'.
             */
            if(persistentClass.entityName.equalsIgnoreCase("com.pritom.SportUser")) {
                persistentClass.table.columns.each {
                    /**
                     * user_id: Column name to behave as a foreign key
                     * There must be a domain name 'User' to linked with it.
                     */
                    if(it.value.name.equalsIgnoreCase("user_id")) {
                        Column column = (Column) it.value;
                        /**
                         * ACL_USER: Foreign key constraints name
                         * com.pritom.User: Domain to make the foreign object
                         */
                        persistentClass.table.createForeignKey("ACL_USER", [column], "com.pritom.User")
                    }
                }
            }
        }
    }

    String createHumanReadableName(ForeignKey key) {
        "${key.columns.first().name}__${key.referencedTable.name}_fkey"
    }
}


What is left is to activate the code which is a one liner, we just have to add a configClass parameter to the datasource configuration:

    dataSource {
        configClass = com.pritom.GormConfiguration
        driverClassName = "com.mysql.jdbc.Driver"
        dbCreate = "create"
        url = "jdbc:mysql://localhost/grails_db"
        username = "grails"
        password = "grails"
    }  

Grails: renaming foreign keys constraints

In Grails if we create a domain class with association we will get a foreign key for the association. Let’s see how the foreign key gets named using an example using a many-to-one association.
Here is a simple domain class with an association to itself.

package gorm

class Category {
    Category child
}
The create table for this class will look like this:
CREATE TABLE category (
    id bigint NOT NULL,
    version bigint NOT NULL,
    child_id bigint,
    CONSTRAINT category_pkey PRIMARY KEY (id),
    CONSTRAINT fk302bcfeabee40a7 FOREIGN KEY (child_id)
        REFERENCES category (id)
)
We can see that the primary key gets a human readable name category_pkey but the name of the foreign key is fk302bcfeabee40a7. Not too readable. It would be nice if we could give the foreign key a human readable name too. What about <COLUMN>_<TARGET_TABLE>_fkey to make it similar to the name of the primary key?
It looks like Grails does not offer any GORM DSL sugar to customize the name of the foreign key. But naming it manually on each domain class may be a bit cumbersome anyway. It would be nice if we could make it use the <COLUMN>__<TARGET_TABLE>_fkey format automatically.

Here is a version in groovy implementing the format given above:

package com.pritom

import org.codehaus.groovy.grails.orm.hibernate.cfg.GrailsAnnotationConfiguration
import org.hibernate.mapping.PersistentClass
import org.hibernate.mapping.ForeignKey

/**
 * Created with IntelliJ IDEA.
 * User: pritom
 * Date: 21/10/13
 * Time: 10:41 AM
 * To change this template use File | Settings | File Templates.
 */
class GormConfiguration extends GrailsAnnotationConfiguration {
    boolean renamedForeignKeys = false

    @Override
    protected void secondPassCompile() {
        super.secondPassCompile()

        if (renamedForeignKeys) {
            return
        }

        renameForeignKeys()
        renamedForeignKeys = true
    }

    void renameForeignKeys() {
        classes.values().each { PersistentClass persistentClass ->
            persistentClass.table.foreignKeyIterator.each { ForeignKey key ->
                key.name = createHumanReadableName(key)
            }
        }
    }

    String createHumanReadableName(ForeignKey key) {
        "${key.columns.first().name}__${key.referencedTable.name}_fkey"
    }
}
It is not perfect. We do not check the length of the generated name and there are are probably other details I do not know anything about yet we have to take care of. But it it a start. :-)
The create table now look like this:
CREATE TABLE category (
    id bigint NOT NULL,
    version bigint NOT NULL,
    child_id bigint,
    CONSTRAINT category_pkey PRIMARY KEY (id),
    CONSTRAINT child_id__category_fkey FOREIGN KEY (child_id)
        REFERENCES category (id)
)
With a nicely named foreign key. :-)
What is left is to activate the code which is a one liner, we just have to add a configClass parameter to the datasource configuration:
    dataSource {
        configClass = com.pritom.GormConfiguration
        driverClassName = "com.mysql.jdbc.Driver"
        dbCreate = "create"
        url = "jdbc:mysql://localhost/grails_db"
        username = "grails"
        password = "grails"
    }  

Registering new bean classes in grails application programmatically by registerBeanDefinition

As part of amazing goodies of grails is the ability to extend your applications - registering new classes problematically is a breeze


GenericApplicationContext context = new GenericApplicationContext();

context.setParent(applicationContext);
/* OR */
context.setParent(grailsApplication.mainContext);

// Create class from string
Class clazz = new GrailsAwareClassLoader().parseClass(classString);

// First create a bean definition
def myBeanDef = new GenericBeanDefinition()

// Set bean class
myBeanDef.setBeanClass(clazz)

// Set scope
myBeanDef.setScope(BeanDefinition.SCOPE_SINGLETON)

context.registerBeanDefinition("beandefName", myBeanDef);

Wednesday, October 9, 2013

The Grails Hibernate Dynamica/On Fly CriteriaBuilder

The HibernateCriteriaBuilder is a great Grails feature, that you should know when working with the Grails framework. It is an alternative way to create dynamic or static queries with a groovy syntax based on the Criteria API of Hibernate.


Although there is some documentation about it in the Grails Pages, this does not demonstrate some advanced features like joins or pagination. There are several blog posts about the HibernateCriteriaBuilder, but most of them only show a simple example. I want to share my experience with a more complex example that demonstrates some features like:
  • query attributes of associated entities using joins
  • pagination parameters and their consequences
  • reuse of the criteria definition for similar domain classes

Query attributes of associated entities using joins

The example code uses a parcel instance as an example to carry parameter values to construct the criteria.

def example = new Parcel(parcelNumber: '1234%');

def crit = Parcel.createCriteria()
def parcels = crit.list {
  ilike("parcelNumber", example.parcelNumber)
  gt("parcelPrice", example.parcelPrice)
  recipient {
    ilike("lastName", example.recipient.lastName)
  }
  depot {
     address {
       ilike("city", example.depot.address.city)
       ilike("postalCode", example.depot.address.postalCode)
     }
  }
}

Dynamic query parameters

We extend the example to include the filtering only when the value of the example object is not empty.
def example = new Parcel(parcelNumber: '1234%');


def crit = Parcel.createCriteria()

def parcels = crit.list {
    if (example.parcelNumber) ilike("parcelNumber", example.parcelNumber)
    if (example.parcelPrice) gt("parcelPrice", example.parcelPrice)
    if (example.recipient) {
        recipient {
            if (example.recipient?.lastName) {
                ilike("lastName", example.recipient.lastName)
            }
        }
    }
    if (example.depot?.address) {
        depot {
            address {
                if (example.depot.address.city) {
                    ilike("city", example.depot.address.city)
                }
                if (example.depot.address.postalCode) {
                    ilike("postalCode", example.depot.address.postalCode)
                }
            }
        }
    }
}

Reuse of the criteria definition

We have 2 domain classes with the same properties: Parcel and ParcelArchive. We want to reuse the criteria definition, but we must be careful, because extracting it with your IDE ("extract method" refactoring) could break your code. You can use the groovy with.{closure} to extract the criteria:


// search Parcel

def example = new Parcel(parcelNumber: '1234%');

def crit = Parcel.createCriteria()
def parcels = crit.list {
    buildSearchCriteria(example, crit)
}

// search ParcelArchive

def example = new ParcelArchive(parcelNumber: '1234%');

def crit = ParcelArchive.createCriteria()
def parcels = crit.list {
    buildSearchCriteria(example, crit)
}

// criteria method for both

private static void buildSearchCriteria(def example, HibernateCriteriaBuilder crit) {
    crit.with {  
    // make the criteriaBuilder the receiver, because the definitions have been extracted a method
        if (example.parcelNumber) {
            ilike("parcelNumber", example.parcelNumber)
            if (example.parcelPrice) {
                gt("parcelPrice", example.parcelPrice)
            }
            if (example.recipient) {
                recipient {
                    if (example.recipient?.lastName) {
                        ilike("lastName", example.recipient.lastName)
                    }
                }
            }
            if (example.depot?.address) {
                depot {
                    address {
                        if (example.depot.address.city) i {
                            like("city", example.depot.address.city)
                        }
                        if (example.depot.address.postalCode) {
                            ilike("postalCode", example.depot.address.postalCode)
                        }
                    }
                }
            }
        }
    }
} 

Check and create criteria dynamically



package com.pritom
 
import grails.orm.HibernateCriteriaBuilder
import org.codehaus.groovy.grails.commons.GrailsApplication

class DataService { 
    /**
     * Now if you want to search schoolName against a Student you 
     * have to something like below:
     */
    def searchStudentBySchoolName() {
        def key = "discpline.school.name";
        def value = "Some Value";
        def criteria = Student.createCriteria();
        criteria.list {
            buildSearchCriteria(criteria, key.trim("."), value);
        }
    }

    /**
     * isDomainClass() ? Somehow check that the name is a domain class
     * Otherwise it is a property in the domain.
     * 
     * Implement getDomainClassByName() to get the domain class dynamically
     */

    def buildSearchCriteria(HibernateCriteriaBuilder criteria, String[] fields, String value) {
        criteria.with {
            for (Integer index2 = 0; index2 < fields.size() - 1; index2++) {
                if(fields[index2] != null && fields[index2 + 1] != null && isDomainClass(fields[index2])
                        && !isDomainClass(fields[index2 + 1])) {
                    def domainClass = getDomainClassByName(fields[index2].getMetaClass().getTheClass());
                    def domainCriteria = domainClass.createCriteria();
                    'in' (fields[index2], domainCriteria.list {
                        like(fields[index2 + 1], "%" + value + "%")
                    });
                } else if(fields[index2] != null && fields[index2 + 1] != null && isDomainClass(fields[index2])
                        && isDomainClass(fields[index2 + 1])) {
                    def domainClass = getDomainClassByName(fields[index2]).getMetaClass().getTheClass();
                    String current2 = fields[index2];
                    fields[index2] = null;
                    def domainCriteria = domainClass.createCriteria();
                    'in' (current2, domainCriteria.list{
                        buildSearchCriteria(domainCriteria, fields, value)
                    });
                }
            }
        }
    }
} 
 
/* The domain classes are below: */
class Student {
    Integer id;
    String studentId;
    Integer age;
     det belongsTo = [
        discipline: Discipline
    ]
} 
 
class Discipline {
    Integer id;
    String disciplineName; 
 
     def belongsTo = [
        school: School
    ]
}

class School {
    Integer id;
    String schoolName;
} 

http://www.viaboxxsystems.de/the-grails-hibernatecriteriabuilder

Tuesday, October 8, 2013

Groovy Goodness: Sorting a List of Map by specific Key

Sorting a Map by value with Groovy is very simple, due to the added ‘sort’ method on the Map interface.
def hashMap = "fields": {
      "field_3": {
        "order": 4
      },
      "field_2": {
        "order": 3
      },
      "field_1": {
        "order": 2     
    },
      "field_0": {
        "order": 6   
      }
    }

hashMap.sort { a , b -> a.value.order <=> b.value.order }

Sunday, October 6, 2013

Grails and dynamic controller names in UrlMappings

I was creating some REST web services and I needed to map a URL like “/api/1.0/recipes” to a controller called RecipesRestController. It took me a while to work out how to generate a dynamic controller name from the request path. The trick is not to use $controller, it doesn’t seem to work, so instead I renamed it $aController.


class UrlMappings {
    static mappings = {
        "/$controller/$action?/$id?"{
            constraints {
                // apply constraints here
            }
        }
        "/"(view:"/index")
        "500"(view:'/error')
        "/api/1.0/$aController"{
            controller={"${params.aController}Rest"}
            action=[GET:"list", POST:"save"]
        }
        "/api/1.0/$aController/$id"{
            controller={"${params.aController}Rest"}
            action=[GET:"show",  PUT:"update", DELETE:"delete"]
        }
    }
}

 

OR

 
import org.codehaus.groovy.grails.commons.ApplicationHolder

class UrlMappings {
  static mappings = {        
    for( controllerClass in ApplicationHolder.application.controllerClasses) {
      // Admin Controllers first
      if( controllerClass.name.startsWith("Admin")){
        // note... fixes the case so that AdminUserController maps to /admin/user
        "/admin/${controllerClass.name[5].toLowerCase() + controllerClass.name[6..-1]}/$action?/$id?" {
          controller = "admin${controllerClass.name[5..-1]}".toString()
        }
      }
    }
  }
}
 

OR


    "/admin/$controller/$action?/$id?"{
        controller = {
            def controllerName = (request.requestURI - request.contextPath).split('/')[2]

            // or
            //def controllerName = request.servletPath.split('/')[2]

            "${controllerName}Admin"
        }
    }

Friday, October 4, 2013

Java: Reflecting to Get All Classes in a Package, check and invoke method dynamically

It took me a little while to figure out that Java doesn't provide a way to reflect an entire package. In other words, there is no built-in way for me to dynamically retrieve a list of all the classes in a given package in Java through reflection. So I wrote my own method to do it.
Since it took a bit of googling and effort, I thought it would be nice to share the convenience method I wrote with the world. Enjoy:

Main.java under code.com.pritom package

package code.com.pritom;

import java.io.File;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;

public class Main {
    java.lang.reflect.Method method;

    public static void main(String[] args) throws Exception{
        Main main = new Main();
    }

    public Main() throws Exception{
        Class loadClass = getClassObjectByName("Load");
        System.out.println("Product Type Class: " + loadClass);
        System.out.println("Product Type Simple Name: " + loadClass.getSimpleName());
        System.out.println("Product Type Package Name: " + loadClass.getPackage().getName());
        System.out.println("Product Type Is Enum: " + loadClass.isEnum());

        try {
            method = loadClass.getMethod("println", String.class);
            System.out.println("Method Exists: println(String.class)");
            method.invoke(loadClass.newInstance(), "Pritom");
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }

        try {
            Class[] classes = new Class[2];
            classes[0] = String.class;
            classes[1] = Integer.class;
            method = loadClass.getMethod("println", classes);
            System.out.println("Method Exists: println(String.class, Integer.class)");
            method.invoke(loadClass.newInstance(), "Pritom", 26);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
    }

    private static HashMap allClassList = new HashMap();
    private static HashMap classObjectList = new HashMap();
    private static HashMap allClassObjectList = new HashMap();

    public static Class getClassObjectByName(String name) throws Exception {
        if(allClassObjectList.containsKey(name)) {
            return (Class) allClassObjectList.get(name);
        }
        String packageName = "code.com.pritom";
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        String path = packageName.replace('.', '/');
        Enumeration<URL> resources = classLoader.getResources(path);
        List<File> dirs = new ArrayList<File>();
        while (resources.hasMoreElements()) {
            URL resource = resources.nextElement();
            dirs.add(new File(resource.getFile()));
        }
        ArrayList<String> classes = new ArrayList<String>();
        for (File directory : dirs) {
            findClasses(directory, packageName);
        }
        if(allClassList.containsKey(name)) {
            Object object = getClassObjectByFullName(allClassList.get(name).toString()); /* Get the object */
            allClassObjectList.put(name, object); /* Storing for further use by class name only */
            return (Class) object; /* Return object */
        }
        throw new Exception("Invalid entity '" + name + "'");
    }

    private static void findClasses(File directory, String packageName) throws ClassNotFoundException {
        if (!directory.exists()) {
            return;
        }
        File[] files = directory.listFiles();
        for (File file : files) {
            if (file.isDirectory()) {
                findClasses(file, packageName + "." + file.getName());
            } else if (file.getName().endsWith(".class")) {
                allClassList.put(file.getName().substring(0, file.getName().length() - 6), packageName + "." + file.getName().substring(0, file.getName().length() - 6));
            }
        }
    }

    private static Object getClassObjectByFullName(String fullName) throws Exception {
        if (classObjectList.containsKey(fullName)) {
            return classObjectList.get(fullName);
        }
        try {
            ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
            Object object = Class.forName(fullName, true, classLoader);
            classObjectList.put(fullName, object); /* Storing by (package + class) name */
            return object;
        } catch (Exception ex) {
            throw new Exception(fullName + " does not appear to be a valid class.");
        }
    }
}

And Load.java under code.com.pritom package


package code.com.pritom;

public class Load {
    public void println(String text) {
        System.out.println("\tName: " + text);
    }
    public void println(String text, Integer age) {
        System.out.println("\tName: " + text +", Age: " + age);
    }
}


And output will be as follows:


Product Type Class: class code.com.pritom.Load
Product Type Simple Name: Load
Product Type Package Name: code.com.pritom
Product Type Is Enum: false
Method Exists: println(String.class)
	Name: Pritom
Method Exists: println(String.class, Integer.class)
	Name: Pritom, Age: 26