Chapter 4. Putting the model to work


This chapter covers

  • Generating UIs instantly with scaffolding
  • Customizing scaffolds for your project
  • Using dynamic finders for easy querying
  • Advanced query techniques

We spent a lot of time last chapter on the basics of domain modeling. It wasn’t easy defining all those relationships, but it did lay down the fundamentals you’ll need to build on in each new Grails project. In this chapter, we’ll take our new domain knowledge and build a simple UI for our Hubbub application.

You’ll learn how scaffolds can make bootstrapping a UI lightning fast, and how they can be customized to generate a unique look and feel for your application. Then, once you have some experience building basic scaffolds for domain classes, we’ll teach you some query skills to put that domain model to work. Along the way, we’ll implement a basic search form for Hubbub. With this search form in place, we’ll teach you some powerful grouping query skills and finish up writing a tag cloud.

First things first, though. Let’s explore the amazing productivity gains available through Grails scaffolding.

4.1. Creating instant UIs with scaffolding

In chapter 3, we created a basic User object and explored CRUD operations and querying by hand. But we took you via the scenic route. We could have implemented our feature set in one line of code, without any HTML, by taking advantage of Grails’ scaffolding.

We wanted you to know how to write basic CRUD operations by hand, but now that you do, it’s time to show you how to get things up and running with a lot less code.

4.1.1. Scaffolding Hubbub’s domain classes

In chapter 1, we introduced Grails’ scaffolding. Scaffolding lets you automatically generate CRUD screens and matching controller actions for your domain classes on the fly with a single line of code. When you’re starting work on an application, this is a fantastic way to get a feel for how your site might work, and scaffolds even prove useful later on for the basic admin screens that end users never see.

Let’s create a scaffold for our User object. The first step is to create the controllers that would normally expose our CRUD logic to the web:

grails create-controller com.grailsinaction.User
grails create-controller com.grailsinaction.Profile
grails create-controller com.grailsinaction.Post
grails create-controller com.grailsinaction.Tag

With our controllers generated, it’s time to edit each of the created classes to turn on scaffolding. For example, here’s how you would edit the /grails-app/controllers/com/grailsinaction/UserController.groovy file:

package com.grailsinaction

class UserController {

def scaffold = true
}

Repeat the process for the ProfileController, PostController, and TagController files, and then start the Grails application to see the scaffolding in action. Do a grails run-app and point your browser to http://localhost:8080/hubbub. Click on the User-Controller link that appears on the home page, and you’ll be presented with an empty list of users. Click on the New User button to create a new user. Grails will display a basic editing screen so you can populate the user’s details, as shown in figure 4.1.

Figure 4.1. Scaffolding also includes relationships and validations.

The generated User scaffold includes options for assigning the related one-to-one Profile object we generated in the last chapter. Any validations that we’ve applied to fields are also honored in the generated form. For instance, the text box generated for the password field is limited to 8 characters based on the size: 6..8 validation setting we generated for that field.


Validation ordering dictates scaffolding ordering

The order of the fields in your generated scaffold follows the ordering of the constraints in your domain class. If you don’t have any constraints, the field order is random (almost never what you want for a form). You’ll recall from last chapter that our User object has the following constraints:

static constraints = {
userId(size:3..20)
password(size: 6..8, validator: { passwd, user ->
passwd != user.userId
})
dateCreated()
profile(nullable: true)
}

Compare the ordering of these constraints with the generated form in figure 4.1. Notice how the order matches?

In our constraints, we’ve included dateCreated() but applied no validators to it. This technique is useful when you need to influence scaffold ordering but don’t want to apply any real constraints on the field.


Although our constraints permit a user to have no assigned profile, we’re going to be doing some profile experiments in upcoming sections, so let’s work through the process of assigning one. In order to assign a Profile object to the User, you’ll first need to create a Profile object using the Profile scaffold, which is obviously not ideal. But let’s create one to keep the scaffolds working. Point your browser to http://localhost:8080/hubbub/profile/create and create a new user profile.

4.1.2. Scaffolding and validation

While we’re creating our new Profile object, we can explore how the scaffold handles failing validations. Let’s review the Profile domain class, which is shown in listing 4.1.

Listing 4.1. The Profile class models the personal attributes of a user.

We have constraints on the home page (an optional URL) and the email address (an optional email), and we have a byte[] to handle the uploaded photo.

Figure 4.2 shows what happens if we try to create a profile with an invalid home page URL and email address.

Figure 4.2. Validation errors are handled for scaffolds, and so are file uploads and textareas for large fields.

Notice that the failing fields are highlighted, and appropriate error messages are displayed at the top of the screen. The descriptions might not be quite to your taste (we’ll customize them shortly), but you have to be impressed with the amount of UI functionality we’ve been able to get for a few lines of code. There’s even a file upload control for the photo.

Now it’s time to customize those error messages.

4.1.3. Customizing error messages

