Getting Started with Grails and IntelliJ – Part 2

Posted by jt - 12/10/09 at 07:10 pm

At the very heart of Grails, are Domain Classes.  These are used to define the model of your application.  Via a few simple commands Grails can auotmagically create web screens over your domain classes to support basic CRUD (Create, Read, Update, Delete) operations.  This allows you to rapidly prototype applications.  As this series progresses, you will be amazed, at how little code you will need to write in order to build a functional web application.

I played with Hibernate back in its 2.x days.  Quite frankly, it sucked. It was a combination of verbose classes and XML hell.  To me, working with Hibernate was a bigger pain in the neck than it was worth.  I became convinced that Hibernate was for Java developers that didn’t understand SQL. Later, with the advent of JPA and annotations, we were able to say adios to much of that XML hell.  The developer experience was much improved.  But things still felt a bit clunky.

Grails brings us GORM – which is Grails/Groovy over the Hibernate 3 ORM engine.  Goodbye XML, goodbye annotations. And hello dynamic methods and finders.  So far, working with GORM is the most positive experience I have had using any ORM.  The more I work with it, the more things just make sense.  It doesn’t feel like you are working with some kludge to mask the database.

For my Log4Ora project I wish to refactor how the log levels are defined.  For simplicity, I’ve started calling any PL/SQL program unit in Oracle a module.  Therefore, we will need an entity to store module data.  Using IntelliJ, you can create a new Grails in a few simple mouse clicks.  Simply right click on the grails-app folder in your Grails project and select New/Grails/Grails Domain Class.

picture-8

You’ll be prompted to enter the name for the Domain Class.

picture-9

Enter the name, and click Ok.  After a few seconds, you will see the stub of your newly created class in the main window of the IDE.

picture-10

After a few seconds, you’ll see the stub of your new Domain Class.  What is important to understand here, is IntelliJ is running a standard Grails command for you.  From a command line in the root directory of the project you could have run this command:

grails create-domain-class Module

My Module class is going to be fairly simple. I just need to track the owner (aka schema) and object name.

To add these properties, I need to change my Module class as follows.

class Module {
 
    String owner, object
 
    static constraints = {
    }
}

If your familiar with Java, you might notice a couple oddities. Yes, in a single line, we are declaring two String properties, one called ‘owner’, and the second called ‘object’ and there is no need for semicolon to identify the end a line. In Groovy, you can optionally omit the semicolon.

I’m sure you’re looking at what *is* there in the above Grails code. Before we get too far, lets step back a second, and talk about what is *not* there.

Here is roughly the same entity expressed in JPA:

import javax.persistence.Entity;
import javax.persistence.Id;
 
@Entity
public class Module {
    @Id
    private int id, version;
    private String owner, object;
 
    public Module() { }
 
    public Module(int id, String owner, String object) {
        this.id = id;
        this.owner = owner;
        this.object = object;
    }
 
    public int getId() {
        return id;
    }
 
    public void setId(int id) {
        this.id = id;
    }
 
    public int getVersion() {
        return version;
    }
 
    public void setVersion(int version) {
        this.version = version;
    }
 
    public String getOwner() {
        return owner;
    }
 
    public void setOwner(String owner) {
        this.owner = owner;
    }
 
    public String getObject() {
        return object;
    }
 
    public void setObject(String object) {
        this.object = object;
    }
}

The JPA flavor requires imports for the annotations, while Grails dynamically takes care of common imports. Next you’ll see the JPA flavor has properties for id and version. You might be thinking, wait, the Grails version didn’t have these properties. Not true. They are added automatically for you. Next difference is specifying the constructors. No, this wasn’t in the Grails version, but again, automatically there. The getters and setters? Same story, added automatically for you.

The GORM magic does not stop there. In JPA, to retrieve an object by its primary key value you’d need to write roughly the following code:

Module module = em.find(Module.class, 1); //assuming PK value is 1

Under Grails:

def module = Module.findById(1)

