Get ahead
VMware offers training and certification to turbo-charge your progress.
Learn moreLast week, I described how Grails now treats plugins like normal dependencies that can be pulled from Maven-compatible repositories. Although this was the big new feature for 1.3, it wasn't the only one. In this post, I'll look at some of the others, starting with a feature that I only recently found out about.
Develop a non-trivial Grails application and you will soon realise that you often use the same queries again and again. What should you do? The copy-and-paste technique is simple but leaves you with major maintenance issues. You could write service methods for each of your common queries, but then you end up with quite fine-grained services and a pretty dumb domain model. The ideal place for these queries is on the domain classes themselves.
That's where named queries come in - a feature that slipped under the radar and into Grails 1.2.
The basic idea is that a report can be about one or more servers and it may be generated one or more times a month. So dow stands for "day of week" and wom stands for "week of month". Each server has an associated location, which is just the name of a city in this much-reduced model.
Now consider what sort of information an application might want to extract from this model: perhaps all reports generated in the first week of the month, or all reports about a particular server. We could add static methods to the Report class to provide such queries, but named queries give us some additional benefits that you'll see later.
Creating a named query is straightforward, as you would expect with Grails. Simply add a static namedQueries property to the relevant domain class and assign a closure to it:
class Report {
String name
static hasMany = [frequencies: Frequency, servers: Server]
static namedQueries = {
inFirstWeek {
frequencies {
eq("wom", 1)
}
}
inWeek { wom ->
frequencies {
eq("wom", wom)
}
}
dilbertsReports {
servers {
eq("mgrEmail", "[email protected]")
}
}
inCity { city ->
servers {
location {
eq("city", city)
}
}
}
}
The code above sets up four queries: inFirstWeek, inWeek, dilbertsReports, and inCity. You can then use them in the same places that you can use dynamic finders, for example in controller actions or service methods. If you want to retrieve all reports that are generated in the first week of the month, call the relevant named query like so:
Report.inFirstWeek.list()
If you want all reports generated in one of the other weeks of the month, then use inWeek instead:
Report.inWeek(2).list()
See how you can pass arguments to the named queries? Just make sure that your named query closure declares the appropriate number of arguments.
Hopefully you can see how easy it is to both declare and use named queries, but before I move on, a few points deserve clarification.
First, you must write the queries in Grails' criteria DSL. If you've been putting off learning about the criteria DSL, you now have a good reason to cease procrastinating!
Second, you invoke the DSL as a static property (if you aren't passing any arguments to it) or method, followed by a standard GORM retrieval method, such as list(), get(), or a dynamic finder. That means you can add extra filtering to your named queries. It's also worth pointing out that get() will only return a domain instance if the result of the named query contains the required entity. Otherwise, get() simply returns null.
In other words, let's say the inFirstWeek query returns the domain instances with IDs of 1, 3, and 6. Then
Report.inFirstWeek.get(3)
will return the domain instance with ID of 3, whereas
Report.inFirstWeek.get(2)
will return null, even if Report.get(2) returns a real domain instance. So the named query acts like a filter.
So far, so good. The way that the named queries combine with get(), list(), and dynamic finders may be enough of a reason to use them right away. But Grails 1.3 includes another trick up its sleeve.
Report.dilbertsReports.inFirstWeek.list()
Alternatively, if I want all first week reports about servers in London, I can use
Report.inFirstWeek.inCity("London").list()
In fact, you can chain as many named queries as you like, as long as they all return the same type of domain class.
Named queries provide a powerful technique for query reuse that is both simple to implement and to use. You can now have a very rich domain model with client code that is easy to read and understand. How good is that?
Now I'd like to quickly look at some other Grails 1.3 features.
def book = Book.get(10)
assert !book.dirty
book.title = "Unknown"
assert book.dirty
assert book.isDirty("title")
assert !book.isDirty("author")
See how you can also check whether individual fields have been modified?
grails.sitemesh.default.layout = 'defaultLayout'
or by creating the file grails-app/views/layouts/application.gsp. The first approach would pick up the layout from grails-app/views/layouts/defaultLayout.gsp.
With that, I'll wrap up this instalment of Grails 1.3 features. I hope you can make good use of the named queries! Next time, I'll look at in-place plugins.