Our validation is now in place, but the default error messages might not be to your taste. It’s time to customize those messages. At the same time, we’ll internationalize (i18n) things for our Hubbub global market.

Our exploration starts in the /grails-app/i18n/messages.properties file, which is Grails’ default resource bundle:

default.invalid.url.message=Property [{0}] of class [{1}] with value [{2}] is not a valid URL
default.invalid.creditCard.message=Property [{0}] of class [{1}] with value [{2}] is not a valid credit card number
default.invalid.email.message=Property [{0}] of class [{1}] with value [{2}] is not a valid e-mail address
default.invalid.size.message=Property [{0}] of class [{1}] with value [{2}] does not fall within the valid size range from [{3}] to [{4}]

Here you’ll find the standard Grails validation error messages. You’ll recognize the URL and range messages from figure 4.2. These can be customized for your needs.

You can see that there are special placeholder fields:

  • {0} for a property name
  • {1} for a class name
  • {2} for a value

There are also others for certain maximum and minimum values required by some constraints. Grails will inject these into your message at runtime if you wish to make use of them.

Default error messages are fine, but typically there’s some context required to clearly communicate the problem. Consider our home page and email address fields. To make sure the user is given nice user feedback we can create an entry in messages.properties specifically for it, like so:

profile.homepage.url.invalid=You must provide a valid web address for your homepage. [{2}] simply does not cut it
profile.email.email.invalid=You must provide a valid email address. [{2}] is just not the business

The format for the entries is {className}.{field}.{errorCode}. These are the same error codes we introduced in the last chapter—if you need a refresher, take a look at section 3.3.1, “Standard validators.”

You can change the resource file, messages.properties, while your application is running, and the next validation will pick up the changes. Figure 4.3 shows our new, and more casual, error messages in action.

Figure 4.3. You can create your own error messages in resource bundle files.

Those error messages might still need some drafting, but you’ll recognize the power of externalizing all your messages to a separate file. If you’ve done work on internationalizing applications, you might recognize that messages.properties looks like a resource bundle file—because it’s a resource bundle! This means Grails makes it straightforward to internationalize your error messages (as discussed in the sidebar).

With our journey through error handling and customization finished, it’s time to wire up our new Profile object to our User.


Scaffolds and internationalization (i18n)

You may notice that the i18n directory contains error messages for several different languages (or locales, as they’re known), each with a different filename suffix. Grails automatically determines the user’s locale based on the Accept-Language header their browser sends, but for testing you can pass in a ?lang=fr header to any of your controllers to simulate a French user. Grails will even remember your locale setting in a cookie for future requests. Here’s a sample editing screen with French error messages:

But there’s not much point internationalizing your error messages if the rest of your scaffold form is in English. For this reason, Marcel Overdijk developed an i18n Templates plugin that you can install into your Grails application. We cover the details of how plugins work in chapter 8, so skip ahead if you’re super keen.

The i18n Templates plugin provides versions of the scaffolding templates that are entirely driven by message bundles, like the ones for the error messages. You can provide internationalized versions of button labels, headings, and user feedback messages for all your domain class elements. Check it out in the plugins section of the Grails website at http://grails.org/Plugins.


4.1.4. Managing relationships via scaffolds

Once we have created a valid Profile through the scaffold, we return to the Create User screen and attach the Profile to the User. Figure 4.4 shows the Create User screen with the new profile attached.

Figure 4.4. The User scaffold lets you select and attach a Profile.

If we’d created many profiles, it would be difficult to select one based on its database ID, but this presentation can be customized (as discussed in the sidebar).


Tip

Grails allows you to make source code changes whilst your application is running. This is a fantastic facility for improving your productivity, because you don’t waste time stopping and starting your application to pick up any changes. If Grails detects any changes, it will automatically restart your application. Be warned, though. If your dataSource.groovy has a dbCreate property set to create-drop, your database will be recreated and all data lost.


Grails’ scaffolding supports all of the common relationship types: 1:1, 1:m, and m:n. With a few lines of code, you can scaffold your entire application to the point where you can enter both data and relationships.


Customizing scaffolding combo boxes with toString()

Grails scaffolds use the toString() method of the object to populate the combo box, so let’s customize the Profile class’s toString() method to make it a little more selectable:

class Profile {

// existing domain class code here

String toString() {
"Profile for ${fullName} (${id})"
}

}

If we try to create the user again, Grails will populate the Profile combo box using this new toString() routine, as shown here:


This does get pretty unmanageable when there are a lot of relationships going on, particularly in parent-child forms. For those situations, you’re better off hand-coding your UI.


Should I use scaffolding in production applications?

Because Grails makes basic CRUD screens easy to generate, you might wonder whether you could use Grails scaffolding for your production applications. Although they probably don’t have the cosmetics or ease-of-use that a public-facing site demands, they’re fine for internal applications. They’re perfect for admin screens that contain reference data, and for CRUD screens that don’t need the sophistication of a custom UI. In the next section, we’ll even show you how to skin them, so they can look like your other internal apps. And in chapter 10, we’ll show you how to secure them so that only admin users can get to them.