No, our Module class did not include a method called findById. GORM provides dynamic finders for your Domain Classes for every property in the class. Dynamic finders are extremely powerful. Fully explaining them is beyond the scope of this post. But consider the following code snippet. Each statement is valid, and does roughly what is says.

def module = Module.findAllByOwner("FOO")
def module = Module.findAllByObjectLike("FOO")
def module = Module.findByOwnerIsNotNull()

My Log4Ora project is specific to Oracle, and I know within Oracle both owner and object are kept within VARCHAR2(30) fields. With this in mind, my web application should adhere to the same rules. Grails has a number of built in validators. In this case, we will be using the maxSize constraint. Grails will use this data when generating the web form, and Hibernate will use it to determine the column size when generating the database tables.

To add the constraints, change the Module class as follows:

class Module {
 
    String owner, object
 
    static constraints = {
      owner(maxSize:30, blank:false)
      object(maxSize:30, blank:false)
    }
}

In addition to adding MaxSize constraint, I also added the ‘blank’ constraint. Above I am setting it to false. This will prevent someone from submitting an owner of spaces.

Two other entities I will need are for Log Level and the Log Method. Log Level is simply the severity level of the log message (debug, info, error, etc). Log method is to define the logging method we wish to enable. Initially, Log4Ora will have 3 methods, Log to table, Log to JMS, and log to the dbms_output buffer.

I am defining my LogLevel Domain class as follows:

class LogLevel {
    String levelCode
 
    static constraints = {
      levelCode(maxSize:30, blank:false, unique:true)
    }
}

The only thing new here is the use of the unique constraint. This will ensure the uniqueness of the log level code value.

My LogMethod domain class is defined as follows:

class LogMethod {
    String methodCode, methodDescription
 
    static constraints = {
      methodCode(maxSize:30, blank:false, unique:true)
      methodDescripition(maxSize:100)
    }
 
    def toString = {
      return methodCode
    }
}

In this case, I’ve added a toString method. This method will return the method code for the object. I’ve read this will be used by the Grails scaffolding. We’ll see what happens later when we build the web pages.

We’re going to need two more entities. One to defined the log levels enabled for a module, and second to define the log method to use for the module / log level.

To define the Module Level relationship, I created the following domain class:

class ModuleLevel {
 
    Module module
    LogLevel level
    String loggingEnabled
 
    static constraints = {
      loggingEnabled (inList:['Y', 'N'])
    }
}

The entity to define the logging methods is defined as:

class LogMethod {
    String methodCode, methodDescription
 
    static constraints = {
      methodCode(maxSize:30, blank:false, unique:true)
      methodDescription(maxSize:100)
    }
 
    def toString = {
      return methodCode
    }
}

With these last two classes, this is the first time we’ve defined relationships between our entities. This is a pretty large subject. For now, I just want to focus on ownership. In my model here, ‘Module’ will own related records for ModuleLevel, and ModuleLevelMethod. If I delete a module record, I want all of its children to be deleted too. As it stands right now, I would have to delete the kids before deleting the parent. With a couple minor changes, I can instruct GORM about the behavior I want.

In Module and ModuleLevel, I need to add the ‘hasMany’ property. My resulting classes now look like:

class Module {
 
    String owner, object
 
    static constraints = {
      owner(maxSize:30, blank:false)
      object(maxSize:30, blank:false)
    }
 
    static hasMany = [moduleLevels:ModuleLevel]
}
 
---------
class ModuleLevel {
 
    Module module
    LogLevel level
    String loggingEnabled
 
    static constraints = {
      loggingEnabled (inList:['Y', 'N'])
    }
 
    static hasMany = [moduleLevelMethod:ModuleLevelMethod]
}

To help you visualize your model, IntelliJ has a nice graphical view. Be selecting the ‘domain classes dependencies’ tab at the bottom left of the editor window, I can see my model now looks like this:

picture-14

Next up in the series we’ll take a look at creating some unit and integration tests.

Share/Save/Bookmark

Leave a Reply