BT

Facilitating the Spread of Knowledge and Innovation in Professional Software Development

Write for InfoQ

Topics

Choose your language

InfoQ Homepage Articles Grails + EJB Domain Models Step-by-Step

Grails + EJB Domain Models Step-by-Step

Bookmarks

Ruby on Rails continues to turn many heads in the software engineering world, but enterprise shops remain relatively unconvinced. Why? We wonder, "How is some framework built on a scripting language going to fit into my enterprise application?!" In the case of Ruby on Rails, the typical argument concerns the lack of support for enterprise services (e.g., distributed transactions, messaging, etc.). To many businesses, a platform without those services simply isn't an option.

Grails aims to address those concerns and legitimize rapid application development (RAD) for the enterprise. Built on Groovy, Grails offers seamless integration with Java. It provides direct access to those enterprise services that your business depends on, and at the same time adds powerful dynamic language constructs to your toolkit.

As one impressive example of its enterprise integration abilities, Grails let's you quickly and easily build a web application backed by your existing EJB3 entity beans. But, it doesn't stop there. Grails gives your entity beans a hearty shot of steroids, but does so completely dynamically, without altering your EJB source code in any way. Grails Object Relational Mapping (GORM) is built on Hibernate 3 (but will eventually offer support for the Java Persistence API) and uses Groovy's Meta Object Protocol (MOP) to add all sorts of handy dynamic methods to your otherwise-static entity beans. And those methods are not only accessible from Grails and Groovy; your Java code can invoke those methods as well! We suddenly have all the enterprise-level capabilities of JEE/EJB3 and the benefits of RAD web application development!

So, let's see what it takes to build a Grails application backed by EJB3 entity beans. In the steps below, we'll create a new Grails application, import entity beans into the application, generate the scaffolding to quickly build a default web interface for the entity beans, and then explore a few of the handy dynamic methods Grails adds to the entity beans.

First we need an EJB3 application to start with. (Well, Grails doesn't require that you have an EJB3 application as your starting point. However, the general assumption of this article is that you're interested in incorporating RAD web development into your EJB3 project.) Let's assume we have some EJB3 entity beans that model the employees (EmployeeBean) in a company and the computers (ComputerBean) assigned to those employees. (Be sure to see the Resources section if you run into any issues along the way. There you'll find the complete source code for the Grails application, a sample JEE application that uses these entity beans, and other useful artifacts.)