4.1.5. Tweaking scaffold layouts with CSS

Although the default scaffolds are powerful, they can sometimes look a little “default.” Sometimes all that’s necessary is adding a skin, and the built-in templates are fine for production use (particularly for internal admin use).

If you want to change the CSS styling for elements in your scaffolds, you can edit the CSS file directly in /web-app/css/main.css. This file is included in all the standard Grails templates, so make your changes and refresh your browser. If your customization goes deeper, and you want to change the layout of the pages themselves (to add banners and footers, for example), you’ll need to edit the standard Grails layout that’s applied to your scaffolds.

In chapter 1, we introduced layout templates. We said then that Grails makes use of these templates (sometimes known as decorators) to add standard headers, footers, and sidebars to the pages of our applications. You’ll learn more about the details of layouts in chapter 6, but for now it’s enough to know that you can override the global layout for your site by editing /grails-app/views/layouts/main.gsp. Listing 4.2 updates this file with a new Hubbub skin.

Listing 4.2. Changing the standard layout decorator for Hubbub

This revision adds custom images and CSS files to our main Hubbub layout page. It changes the standard scaffolds so they look like figure 4.5.

Figure 4.5. You can change the skin for scaffolds to use your own layouts.

Using this technique, you can quickly change the layout for your entire scaffolded site. Even better, once you learn more about how layouts work (in chapter 6), you’ll be able to override the layouts on a per-controller basis (just for profiles, for example) or even per-action basis (just for profile edits).

4.1.6. What can’t you do with dynamic scaffolds?

Dynamic scaffolds give you an incredible amount of power out of the box, and we’ve shown you several techniques for customizing them, but there are situations where using them doesn’t make sense:

  • Complex cross-domain forms are tricky to implement with dynamic scaffolds (and you end up with a UI that only a mother could love).
  • Dynamic scaffolds have no Ajax support (though you could get around this by customizing the scaffold templates themselves, which we’ll talk about soon).
  • Dynamic scaffolds have limited support for interaction customizations. (We’ve done tricky work with CSS and layout customization, but there’s not much you can do to customize the generated UI without changing the generator itself, which we’ll also do shortly.)

As you can see, dynamic scaffolds aren’t the answer to every situation. Fortunately, dynamic scaffolds aren’t the only scaffolds in town. Grails allows you to generate static scaffolds, which you can then edit by hand to add your own features. It’s time to generate some static view files and take a look at what static scaffolding offers.

4.1.7. Static scaffolding: generating and customizing scaffold code

There are times when dynamic scaffolding is not a good fit for your project. Perhaps you’ve got some complex many-to-many or parent-child form interactions that need a craftsman’s hands. Or maybe you want to use a completely different approach to handing the view.

For these sorts of situations, Grails can generate some basic static scaffolding to get you started. By handing you a dump of all the controller and view source files that it would normally generate dynamically, you can get up and running quickly.

To cater for specific situations, Grails offers two methods of generating static scaffolding files. The first generates all the controller and view files at once:

grails generate-all com.grailsinaction.User

The second generates only the view files, and leaves your controller class alone:

grails generate-views com.grailsinaction.User

If you have existing controller or view files, you will be prompted as to whether you want to overwrite them.

It’s useful having separate Grails commands for each operation, because your controller code is likely to be fairly insensitive to changes in the domain classes. For example, controller code performs operations like User.save(params) or User.get (params.id) rather than accessing individual properties of the domain objects. Views, however, will need to display and edit each property on a domain class, so they are very sensitive to any changes made to the domain class. Views, in contrast, are dependent on model changes (adding new fields, renaming fields, and so on), so it can be handy to be able to regenerate just the view code on demand.

The standard controller code is not rocket science. You’ve written trickier stuff by hand in the previous few chapters. But it’s worth knowing which methods are generated for you—they’re shown in table 4.1.

Table 4.1. The basic scaffolding controller methods

Action name

Function

Rendered view

index

Redirects to list().

N/A

list

Shows paginated list of domain class instances.

list.gsp

show

Shows a table of all properties for one instance of the domain class.

show.gsp

delete

Deletes a given ID, then redirects to list().

N/A

edit

Displays an editing form for a domain instance, and then submits it to update().

edit.gsp

update

Updates a given instance of a domain class with new values. Redirects to either list() or edit() depending on validation.

None

create

Shows a blank editing form for a new domain class instance, and then submits it to save().

create.gsp

save

Saves new instances of domain classes. Redirects to either list() or create() depending on validation.

N/A

A standard static scaffold generates only four GSP files (list, show, edit, and create) and the rest is done in controller logic.

4.1.8. Customizing scaffolding templates: building your own UI generator

Working with the standard Grails templates is a big time saver, but they won’t always provide the functionality you need for your particular project. It’s easy enough to style the templates at runtime using CSS, but you might find that you want to customize the behavior, perhaps removing the Delete button for non-admin users. For these sorts of scenarios, you need to edit the templates that Grails uses to generate the scaffolds. You’ll be pleased to know you can!

The first step is installing the templates into your application:

grails install-templates

This will create a bunch of template files in /src/templates that you can customize. Figure 4.6 shows a full list of the files that will be generated.

Figure 4.6. The generated template files you can install and modify for scaffolds

The files we’re concerned with are the entries under /templates/scaffolding. These represent the code that’s generated dynamically during scaffolding. You can customize every aspect of the template process, from the controller code to the GSP files that are generated.

The entries in the artifacts directory are the shell classes that are generated when you execute Grails commands. For example, when you execute grails generate-controller, it’s the /templates/artifacts/ Controller.groovy file that will be copied over. Hooking into this process can be useful if you want to generate standard license headers or some basic coding standards for new classes.

We’ll talk about customizing web.xml in chapter 15 a little later in the book. But keep in mind that Grails won’t hold you back from tweaking UI templates in whatever ways make sense for your applications.

4.2. Groovy querying with dynamic finders and Query by Example

We’ve covered a lot of scaffolding territory and put our data model to work generating scaffolds to handle all the basic data-model operations (creating, editing, and deleting). But there’s one glaring omission: querying.

The fastest way to query your data model in Grails is through a facility known as dynamic finders. They are a concise way of querying the data model using a natural-language type of syntax. In this section, we’ll start exploring dynamic finders in real depth. We’ll also look at Query By Example (QBE), which provides a useful way to query domain objects based on a partially populated template object. Then we’ll take a look at some of the useful Grails query facilities for listing and counting objects.

4.2.1. Implementing a basic search form

One of the best ways to get familiar with dynamic finders is by implementing a basic search form for Hubbub. Suppose we want to search to see if any of our friends are already signed up with Hubbub. Let’s enhance our User scaffold to allow this.

First, we’ll create a search form in /grails-app/views/user/search.gsp, as shown in listing 4.3.

Listing 4.3. A basic search form for Hubbub

There are a few special <g:> tags in listing 4.3 that you won’t be familiar with. The <g:form> tag specifies that when the user clicks Submit, the form values should be sent to the results action on the current controller, which in this case is User. The <g:textField> tag names the text box userId, which means that it can be handled inside the results action as params.userId.

With this basic search form in place, we can point our browser to /hubbub/user /search and enter our search criteria. Figure 4.7 shows the search form in action. We’ve seen basic form code like this earlier in the chapter. What’s new is how we can implement the search in the controller. Let’s customize our UserController scaffold to handle the new form submission. Listing 4.4 shows the controller with two new actions: search and results.

Figure 4.7. A basic Hubbub search form in action

Listing 4.4. Adding the search logic to the UserController.groovy file

Notice how we’ve kept our scaffolding on the User object, but augmented it with our new search operation. We’ve added a search action with no logic . This is for when we want to display the search form. When the user navigates to /user/search, this action will fire, and Grails will pass the user through to /grails-app/views/user /search.gsp, which acts as a placeholder for the target form.

When the user clicks Search, the form tag submits the search form to the results action , which executes a dynamic finder on the User object and places the search term and the matching user objects in the view .

The User class doesn’t have a findByUserIdLike() method, but Grails creates one dynamically using the Groovy MetaClass. By using Groovy’s dynamic language features, Grails lets you create queries based on all of the available persistent properties in your class. You can even chain two criteria together using And and Or. A login action might do something like this:

def user = User.findByUserIdAndPassword(params.userId, params.password)
if (user) {
// found them in the database..
}

In the next section, we’ll explore the options for these dynamic finders, but for now, let’s be content with our findAllUserIdLike() call. In figure 4.7 we make use of the SQL wildcard operator (%) to search for all users starting with “chuck”. But it’s impractical to force the user to enter those % characters for wildcards in their searches. Let’s smarten up our controller code to do the magic for them:

def users = User.findAllByUserIdLike("%${params.userId}%")

This line adds wildcards to either end of the search term to simulate a full-text search.

With our search operations in place, we’re going to need a basic results screen. Listing 4.5 implements a results.gsp page (in the same directory as search.gsp) so our controller can return the results to the user.

Listing 4.5. A basic results screen for the search

The results page in listing 4.5 takes advantage of another dynamic routine, User.count(), to display a count of all User objects in the database . It then uses another Grails tag, <g:each>, to iterate through all the matching User objects to print out their names . Figure 4.8 shows our Search Results screen in action.

Figure 4.8. The Search Results screen displays several chucks.

We have a basic search function in place, but there’s a lot more to learn about query operations. Let’s explore some test cases to see what’s achievable.

4.2.2. The many faces of dynamic finders