The following two tables support our entity beans. (We'll use MySQL 5.0 for this example. You can use this script to create a new database named ejb3example populated with these tables.)

And let's have a quick look at our company's motley lineup of worker bees and their hardware.

How long should it take to build a web user interface that interacts with this data and that integrates with our existing code base? Well, it shouldn't take us long, but we've sure come to accept that this process requires significant effort. With Grails, it doesn't have to be that way.

Step 1 - Install Grails

Since EJB3 is dependent upon JDK 5, you'll want to make sure that you have JDK 5 installed and that your JAVA_HOME environment variable points to your JDK 5 installation.

Follow these quick steps to install Grails on your system. (This article uses Grails 0.2.1 - the current stable release as of this writing.) (Also, if you're using a *nix system, you may need to check out this thread if you encounter installation issues.)

Step 2 - "Hello, Grails!"

  1. From a command prompt, navigate to the directory where you want to create your Grails application. Then, enter grails create-app. When asked for an application name, enter ejb3_grails.
    jMac:~/dev jason$ grails create-app
    ...
    create-app:
        [input] Enter application name:
    ejb3_grails
    ...
    BUILD SUCCESSFUL
    Total time: 4 seconds
    
  2. Just to make sure all is well with our environment, let's start our application. Move into the new directory created for our application, and then enter grails run-app to start the application.
    jMac:~/dev jason$ cd ejb3_grails
    jMac:~/dev/ejb3_grails jason$ grails run-app
    ...
    run-app:watch-context:
    
  3. The application is now waiting for our requests. Open your browser to http://localhost:8080/ejb3_grails/, and you should see this friendly message welcoming you to Grails.

Step 3 - Import the Entity Beans

  1. Grails comes pre-packaged with HSQLDB, but since we're using MySQL, we have a few quick steps to tell Grails how to talk to our database. First, download the Java MySQL driver from http://www.mysql.com/products/connector/j/. I opted for the current production-ready version which, as of this writing, is 3.1.13.

  2. Open the zip file and extract the mysql-connector-java-3.1.13-bin.jar file into the lib directory of your Grails application - in our case, that's ejb3_grails/lib. (Please note that the exact name of the JAR file may differ based on the version of the driver you downloaded.)

  3. Now we're ready to tell Grails where to find our database. Open ApplicationDataSource.groovy in your editor of choice, and modify it to match the settings below. You'll find this file in ejb3_grails/grails-app/conf/. (Note that you'll need to change the username and password to the appropriate values for your MySQL account.)

    import org.codehaus.groovy.grails.orm.hibernate.cfg.GrailsAnnotationConfiguration
    
    class ApplicationDataSource {
       def configClass = GrailsAnnotationConfiguration.class
       boolean pooling = true
       //String dbCreate = "create-drop" // one of 'create', 'create-drop','update'
       String url = "jdbc:mysql://localhost/ejb3example"
       String driverClassName = "com.mysql.jdbc.Driver"
       String username = "ejb3example"
       String password = "ejb3example"
    }
    

    In addition to specifying the connectivity settings, we also need to define the configClass member to allow Grails to support the annotations used in our entity beans.

    Lastly, we want to comment out the dbCreate setting. This setting allows you to have Grails update your database schema at runtime to synchronize it with your domain classes. While this is a powerful option, we simply don't need it for this example. By commenting out this property, we're instructing Grails to leave the schema as is.

  4. Next, we need to copy the entity beans - EmployeeBean and ComputerBean - into our Grails project. Grails looks for Java classes in the src/java directory. Be sure to create the full directory structure matching the package name for these classes.

    jMac:~/dev/ejb3_grails/src/java/com/jasonrudolph/ejb3example/entity jason$ ls
    ComputerBean.java       EmployeeBean.java
    

    Long-term, you'll want to make sure that these files stay in sync with the official copy of these classes (in the source tree of your JEE project). To do so, you could easily instruct your build script to copy these files from the JEE project into the Grails project at build time.

  5. Grails allows us to work with any Java classes, but we want Grails to give special treatment to these particular Java classes (i.e., our entity beans). We want Grails to recognize these classes as our domain classes and to provide all the ORM and dynamic method goodness that comes with a Grails domain class. To do so, we'll register these classes by adding the following hibernate.cfg.xml file to the hibernate directory of our application.
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE hibernate-configuration PUBLIC
    		"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
    		"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
    
    <hibernate-configuration>
        <session-factory>
            <mapping package="com.jasonrudolph.ejb3example.entity" />
            <mapping class="com.jasonrudolph.ejb3example.entity.EmployeeBean" />
            <mapping class="com.jasonrudolph.ejb3example.entity.ComputerBean" />
        </session-factory>
    </hibernate-configuration>
    

Step 4 - Generate the Scaffolding

Now we're ready for the real time savings to begin. There is just no escaping most of the things we've done so far. (No matter how smart your framework is, you'll always have to tell it where to find your database.) However, building a good and functional starting point for your user interface is no longer a manual task.

  1. Make sure you're in the project's root directory - in our case, it's ejb3_grails. Then, enter grails generate-controller. When asked for the domain class name, enter the fully-qualified class name for our first entity bean - com.jasonrudolph.ejb3example.entity.EmployeeBean.
    jMac:~/dev/ejb3_grails jason$ grails generate-controller
    ...
    input-domain-class:
        [input] Enter domain class name:
    com.jasonrudolph.ejb3example.entity.EmployeeBean
    ...
    [java] Generating controller for domain class
    [com.jasonrudolph.ejb3example.entity.EmployeeBean]
         [java] Controller generated at ./grails-
    app/controllers/EmployeeBeanController.groovy
    
    BUILD SUCCESSFUL
    Total time: 10 seconds
    
  2. Now that we have the controller, let's generate the corresponding views. Enter grails generate-views. When asked for the domain class name, enter com.jasonrudolph.ejb3example.entity.EmployeeBean.
    jMac:~/dev/ejb3_grails jason$ grails generate-views
    ...
    input-domain-class:
        [input] Enter domain class name:
    com.jasonrudolph.ejb3example.entity.EmployeeBean
    ...
    [java] Generating views for domain class [com.jasonrudolph.ejb3example.entity.EmployeeBean]
         [java] Generating list view for domain class
    [com.jasonrudolph.ejb3example.entity.EmployeeBean]
         [java] list view generated at /Users/jason/dev/ejb3_grails/./grails-
    app/views/employeeBean/list.gsp
         [java] Generating show view for domain class
    [com.jasonrudolph.ejb3example.entity.EmployeeBean]
         [java] Show view generated at /Users/jason/dev/ejb3_grails/./grails-
    app/views/employeeBean/show.gsp
         [java] Generating edit view for domain class
    [com.jasonrudolph.ejb3example.entity.EmployeeBean]
         [java] Edit view generated at /Users/jason/dev/ejb3_grails/./grails-
    app/views/employeeBean/edit.gsp
         [java] Generating create view for domain class
    [com.jasonrudolph.ejb3example.entity.EmployeeBean]
         [java] Create view generated at /Users/jason/dev/ejb3_grails/./grails-
    app/views/employeeBean/create.gsp
    
    BUILD SUCCESSFUL
    Total time: 11 seconds
    
  3. Repeat this process to generate the controller and views for the other entity bean (i.e., com.jasonrudolph.ejb3example.entity.ComputerBean).

  4. Now let's run our application and see just how much we can get for such little effort. Enter grails run-app, and we're ready to go. Open your browser to http://localhost:8080/ejb3_grails/.

  5. OK. We have our two controllers. And, as is suggested by the text here, we'll eventually want to replace this page with our own custom landing page. For now, let's move on to the EmployeeBeanController.

We see most of the information we'd want here. Obviously the Computers column needs some work, but otherwise this page can probably be ready to go with just a few cosmetic changes.

Take some time to look around the application and see what we have so far. Try to create a new employee, edit an employee, and then (if you're feeling particularly powerful) terminate one of those poor souls. Try out similar features for managing computers. Along the way, make note of the things you'd want to change as well as the things we need to change. At the same time, be sure to consider those things that already meet your needs. Those are the features you got for free!

...

Done? OK. Here's my take on the list of things we need, and then we can talk about those nice-to-have items later.

  • Relationship Management - The scaffolding clearly tries in this area, but it just doesn't meet our needs. Luckily, this is pretty easy to remedy. (Heck. Even the Ruby on Rails scaffolding doesn't give you relationship management for free!)
  • Validation - It's just missing. Plain and simple. But, we can quickly add that as well.

That's it. If we can implement proper relationship management and validation, we will have a fully-functioning web app to manage our entity beans. After that, the rest is just gravy. (Though we'll be sure to sample some of that tasty gravy before we're finished.)

Step 5 - Add Relationship Management

What do we expect from the application in regards to relationship management? Well, I'd say we should reasonably be able to ...

  • View all computers assigned to an employee.
  • View the details for a single computer (including its assignment status).
  • Create, update, and delete a computer (including its assignment status).

Ready? Let's get started.

  1. It doesn't really make sense to list computers on the Employees page, so let's remove that column. Open grails-app/views/employeeBean/list.gsp, and remove that column. Now just refresh the browser, and verify the updated page.

  2. Next, click the Show link to view the details for an employee.

    At the very least, we need to clean up the text listed for each computer. But perhaps we don't want to see the computers directly on this page at all. Instead of showing the computers on this page, let's include a link to this employee's list of computers.

    Open the template for this page (i.e., grails-app/views/employeeBean/show.gsp), and remove the row that currently displays the employee's computers. Then, add the following row to link to a separate page showing the computers for this employee.

    <tr class="prop">
        <td colspan="2" align="left" class="name">
    	   <g:link controller='computerBean' action='showComputersByEmployee'
    	           id='${employeeBean.id}' >Show Computers</g:link>
        </td>
    </tr>
    
    

    We're using the Grails tag library to help us out here. The link tag will generate a link to the ComputerBeanController and invoke a new action that we need to define called showComputersByEmployee. That link will also include the ID for the employee in question.

    Let's refresh the browser and see our changes.

    OK. We have our link. Now we need to define the new action for that link. Open grails-app/controllers/ComputerBeanController.groovy in your editor. Because the new action will search for computers by employee, we first need to add an import statement for the EmployeeBean class.

    import com.jasonrudolph.ejb3example.entity.EmployeeBean

    Then we just add the new action.

    def showComputersByEmployee = {
        render(view:'list', model:[ computerBeanList:
    	ComputerBean.findAllByEmployeeBean(EmployeeBean.get(params.id)) ])
    }
    

    This action gives us a good look at just how much you can say in Groovy (and Grails) in a few concise lines. In those few lines, we're telling Grails that any calls to showComputersByEmployee should ...

    • Get the employee ID from the request using params.id
    • Get the EmployeeBean for that employee ID using the EmployeeBean#get method
    • Find all computers assigned to that employee using the ComputerBean#findAllByEmployeeBean method
    • Put the results in an object named computerBeanList
    • render the view, using the computerBeanList object as the model, in a template named list

    Remember defining the get method in EmployeeBean and the findAllByEmployeeBean method in ComputerBean? No? You're right. Those methods are just a small sampling of the many dynamic methods that Grails provides for your domain classes. We'll explore these items more later. In the meantime, we're ready to click on the Show Computers link.

    We're getting close. We still need to change the text in the Employee Bean column. We certainly want something with a bit more human meaning here.

    Also, we might prefer to identify the employee to which these computers belong. We're currently reusing the list template (generated as part of the scaffolding for the ComputerBean), and that template is designed primarily to list all computers. However, we could always define a separate template for displaying this subset of computers or perhaps make the list template more dynamic in order to support both scenarios. For now, we'll tuck this feature away as nice-to-have but not essential.

    That should complete all the changes we need for managing employees. Now we just need to clean up the computer management features.

  3. Since we're here, let's clean up the computer list template. Instead of the current text in the Employee Bean column, let's change it to display the employee's network ID. Open grails-app/views/computerBean/list.gsp. Find the Groovy scriptlet that renders the text in the Employee Bean column ...
    ${it.employeeBean}
    

    ... and change it to render the employee's network ID.

    ${it.employeeBean.networkId}
    

    Refresh the browser and let's see how it looks. (The Employee column seemed a little awkward between the Brand and the Model columns, so I switched things around a bit. Feel free to do the same.)

    The list template is complete. On the show template.

  4. Click the Show link, and let's assess what we need to change.

    It looks like we just need to change the text for the employee link. We're almost pros at this by now. So, open grails-app/views/computerBean/show.gsp, and find the scriptlet that renders the current text for the link.

    ${computerBean?.employeeBean}
    

    Just like we did for the list template, change the code to display the employee's network ID.

    ${computerBean?.employeeBean.networkId}
    

    Let's refresh the browser again and verify our changes. (Again, the Employee row seemed a little awkward between the Brand and the Model rows, so you're welcome to rearrange the rows as you see fit.)

  5. Let's move on the edit functionality.

    We're starting to see a theme here. We need to change the select box to provide a list of network IDs. Find the <g:select> tag in grails-app/views/computerBean/edit.gsp, and tweak it like so.

    <g:select optionKey="id"
        from="${com.jasonrudolph.ejb3example.entity.EmployeeBean.list()}"
        name='employeeId'
        optionValue='networkId'
        value='${computerBean.employeeBean?.id}'>
    </g:select>
    
    

    By adding the optionValue parameter to the tag, the text in the select box has taken on a more meaningful form.

    The view is correct now, but the update functionality requires a bit of effort outside of the view as well. We also need to enhance the controller (i.e., ComputerBeanController.groovy). If the user changes the employee associated with a computer, we need to make sure we persist those association changes properly. In other words, we need to disassociate the computer from the current employee and assign it to the new employee. The enhanced update method requires just a few additional lines of code.

    def update = {
        def computerBean = ComputerBean.get( params.id )
        if(computerBean) {
               if (computerBean.employeeBean) {
                      computerBean.employeeBean.computers.remove(computerBean)
               }
    
               computerBean.properties = params
    
               def employeeBean = EmployeeBean.get(params.employeeId)
               employeeBean.computers.add(computerBean)
               computerBean.employeeBean = employeeBean
    
               if(computerBean.save()) {
                      redirect(action:show,id:computerBean.id)
               }
               else {
                      render(view:'edit',model:[computerBean:computerBean])
               }
        }
        else {
               flash.message = "ComputerBean not found with id ${params.id}"
               redirect(action:edit,id:params.id)
        }
    }
    
    

    Save your changes, and we're ready to try it out. Let's change the brand for this computer - it's a Lenovo now - and let's reassign it to John Doe.

  6. Of course, we also need to be able to add new computers to our inventory. So, let's click on New ComputerBean.

    After our last change to the Edit page, we're well-qualified to clean up this page. We need to make the same adjustments to the select box. Open grails-app/views/computerBean/create.gsp, adjust the <g:select> tag, and refresh your browser.

    <g:select optionKey="id"
        from="${com.jasonrudolph.ejb3example.entity.EmployeeBean.list()}"
        name='employeeId'
        optionValue='networkId'
        value='${computerBean.employeeBean?.id}'>
    </g:select>
    
    

    As we saw for the edit functionality, we need to make a slight enhancement to the controller. When we create a new computer, we need to make sure we assign it to an employee before we save it. Edit ComputerBeanController.groovy to include this updated save method.

    def save = {
        def computerBean = new ComputerBean()
        computerBean.properties = params
    
        def employeeBean = EmployeeBean.get(params.employeeId)
        employeeBean.computers.add(computerBean)
        computerBean.employeeBean = employeeBean
    
        if(computerBean.save()) {
               redirect(action:show,id:computerBean.id)
        }
        else {
               render(view:'create',model:[computerBean:computerBean])
        }
    }
    

    Back in the browser, we're ready to create a new computer. Fill in the empty fields and create away.

    Jane now has a hot new laptop.

  7. And the last piece we need is the delete functionality. We don't need any template changes this time. We just need to add one line to the controller. When we delete a computer in ComputerBeanController.groovy, we also need to remove the computer's association to the employee. The fourth line below will take care of that.

    def delete = {
        def computerBean = ComputerBean.get( params.id )
    
        if(computerBean) {
               computerBean.employeeBean.getComputers().remove(computerBean)
               computerBean.delete()
               flash.message = "ComputerBean ${params.id} deleted."
               redirect(action:list)
        }
        else {
               flash.message = "ComputerBean not found with id ${params.id}"
               redirect(action:list)
        }
    }
    

    It looks like Jane's new MacBook has been recalled. Shall we delete it?

    And there we have it! In just a few quick steps, we now have a functioning web application built on top of our entity beans. It can still use a little polish, of course. At the very least, it's completely sufficient as a working prototype. Better yet, it's a prototype that is fully capable of growing into the finished product.

Step 6 - Define Validation Rules

One thing we certainly need in the near term is some validation logic. Fortunately, Grails offers a very convenient declarative validation mechanism. For each of our entity beans, we simply need to add a Groovy script to define the appropriate constraints. By convention, Grails looks for a script with the same name as your domain class but ending with the word "Constraints" (e.g., EmployeeBeanConstraints.groovy).

So, let's define the constraints for each of our entity beans. At a minimum we need our constraints to match our data model. We'll also add in some basic business validation. For example, the database requires that network IDs not exceed eight characters in length, but we'll also enforce our business rule that a network ID must be no fewer than six characters in length.

Grails looks for the constraints declarations in the same directory as our domain class. For the EmployeeBean class, we need to create a file named EmployeeBeanConstraints.groovy and place it in ejb3_grails/src/java/com/jasonrudolph/ejb3example/entity/. The file should include the following rules:

constraints = {
      networkId(length:6..8,blank:false,unique:true)
      firstName(maxLength:20,blank:false)
      lastName(maxLength:20,blank:false)
      startDate(nullable:false)
}

Next, we'll define the constraints for the ComputerBean class. The file goes in the same directory, and we need to name it ComputerBeanConstraints.groovy.

constraints = {
      serialNumber(maxLength:20,blank:false)
      brand(maxLength:20,blank:false)
      model(maxLength:20,blank:false)
}

To make these constraints take effect, we need to restart the application. In the console where you started the application, enter Control-C to stop the application. Then, enter grails run-app, and we're ready to test the validation.

Now, if we try to sneak in a new employee with an invalid network ID, our application knows not to accept it.

Naturally we'll want change the error message to something more business-focused and less technical. Grails provides a message resources file for you to define your own custom error message.

The constraints we defined represent just a small sample of the validation that Grails offers. Be sure to check out the Grails documentation for the complete list of available constraints.

Step 7 - Get Dynamic

Everything up to this point has shown us that we can build web applications more quickly than we might have thought, and we've seen some impressive features of this up-and-coming framework. That said, here's where the real magic begins. Recall our brief mention of dynamic methods back when we added the ability to see all computers assigned to a user? Let's take a deeper at look at this feature.

Once we have some basic CRUD functionality like this, one of the first requests we get is, "I need a way to see all employees by _______." Or, "Show me all computers made by _______ with serial numbers starting with _______." These are reasonable enough requests, but how much effort typically goes into building these simple reports? Too often it requires a new method in some session bean, a new mapping in one of my web framework's configuration files, perhaps even some SQL, etc. Not only can we get by without those things, we can do so and be ready to quickly adapt to the next change.

For starters, let's say we need to be able to find all employees by last name. We'll add a new menu option to the Employee List page to take us to our new query page. Add this block to the menu section of grails-app/views/employeeBean/list.gsp.

<span class="menuButton">
	<g:link action="search">Search Employees</g:link>
</span>

Clicking this menu option will invoke a new action in our controller named search. We don't need that action to do anything more than render our search input page. So, add an empty method in EmployeeBeanController.groovy to accept these requests.

def search = {
}

This empty action tells Grails that search is a valid action for this controller. When Grails receives a request for this action, it will simply look for a template with the same name (i.e., search.gsp) in grails-app/views/employeeBean/ and render its content.

Since we really just want a simple form with input fields for an employee, we can use the create.gsp template as a good staring point. When we're finished, our new search.gsp template should look like this:

<html>
    <head>
         <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
         <meta name="layout" content="main" />
         <title>Search Employees</title>
    </head>
    <body>
        <div class="nav">
            <span class="menuButton">
                <a href="${createLinkTo(dir:'')}">Home</a></span>
            <span class="menuButton">
            <g:link action="list">EmployeeBean List</g:link></span>
        </div>
        <div class="body">
           <h1>Search Employees</h1>
           <g:form action="showSearchResults" method="post" >
               <div class="dialog">
               <table>
                    <tr class='prop'>
                        <td valign='top' class='name'>
                            <label for='lastName'>Last Name Like:</label>
                        </td>
                        <td valign='top' class='value'>
                            <input type='text' name='lastName' value='' />
                        </td>
                   </tr>
               </table>
               </div>
               <div class="buttons">
                   <span class="formButton">
                       <input type="submit" value="Search"></input>
                   </span>
               </div>
           </g:form>
        </div>
    </body>
</html>

If you're thinking that we shouldn't be repeating so much of the wrapper content (e.g., the <head> tag, etc.), you're right. Grails offers various layout mechanisms to avoid this duplication. (We'll consider that an exercise for the reader.) In the meantime, our search page awaits. (While Grails generally provides dynamic deployment of such changes, some readers have reported the need to restart the application in order to successfully access the new search page. To do so, enter Control-C in the console where you started the application. Then, enter grails run-app, and make your way to the search page.)

You'll notice that in search.gsp we specified that the form should post all requests to a new action named (plainly enough) showSearchResults. So, we need to define that new action in EmployeeBeanController.groovy.

def showSearchResults = {
    render(view:'list', model:[ employeeBeanList:
        EmployeeBean.findAllByLastNameLike("%" + params.lastName + "%") ])
}

Because we just want to display a list of matching employees, we can safely use the existing list.gsp template. This is another example of Grails's ability to provide significant functionality in a few succinct commands. All requests to the showSearchResults action will ...

  • Get the search criteria from the request using params.lastName
  • Get a list of EmployeeBean objects for employees matching that last name
  • Put the results in an object named employeeBeanList
  • render the view, using the employeeBeanList object as the model, in the list template

When we click Search, sure enough, we get the matching results.

Of course, if we can search employees by last name, why not search by first name as well? We can easily add a new input field to our search page.

<tr class='prop'>
    <td valign='top' class='name'>
        <label for='lastName'>First Name Like:</label>
    </td>
    <td valign='top' class='value'>
        <input type='text' name='firstName' value='' />
    </td>
</tr>

That's trivial enough, but what do we have to change in our controller? Very little. Instead of using the findAllByLastNameLike method, we'll now invoke findAllByLastNameLikeAndFirstNameLike.

def showSearchResults = {
    render(view:'list', model:[ employeeBeanList:
        EmployeeBean.findAllByLastNameLikeAndFirstNameLike("%" + params.lastName + "%",
            "%" + params.firstName + "%") ])
}

Grails automatically provides dynamic finders for just about every query combination you can think of! Try it out. Our search from above yields just what we'd expect given our updated query.

Step 8 - Build Your Own Criteria

Over time, you may find that your requirements grow in complexity. How can we handle queries that need to group various sets of criteria? For example, suppose I want to find all employees where...

  • The network ID partially matches some value with case-sensitivity, or
  • The first name and the last name exactly match their respective criteria.

That kind of query is beyond the capabilities of the dynamic finders that we just saw. No worries. That doesn't mean that your application has to take on any additional complexity. Grails offers the immensely flexible Hibernate Criteria Builder to satisfy that need without sacrificing any of the functionality we've seen so far.

First, let's modify our search page to capture the input for our new requirements. If we edit search.gsp and replace the current input fields with the following fields, it should give us what we're looking for.

<tr class='prop'>
    <td valign='top' class='name'><label for='lastName'>Network ID Like:</label></td>
    <td valign='top' class='value'>
        <input type='text' name='networkId' value='' />
    </td>
</tr>
<tr><td>-- or --</td></tr>
<tr class='prop'>
<td valign='top' class='name'><label for='lastName'>First Name Equals:</label></td>
<td valign='top' class='value'>
    <input type='text' name='firstName' value='' /></td>
</tr>
<tr class='prop'>
    <td valign='top' class='name'><label for='lastName'>Last Name Equals:</label></td>
    <td valign='top' class='value'>
        <input type='text' name='lastName' value='' />
    </td>
</tr>

Now let's look at the actual query logic. The following code satisfies our (admittedly odd) requirements in just a few short lines. (This code should replace our current showSearchResults method in EmployeeBeanController.groovy.)

def showSearchResults = {
    def criteria = EmployeeBean.createCriteria()

    def results = criteria {
        or {
            ilike("networkId", "%" + params.networkId + "%")
            and {
                eq("firstName", params.firstName)
                eq("lastName", params.lastName)
            }
        }
    }

    render(view:'list', model:[ employeeBeanList: results.adaptee ])
}

Even if you've never used Hibernate or Grails before, you can quickly get the gist of what's going on here. In a Groovy builder, each component is referred to as a "node." The or node tells us that we'll match any rows that satisfy one or more of the criteria specified by its child nodes. In this case, it has two child nodes.

The first child node returns results that satisfy a case-sensitive like operation on the networkId attribute.

ilike("networkId", "%" + params.networkId + "%")

The second child node is an and node. It returns results that match all of its child nodes. Its child nodes look for rows that exactly match (eq) the firstName and the lastName attributes.

and {
    eq("firstName", params.firstName)
    eq("lastName", params.lastName)
}

Together, these nodes fulfill our query requirements.

If the grouping operators (and, or, etc.) seem oddly positioned at first, it's because the Hibernate Criteria Builder somewhat resembles a Polish notation syntax, where you specify the operator before the operands. That's certainly a departure from Java's use of relational operators, but it should become relatively intuitive after your first few queries.

Now let's see it in action.

Here we're looking for all employees where the network ID contains "jr" or the employee's name is "John Doe".

And when we click Search, we get exactly that.

Summary

How does this development process compare to the approach you use today? With each feature we added along the way, how many components would you touch to make that same change in your existing framework? XML files? DAOs? Form/view classes? Others? And what do you gain by having those additional components to maintain? Ultimate flexibility? If so, do you really need it? Some shops definitely do require that flexibility, but for the other 80%, aren't some sensible defaults (al a convention over configuration) well worth the productivity gains?

We've written very little code in this exercise, and yet we have a functioning and flexible application. We didn't change a single line in our existing entity beans. And because we've written such little code to get to this point, we have less code to maintain. We'll be writing less code tomorrow when the requirements change again. Just think how quickly you can respond to evolving business needs when your application is this agile!

Resources

About the Author

Jason Rudolph is an Application Architect at Railinc, where he develops software that helps keep trains moving efficiently throughout North America. He recently delivered an industry-wide inspection reporting and management system relied on for operational safety by Fortune 500 railroads, equipment leasing companies, and the Federal Railroad Administration. Jason's interests include dynamic languages, lightweight development methodologies, improving developer productivity, and a quest to keep programming fun. Jason holds a degree in Computer Science from the University of Virginia. He currently lives in Raleigh, NC with his wife (who can take a functional web app and make it actually look good) and his dog (who can outrun him, but is no match for squirrels). You can find Jason online at http://jasonrudolph.com.

Rate this Article

Adoption
Style

Hello stranger!

You need to Register an InfoQ account or or login to post comments. But there's so much more behind being registered.

Get the most out of the InfoQ experience.

Allowed html: a,b,br,blockquote,i,li,pre,u,ul,p

Community comments

  • Wow EJB is now legacy?

    by j c,

    Your message is awaiting moderation. Thank you for participating in the discussion.

    A legacy approach to using EJBs. Wow that speaks volumes.

    And do it with another language. Ouch.

    Floyd I cant figure if you think Java has a future or if you think we should all party on the VM like it was (a) 1999 (Corba container).

    This is what frustrates me about the Java space right now. Everyone is so rattled by ruby and scripts and trying to wedge Java in a buzz trend that is so far removed from where Java is that its not even worth discussing Java in a "rails" context.

    Or discussing rails in an EJB context.

    If we have to port rails to the JVM to be happy then maybe we should just use the ruby interpreter and support its toolset and let IBM handle the legacy integration as they are so keen to do.

    Its not interesting or all that relevant.

  • no, you missed the point

    by Floyd Marinescu,

    Your message is awaiting moderation. Thank you for participating in the discussion.

    I think you missed the point - the article doesn't treat EJB as legacy, it's about developing a domain model in EJB and using Grails to quickly build a web app around it.

    Considering strategies like Domain Driven Design, focusing on your domain model in Java and generating the UI around that is an emerging best practice that tools like Grails can enable.

  • Re: Wow EJB is now legacy?

    by Graeme Rocher,

    Your message is awaiting moderation. Thank you for participating in the discussion.

    Where was it mentioned that EJB3 is legacy? Grails is all about embracing Java, the platform, language and tools.

    Java has a huge future and it speaks volumes for the strength of the platform that Grails and its underlying frameworks (Spring, Hibernate, SiteMesh, Quartz etc.) exist.

    In terms of porting Rails, well yes that is one option, but Rails (as with Ruby's runtime) has a very different object model and idioms for developing web applications. One that its contradictory to existing knowledge about specifications like Servlets, JSP and, indeed, EJB.

    Grails is not a port of Rails and in fact is very different in many ways including how it is based on Java frameworks.

  • Re: no, you missed the point

    by j c,

    Your message is awaiting moderation. Thank you for participating in the discussion.

    "built on the Groovy language" and "fully integrated with Java"
    "built on the PL/SQL language" and "fully integrated with Java"
    "built on Javascript language" and "fully integrated with Java"
    "built on ActiveX" and "fully integrated with Java"
    "Built on Flash" and "fully integrated with Java"

    I didnt miss the point. The point was accessing EJBs with something other than Java. And if EJB 3 is a big plus in combination with another language then *something* in this picture just went legacy.

    And something just went "integration strategy"

    Thats how you know what legacy is and isnt.

  • Integration w/ Legacy Apps versus Developing w/ Complimentary Technologies

    by Jason Rudolph,

    Your message is awaiting moderation. Thank you for participating in the discussion.

    If we were talking about using Grails to build a front-end to a VSAM file, I'd agree with you that we're talking about legacy integration; however, that's not the case.

    There's a distinct difference between...

    * Using a current technology to integrate with a pre-existing (legacy) application,

    versus...

    * Using a current technology (like Grails) in concert with another complimentary and current technology (like EJB) to build a new application.

    The point was accessing EJBs with something other than Java. And if EJB 3 is a big plus in combination with another language then *something* in this picture just went legacy.

    I respectfully disagree. We've long been dependent on another language to make our Java apps work. That language is XML, and XML does not make Java a legacy language. They're complimentary technologies.

    If the goal is to build a web user interface for your EJBs (which are, by their very nature, business-tier components), chances are you're going to invoke some other (complimentary) technology for your web tier. Maybe it's just JSP. Maybe it's Struts or Spring MVC or Tapestry or Grails. The use of those complimentary technologies does not make EJB a legacy technology. It's instead a choice of using the most efficient tool for the job at hand. And for many applications, Grails offers the productivity gains to make it a strong contender to be that tool.

  • Re: Integration w/ Legacy Apps versus Developing w/ Complimentary Technolog

    by j c,

    Your message is awaiting moderation. Thank you for participating in the discussion.

    OK Ill give you that, but you cant ignore the undercurrent of the Java space right now which is desperately trying to wedge the competition into its model out of fear of outright rejection.

    What if the smalltalk guys had insisted that Java run in a smalltalk VM? What would people have thought of them? The smalltalk VM was way ahead of Java in the early days.

    So now the Java scene wants to steal ruby and rails from the cradle and I say well if Java is so great then let Java solve its own problems without coopting the vibe of the scripting languages.

    I think Java is trying to change before anyone acknowledges or speaks aloud of a greater failure of OO or more specifically Java projects to deliver.

  • Re: Integration w/ Legacy Apps versus Developing w/ Complimentary Technolog

    by Steven Devijver,

    Your message is awaiting moderation. Thank you for participating in the discussion.

    I think Java is trying to change before
    anyone acknowledges or speaks aloud of a greater failure of OO or more
    specifically Java projects to deliver.


    I read in a magazine a couple of years ago that "the Hummer is the greatest off-road vehicle we ever drove". And "with a Hummer you can get through any terrain except through narrow streets".

    Java, Groovy and Ruby are great in many areas but none is great in all areas. Particularly, Java is not so great to write web handlers. Alternatively you could use Ruby and Rails or Grails if you want to reuse Java code. In fact, we've created Grails to augment the Java platform.

    You can hate us for doing so and being successful. There's however no need to start a flame war.

  • relationship management

    by Jeff Payne,

    Your message is awaiting moderation. Thank you for participating in the discussion.

    Any reason why the relationaship management code can't be generated too? It was a little anticlimactic to learn that the developer had to manually edit the generated groovy code just to handle the object relationaships. I'm assuming all that info is stored as annotations in the EJBs.

  • bug in employee list?

    by ma pel,

    Your message is awaiting moderation. Thank you for participating in the discussion.

    Hi,
    I followed up this step-by-step and successfully run 'grails run-app'.
    Everything looked great except this case:
    I assigned 3 computers to an employee, for example, jane and I got
    3 instances of the same 'jane' listed in the browser(IE6.0) when I click on
    employeeBean list link. That seems a bug. How can I fix it?
    Thanks.

  • Re: relationship management

    by Graeme Rocher,

    Your message is awaiting moderation. Thank you for participating in the discussion.

    This is one the todo list: jira.codehaus.org/browse/GRAILS-172

  • Re: bug in employee list?

    by Jason Rudolph,

    Your message is awaiting moderation. Thank you for participating in the discussion.

    Good catch. Thanks for your feedback.

    There's apparently problem with the dynamic list method, which is used to fetch the list of employees. You can track the problem here: jira.codehaus.org/browse/GRAILS-284

  • Nice, but...

    by Randall Burt,

    Your message is awaiting moderation. Thank you for participating in the discussion.

    This is a really great article and does a very nice job of showing off the features of Grails. However, I was sort of hoping for an article about integrating Grails and EJB. As it is, the author just illustrates the flexibility of the new EJB pojo implementation as well as the rapid development capabilities of Grails. What I'd like to see (and I believe is slated for a release later this year) is the ability to map those beans to/from ones already deployed on an existing EJB container instead of simply using Hibernate and copying the pojo implementations into the project. Maybe this is already possible using some tricks with the Spring configuration, but I don't know Spring well enough to say. In any case, thanks for a great article!

  • Re: Nice, but...

    by Pat Osterday,

    Your message is awaiting moderation. Thank you for participating in the discussion.

    Randall,

    Your comment is exactly what I was thinking! I read the article and there's no mention of an app server or anything like that! Just copying the entity code and using that. While Grails looks cool, we've got a lot invested in the JEE resources. Guess we'll have to see what develops!

  • Re: Nice, but...

    by Piero Sartini,

    Your message is awaiting moderation. Thank you for participating in the discussion.

    Yes, thats what I try to find out as well.

  • Article in pdf format?

    by Fred Janon,

    Your message is awaiting moderation. Thank you for participating in the discussion.

    Would it be possible to the content of this article in pdf format, to be able to read it offline and print it nicely? Thanks.

  • having problems to run app with grails 1.0 RC2 & groovy 1.0

    by Michael Brinkmann,

    Your message is awaiting moderation. Thank you for participating in the discussion.

    Hi,

    I am stuck following your steps in a very early stage ("run-app" directly after "create-app"):
    (I am running the grails / groovy-versions mentioned above with JDK 1.6.0_03 on Windows XP SP2)

    > grails run-app

    Welcome to Grails 1.0-RC2 - grails.org/
    Licensed under Apache Standard License 2.0
    Grails home is set to: C:\Programme\Dev\grails-1.0-RC2

    Base Directory: c:\Arbeit\Allgemein\Dev\Projekte\GrailsTest1
    Environment set to development
    Note: No plugin scripts found
    Running script C:\Programme\Dev\grails-1.0-RC2\scripts\RunApp.groovy
    groovy.lang.MissingPropertyException: No such property: war for class: java.lang.String
    at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.unwrap(ScriptBytecodeAdapter.java:48)
    at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.getProperty(ScriptBytecodeAdapter.java:474)
    at Config.run(Config.groovy:62)
    ....

    Do you have any hints for me?

    Kind regards
    Michael

  • Re: having problems to run app with grails 1.0 RC2 & groovy 1.0

    by Michael Brinkmann,

    Your message is awaiting moderation. Thank you for participating in the discussion.

    Hello again,

    FYI: just fixed the error by changing the line
    grails="error"
    to
    grails.app="error"
    in the generated file "grails-app/conf/Config.groovy".

    Kind regards
    Michael

  • Issue GRAILS-2042

    by Venky VB,

    Your message is awaiting moderation. Thank you for participating in the discussion.

    When I try out the tutorial I get an error which is logged in the JIRA (GRAILS-2042). However there seems to be no action on this issue ? Any workaround available for this ? (am using Grails 1.0 RC3)
    - Venky

  • Scrollable results?

    by Siddharth Bala Ravi,

    Your message is awaiting moderation. Thank you for participating in the discussion.

    Jason,

    Thanks for the article - it's awesome.
    I was wondering what needs to be done to make the following search scrollable...being very new to grails, i'm having a real hard time figuring it out...

  • generate-controller failure with Grails 1.0.4

    by Sherry Shen,

    Your message is awaiting moderation. Thank you for participating in the discussion.

    I went through Step 1 to 3 with following changes
    in ejb3_grails dir
    ./src/java/com/jasonrudolph/ejb3example/entity/EmployeeBean.java
    ./src/java/com/jasonrudolph/ejb3example/entity/ComputerBean.java
    ./grails-app/conf/hibernate/hibernate.cfg.xml
    ./lib/mysql-connector-java-5.1.6-bin.jar
    ./grails-app/conf/ApplicationDataSource.groovy
    I am not sure about hibernate.cfg.xml location.

    At Step 4 - Generate the Scaffolding, I got an error.
    % grails generate-controller
    .....
    No domain class found for name com.jasonroudolph.ejb3example.entity.EmployeeBean.
    Please try again and enter a valid domain class name

    How to move forward?
    Thanks for the help!

  • Re: Nice, but...

    by Gazi Mushfiqur Rahman,

    Your message is awaiting moderation. Thank you for participating in the discussion.

    The thing I actually need is using the existing EJB 3 domain entities within a GORM entity. For example I already have a User entity bean developed using EJB 3 annotations. I am going develop the new entities in GORM. So, how can I reference/use the User entity from within the GORM entities?

  • Re: Nice, but...

    by Rajakumar Sennayan,

    Your message is awaiting moderation. Thank you for participating in the discussion.

    I have J2EE EJB2 Session bean and EntityBean application. I would like to migrate EJB2 Session beans and Entity Beans to another working Grails application. Kindly let me know the migration steps

Allowed html: a,b,br,blockquote,i,li,pre,u,ul,p

Allowed html: a,b,br,blockquote,i,li,pre,u,ul,p

BT