Dynamic finders can be used to compose almost all of the basic queries you’re likely to want, and their natural-language domain-specific language (DSL) makes them easy to understand and maintain. Let’s create a new integration test so we can write a few exploratory queries:

grails create-integration-test QueryIntegration

Listing 4.6 demonstrates a variety of ways you can use dynamic finders to search for particular values of any attributes on your domain class. In this test case, we’ll search for a user by username, by username and password, and by date.

Listing 4.6. A variety of dynamic finders in action

The findBy*() method lets you specify either one or two attribute names to query on and returns a single value (or null if no value is found). There’s a corresponding findAllBy*() method that returns all matching values or an empty List if nothing matches.

Say we wanted to find all users created in the last day or two. We could make use of the findAllBySignupDateBetween() method and have a List of users returned as shown in the following code:

def now = new Date()
def users = User.findAllBySignupDateBetween(now-1, now)
assertEquals 2, users.size()

Dynamic finders are powerful, and they have a lot of options. Table 4.2 shows some of the basic dynamic finders that are available. You can join criteria with And, but you can only query on two fields.

Table 4.2. A selection of available dynamic finders

Search criteria

Example

LessThan

User.findAllByDateCreatedLessThan(lastWeek)

LessThanEquals

User.findAllByDateCreatedLessThanEquals(lastWeek)

GreaterThan

User.findAllByDateCreatedGreaterThan(lastWeek)

GreaterThanEquals

User.findAllByDateCreatedGreaterThanEquals(lastWeek)

Like

User.findAllByPasswordLike("secret")

Ilike

User.findAllByPasswordIlike("Secret")

NotEqual

User.findAllByPasswordNotEqual("password")

Between

User.findAllByDateCreatedBetween(lastWeek, now)

IsNotNull

User.findAllByPasswordIsNotNull()

IsNull

User.findAllByPasswordIsNull()

And

User.findAllByDateCreatedGreaterThanAndUserIdLike(la stWeek, 'glen')

Or

User.findAllByPasswordIsNullOrPasswordLike("secret")

Writing test cases is a great way to ensure that your code is working as you expect. But what about when you’re starting to compose your domain queries and you’re looking for a quick way to prototype a whole bunch of different query values? We find the fastest way is to take advantage of the Grails console. If you start your application with grails console, Grails launches a small Swing console with a full Grails environment where you can try out your queries and get instant feedback.


How dynamic finders work

After seeing dynamic finders in action, you’re probably curious about how they work. Methods with those names don’t exist, and it would be inefficient for Grails to generate methods for every possible query combination on each object, right?

Dynamic finders use the Groovy MetaClass to intercept method calls on your domain object. By using Groovy’s methodMissing feature, Grails catches the call to a nonexistent method and creates a Hibernate query from it. It also caches the method so future invocations are quicker. Make sure MetaClass exploration is on your to-do list for increasing your Groovy kung fu.


4.2.3. Tuning dynamic finders with eager and lazy fetching

When GORM executes a search like findByUserId, it queries for all the fields of the User domain class, but any nested collections (such as Post) are returned lazily by default. That means the first time you access the posts field of the User object, GORM goes off and retrieves all the matching Post objects related to that user (using a second query). This is called lazy fetching. GORM (Hibernate) works this way by default because it’s a waste of time retrieving nested objects just in case the user wants to access them.

When you formulate your own queries, though, you often know whether you will access those nested collections right away. In those cases, it makes sense to bring back all the nested objects you want in one SQL query—this is called eager fetching. You can do this by telling Grails which collections you’d like to fetch eagerly in the query. We could create a user query that eagerly fetches their related posts, like this:

def user = User.findByUserId('glen', [fetch:[posts:'eager']])

If you’re not sure how many queries you’re issuing, you can set logSql=true in your DataSource.groovy configuration file. This will print every database query that Hibernate issues to the console, making it easier to spot places where eager fetching would make a big difference. If you always access the nested collection straight away, you can add a mapping entry to configure GORM to always eagerly fetch the collection. We cover data source tuning and fetch mapping options in chapter 13.

4.2.4. When dynamic finders don’t deliver

Dynamic finders are powerful, but they have some limitations. The most important one is that you can only use dynamic finders on a maximum of two fields. If you need to query on more than two fields, you need to use criteria or Hibernate Query Language (HQL) queries.

The same applies if you need to do more complex relational queries based on attributes of a contained object. For those scenarios, we’ll need to resort to HQL. But don’t worry. We’ll explore both those options in depth in section 4.3.

4.2.5. Introducing Query by Example (QBE)

Grails has several ways to query for objects, and we’ve started to look at dynamic finders. But there are many other search options in Grails, like Query by Example (QBE). With QBE, you create a domain object and populate it with properties that you wish to query for (anything that’s null will be skipped). The search then looks for similar objects.

Let’s add a new test case to QueryIntegrationTests (from listing 4.6) to see how QBE works. Our new test is shown in listing 4.7.

Listing 4.7. QBE works by populating a sample object and searching for similar objects

In this test, we populate our sample User objects with the fields we wish to search on, and we pass them into either User.find() to find the first matching object or User.findAll() to return all instances matching the criteria . If User.find() matches more than one entry, you only get the first one back.

4.2.6. Getting dynamic with list(), listOrderBy(), and countBy()

We’ve had a good look at the Grails find()-based commands, but we haven’t yet explored some of the other useful query routines that Grails adds to domain classes, such as list() and count(). User.list() gives you a List of all User objects from the database, and User.count() gives you an integer count of how many User objects there are, but things can be customized a whole lot more than that.

Let’s start by exploring some more sophisticated uses of list(). First, there’s a truckload of options available to the list() command that control sorting, ordering, and offsets, as shown in table 4.3.

Table 4.3. Options for the list() method

Option

Description

max

Specifies the maximum number of rows to return

sort

Specifies the field to sort on in the returned list

order

Specifies the order of the sort: 'desc' or 'asc' (default is 'asc')

ignoreCase

Sets sorting to ignore case (true by default)

fetch

Specifies eager/lazy fetch strategy as a Map of options

offset

Specifies the number of elements into the ResultSet to start at when returning values (useful for pagination)

Let’s invoke the list() call with some of those options. Let’s say we want to see the first five users ordered by ascending userId, and we also want to eagerly fetch the posts associated with those users to iterate them quickly. That’s simple:

def users = User.list([sort: 'userId', order: 'asc',
max: 5, fetch: [posts: 'eager']])

Now that you’ve seen some of the power of list(), it’s time to introduce list-OrderBy*(). This method lets you pick a field to order by. For instance, if we want to sort by ascending userId, we could use this method:

def users = User.listOrderByUserId()

You can still pass in the max, order, and offset options if you need to control sort ordering or offsets for paginating your results.

Now it’s time to see the corresponding countBy*() method in action. To find all users with dodgy passwords in our application, we might try something like this:

def dodgyCount = User.countByPassword("password")

This will return a count of all users with the password “password”.

4.3. More sophisticated query options

Dynamic finders are concise and quite powerful. Being able to do something like User.findByUserId("joe") is a productive way to query, but there are situations where that’s not enough. Grails won’t leave you stranded: it’s time to explore criteria queries and HQL.

This section takes advantage of some pretty advanced query skills in Grails, so don’t get discouraged if some of this seems tricky the first few times. Once you start writing your own applications and can’t implement the features you want with dynamic finders, you’ll find yourself coming back here to review these advanced query features. Buckle up.

4.3.1. With great power: criteria querying

Dynamic finders are great for basic querying, but sometimes you want something more complex. Perhaps you’re querying on more than two fields, or perhaps you require a sophisticated nested query. Or perhaps you’re building a query dynamically from user input in a search form. It’s time to experience the power of GORM criteria queries.

Let’s look at a Hubbub example. Hubbub lets us tag posts with keywords, so suppose we wanted to find all of a user’s posts created in the last day and tagged with “Grails”. For this sort of scenario, criteria queries are what you want. Listing 4.8 demonstrates a basic criteria query.

Listing 4.8. Criteria queries give you astonishing power.

That’s a lot of parentheses. Let’s take a closer look. After creating the Criteria object on the domain class that we wish to query , we call the list method and pass in the criteria using the GORM Criteria DSL. In this case we’re doing an and because we want all of the criteria nested within the and { } block to match (we could also use or or not here, depending on how we want to join the data logically).

Next we add a few parameters, called “restrictions,” to our query. In this case, we’re using an eq restriction to require that the Post’s User matches our existing User object, and a between restriction to restrict the search to a date range. The final item is the subquery on tags to ensure that we only find those posts that are tagged with “Grails” .

Having built the main query, we can add other restrictions. You’ll recognize maxResults() and order() , but here we can specify the order in one instruction, rather than having separate orderBy and sort fields.


Where can I get a full list of available restrictions?

The GORM Criteria DSL is a thin wrapper over the Hibernate Restrictions class. If you take a look at the Restrictions Javadocs, you’ll find all of the options. Included are things like ilike (for case-insensitive matching), ge (greater equal), and isNotEmpty (for fields having a value).


There are a few more shortcuts available for criteria queries. First, because list() is the most common operation on a Criteria instance, you can omit it entirely (though we prefer keeping an explicit list() to make things more readable). Criteria queries also support a count() method for returning a count of the matching values, or a get() method to return a unique result.

Criteria queries are so handy that there’s a special withCriteria closure that’s available on all domain classes. This is often a concise way of expressing a criteria query inline with your controller code:

def entries = Post.withCriteria {
and {
eq('user', user)
between('created', new Date()-1, new Date())
}
}

The shorter withCriteria() form of the query has all of the same Criteria DSL goodness that we saw for the longer .createCriteria().list() version.

4.3.2. Dynamic queries with criteria

Criteria objects make complex queries simple, but they’re also great for generating queries dynamically. Imagine that we want to generate a basic search form for Hubbub profiles. We’ll provide the user with form fields to search on (maybe their full name, email, and homepage), and we’ll give them AND, OR, and NOT options to join their criteria.

First, we’ll generate a basic form with field names that match our Profile object, as shown in figure 4.9.

Figure 4.9. A more advanced search screen with Boolean operators

With our query fields in place, we can now write a generic search action that builds a Criteria object on the fly, based on the fields the user populates in the form. Because of the flexibility of the Criteria object, we can take advantage of Boolean operations (and, or, not) when constructing the search. Listing 4.9 shows what a dynamic criteria search might look like.

Listing 4.9. A dynamic search form

We’ve had to do a little work here. Because Grails populates the params object with its own values, not just those from our form fields, we have to extract from the params just those values related to our Profile object. To perform this filtering, we use the Groovy MetaClass to find all the properties in the Profile class . We then construct a Criteria object, and set the join type to match the Boolean operation the user selects: and, or, or not . Finally, we iterate through all the incoming params , and, for those that correspond to a Profile property, we apply an ilike operator .

This is a pretty advanced use case, so don’t be worried if it sounds a bit complex. It will make more sense when you experiment with the source code. In figure 4.10 we give it a test by combining a few options with an Or constraint. We’ve even thrown in some wildcard operators for good measure (%).

Figure 4.10. An advanced search screen with ANDing and ORing

We can see our results in figure 4.11, which uses a basic output form.

Figure 4.11. A basic results screen shows two profiles matching our criteria

You can see that Criteria objects are fantastic for building up dynamic queries. But there are even more powerful Criteria features for creating report-style queries. It’s time we explored groupBy functionality.

4.3.3. Creating a tag cloud using report-style query projections

Criteria queries shine for complex and dynamic queries, but they can also do projections. Projections are aggregating, reporting, and filtering functions that can be applied after the query has finished.

A common use for projections is to summarize data in a query. For example, to build a tag cloud for a user, we need to find their tags and count how many posts are associated with each. This is a good use case for a count() projection, as shown in listing 4.10.

Listing 4.10. Using projections to group and count search results

Projection queries are normal criteria queries, so we start by specifying what we’re querying for—in this case, a collection of Post objects . Because we’re selecting and grouping on multiple object sets inside Post, we need to set up an alias tag for both our User and Tag fields . This will let us reference these fields in later groupBy clauses.

With all the infrastructure in place, we start specifying our query. Our first restriction finds all posts related to user “glen” using the eq operator . Once we have all posts related to glen, we can start grouping them by specifying the projections clause . We specify the field we want to group by, t.name, along with the operation we want to perform on that grouping, count() .

The result of the query is a List of key/value pairs containing the tag name and the number of posts with that tag. All we need now is to convert that list into a Map, with tagName as the key, and tagCount as the value. It’s time to dust off those Groovy skills:

def tagcloudMap = tagList.inject([ : ]) { map, tag ->
map[ tag[0] ] = tag[1]; map
}

This creates a map of tag name to tag count that’s ready to be fed to a tag cloud taglib.

There are lots more projections available in GORM. In addition to count(), you can use max(), min(), avg(), sum(), and all sorts of other statistical methods. GORM projections are merely DSL enhancements to the standard Hibernate projections. Check out the Hibernate Javadocs on the Projections class to see a full list.

It’s time to look at one final Grails query option: raw HQL. You won’t need it often, but for the fringe cases, it’s a real lifesaver.


When should I use criteria queries and when dynamic finders?

The choice between criteria queries and dynamic finders comes down to developer preference. We’ve found that dynamic finders are fantastic for most query needs, and they’re probably your best first stop. You don’t have to sacrifice fetching strategy, ordering, or offsets, and they give you a clear and maintainable way of expressing queries.

But if you need to query on more than two attributes, to take advantage of query caching, or to express a query based on subrelationships (for example, find all Users that have a Profile with an email of [email protected]), criteria querying is your best bet.

Criteria queries also shine when generating a set of query constraints dynamically based on an HTML form (such as when users specify which fields to include in the form, and what the constraints should be, and you generate the criteria on the fly). We showed an example of this scenario in section 4.3.2.


4.3.4. Using HQL directly

If you’ve come from a Hibernate background, you’re probably used to expressing complex Hibernate queries using HQL—Hibernate’s query language. If you need the power of HQL from Grails, you can take full advantage of HQL directly from find() and findAll(). You should only need HQL for the most complex queries, and you will sacrifice quite a bit of maintenance and readability by using it. But there are situations where this is demanded.

A basic HQL query looks like this:

println User.findAll("from User u where u.userId = ?", [ "joe" ])

You can insert a question mark (?) as a placeholder for any pieces of the query that you need to supply dynamically. The final argument to the call is a list of substitutions that should replace the question-mark placeholders (the number of items in the list should match the number of question marks). In the preceding example, we’ve supplied “joe” as a static substitution, but in a real application you’d be more likely to supply a dynamic value, such as params.userId.


Mitigating against SQL injection attacks

One of the most common web security vulnerabilities is the SQL injection attack. In this type of attack, the user passes in malicious query parameters to alter the queries sent to the database (for example, ...?userId=abc123;delete+from +users;). This is why you should never do your own concatenations of HQL query strings. If you use the property placeholder (?) form of HQL queries, you will always be protected from these sorts of attacks.


But those question marks aren’t particularly readable. If you’ve ever used HQL, you may be familiar with named criteria. These let you replace those question marks with a more readable identifier prefixed with a colon (:). You can then provide a corresponding map of name/value pairs at the end of the parameter list to the call. Let’s try it again with named queries:

User.findAll("from User u where u.userId = :uid", [ uid: "joe" ])

If you’re in the middle of paginating, perhaps via the <g:paginate> tag, the usual max: and offset: fields can be used in addition to your named criteria:

User.findAll("from User u where u.userId like :uid",
[ uid: "j%", max: params.max, offset: params.offset ] )

HQL gives you a lot of power, and in some circumstances you will need to take advantage of it. But there aren’t many things that you can do in HQL that you can’t already do with criteria queries, so HQL should probably not be your first choice.

That concludes our whirlwind journey through Grails query options. There’s only one more aspect of domain models to explore—the bootstrapping of domain model reference data.

4.4. Bootstrapping reference data

Sometimes it’s useful to populate reference data in your database as soon as it’s created. For example, you might always want to create an admin user with a known password for system configuration. But if Grails is creating your database tables for you, how do you hook into the process?

The answer is found in /grails-app/conf/BootStrap.groovy. This file has two closures, init and destroy, which Grails invokes when the system starts and when it’s shut down, respectively.

During the bootstrapping process, you typically have different behavior for different environments (for example, to create several standard dummy users in development, but not in production). Grails allows you to tailor your bootstrap file in a perenvironment way.

In the Bootstrap.groovy file in listing 4.11, we create admin users for our development environment, but skip them in production.

Listing 4.11. Bootstrapping reference data based on the environment

You’ll notice that we check the current environment and only create our admin user if we’re running in development (that is, if we’re running via grails run-app) . We also test to see if our admin user exists in the database before we create it . Although the standard HSQLDB in-memory driver guarantees we’ll have an empty database every time, that won’t be the case if your target users switch to using a persistent database like MySQL. Always check before inserting sample data.

One final thing to note about bootstrapping is that there’s no guarantee that destroy() will be called. If the server terminates or other bad things happen, this closure may never execute. You’ve been warned.

4.5. Summary and best practices

In this chapter, we’ve explored Grails scaffolding and seen how we can make use of scaffolds to create an instant UI for our application. We also saw how to skin the application with our own CSS and layouts, and customize error messages and templates. We even added our own custom search actions right in with the generated templates to demonstrate scaffold flexibility.

Applying our knowledge of scaffolds to querying, we demonstrated lots of techniques for querying in Grails. Dynamic finders are great for creating two-field queries and keeping things maintainable. But for the situations where dynamic finders don’t cut it, we looked at advanced query techniques using criteria queries, projections, and even bare-metal HQL.

Let’s revisit some of the key ideas from this chapter, which you can take away and apply to your own Grails apps:

  • Use scaffolds for instant gratification and to stay motivated. Scaffolds give you a good feeling of progress and keep you focused on getting your app out the door. Also, don’t be afraid of using scaffolding code for admin screens in production code.
  • Understand your customization options. You can do a lot to make scaffold screens integrate with custom pages. Take advantage of CSS and layouts to skin them in a way that harmonizes with the rest of your app.
  • Dynamic finders are fantastic for two-field queries. Dynamic finders are self-documenting and easy for other developers to understand. If you need to query on more fields, criteria queries make more sense.
  • Use the Grails console. The Grails console is ideal for prototyping tricky dynamic finder queries, and it lets you try out lots of options without recompiling.
  • Harness the power of criteria queries. Criteria queries give you a lot more query power than dynamic finders, but they come at the price of a little complexity. Use criteria projections for grouping and reporting queries.
  • Always use named params for HQL. Using named params avoids SQL injection risks. Never build HQL query strings dynamically (use criteria queries for that).
  • Use bootstraps conditionally. Grails lets you perform different bootstrap operations in each environment, so make use of that. Don’t assume that your user’s development database is an in-memory one, and always check whether your sample data exists before re-creating it.

In the next chapter, we’ll take our domain modeling skills and apply them to working with forms and controllers in the UI layer. It’ll be a refreshing break from writing unit tests.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset