Chapter 13. Advanced GORM kung fu


In this chapter

  • Specialized domain class options
  • Caching, profiling, and optimizing tips
  • Dealing with multiple dataSource objects in one application
  • Legacy integration strategies

In chapter 3, you had your first exposure to Grails’ domain classes. Life was simple: we saved, we validated, and we used dynamic finds. You can go a long way with that knowledge—probably all the way to your first few publicly hosted Grails applications. But then you’ll hit the wall. As users flock to your stunning new social networking applications, you’ll wonder how you can tune your queries to run more efficiently. You’ll start to think about query-caching options. You’ll want to refactor your domain classes. And you’ll want to monitor where all your cycles are going. In short, you’ll want to take off the safety harness and get into some serious GORM kung fu. When you hit the performance and scalability wall, this is the chapter for you.

If you’re currently working in enterprise Java, you probably already have some legacy databases (perhaps with existing Hibernate mappings). We’ll explore the ins and outs of integrating Grails applications with your existing tables, JNDI data sources, and other things your enterprise might have lying around. But it’s no good understanding the high-level stuff if you don’t have some idea how the underlying engine works, so we’ll take a peek underneath to see how Grails interacts with Hibernate and how you can tune it to make it all faster.

13.1. Domain model kung fu

In chapter 3, we explored the common domain model relationships: 1:1, 1:m, and m:n. But as you develop your Grails applications, you’ll probably come across situations that don’t fit into those three standard relationships. In this section, we’re going to explore some of the less common domain modeling options, and we’ll start with inheritance.

13.1.1. Exploring inheritance options

Relational databases aren’t designed for object-oriented data, and inheritance relationships don’t map comfortably to a relational design. With that acknowledgment out of the way, what can GORM do about domain classes that use inheritance? GORM provides two basic options for handling inheritance: table-per-hierarchy and table-per-subclass approaches.

By default, GORM uses one table per hierarchy, which means that your base class and its subclasses are stored in the same table.

An example will make this clearer. Let’s modify Hubbub to support a special starred post that will always appear with a star and the reason it was starred. All of a user’s followers will notice these starred posts, marked with a special icon.

We could implement StarPost by hacking our existing Post object:

boolean starred
String reasonStarred

But if we did this, starred would be false for nearly every post, and reasonStarred would be null, which means that our constraints would have to be relaxed.

Let’s do the proper OO thing, and subclass our Post domain class, as shown in listing 13.1.

Listing 13.1. A StarPost class inheriting from Post

Our StarPost is now implemented, and it inherits everything from the base Post class. That means we also have to make modifications to our Tag class to explicitly mention the new relationship:

static belongsTo = [ User, Post, StarPost ]

With our Tag class updated and our StarPost ready to roll, it’s time to create an integration test to make sure that all the relationships work as we expect. Listing 13.2 shows an integration test for our new StarPost.

Listing 13.2. An integration test for our StarPost

When we have domain classes in a hierarchical relationship like this, we can take advantage of polymorphic queries. We can now retrieve all of a user’s posts , or just their starred posts , by taking advantage of the polymorphic query mechanisms that GORM dynamic finders give us for free.

We said that GORM uses one table per hierarchy by default. One of the disadvantages of this approach is that you can’t have a nullable constraint in any of your subclasses (because base classes do inserts in the same table as your subclasses). But this is a small limitation compared to the power of polymorphic queries.

What if you want to use one table per subclass? You can, but it requires a little GORM DSL magic. Add the following mapping to your root class (Post in our case):

static mapping = {
tablePerHierarchy false
}

If you do use one table per subclass, GORM will use outer joins to load instances, so performance can suffer if the class hierarchy is large. But you will be able to use nullable constraints on your domain class fields, so there’s a trade off.

13.1.2. Embedding domain classes

Sometimes you want the convenience of a domain object, but you don’t want the overhead of a separate table. This is particularly likely in legacy scenarios when you have a 1:1 mapping logically, but all the object properties are implemented as separate columns in one big table. For these situations, GORM lets you use embedding. Embedding lets you model one table as two or more related objects.

Let’s take the example of our Profile object. Each user has one Profile object containing their email address and other personal data. But suppose we decide we don’t want the overhead of managing a separate Profile table—we just want to embed the profile information directly in the User table. Listing 13.3 demonstrates how we could rework those classes to make that happen.

Listing 13.3. Embedded relationships stored in a single table

As you can see, embedding is done with the embedded construct. For this to work, you have to define the Profile class in the same Groovy file as the User class (User.groovy)—otherwise, you’ll end up with an empty Profile table in your database.

Embedding gives you a chance to keep your OO semantics without taking up unnecessary table space in your database.

13.1.3. Using maps for quick and dirty (or cheap and cheerful) tables

Although having a custom domain class manage your data is a logical approach, in some situations you don’t want the overhead of writing a custom class.

Let’s imagine that Hubbub supports custom plugins. Users can add these custom widgets to their Hubbub home page, and they configure them with various properties. We could create a domain class called PluginProperty with strings for the property name and value, but it’s a little contrived. All we want is a map to store our key/ value pairs.

For these cases, GORM lets us declare things as maps. Listing 13.4 shows an example of storing map-style properties.

Listing 13.4. Using maps for quick and dirty property storage

Behind the scenes, Grails stores pluginProperties as a Map of key/value pairs of type varchar(255), so you’re constrained to only storing strings.

Here’s how you might test it:

user = new User(userId: 'glen', password: 'notlong',
pluginProperties: ['colour':'green']).save()

user.pluginProperties = [ one: "1", two: "2" ]

Storing free-form string data in maps can quickly become a maintenance nightmare, so you should use this capability with caution. Still, it’s convenient for quick and dirty tables like pluginProperties.

13.1.4. Exploring domain model events

GORM handles a lot of the drudgery of getting data into and out of the database. But sometimes you need to integrate with GORM’s lifecycle to add your own features. For example, imagine implementing an audit capability that logs any changes to the domain model each time an object is modified. GORM events provide a mechanism to do just that, giving you a way to hook into GORM just before and after an object is saved or loaded.

GORM exposes seven main events:

  • beforeInsert
  • beforeUpdate
  • beforeDelete
  • afterInsert
  • afterUpdate
  • afterDelete
  • onLoad

If you want to catch any of these, define a closure with the corresponding name, like this:

def beforeDelete = {
log.warn "${id} is toast by ${jsec.principal}"
}

None of the closures take any arguments or return any values, so if you need some state, it will need to be on the domain class itself or retrievable via the current thread. (In the preceding example, we retrieve the JSecurity user from the current thread.)

GORM makes one more allowance around events, to handle the common case of timestamping. One of the most common uses of events is to timestamp the creation or modification of a domain class. For example, you might catch beforeUpdate and set the modification time (or catch beforeInsert and set the creation time). For these situations, GORM offers a convenience convention: if you name two Date fields on your object dateCreated and lastUpdated, GORM will automatically set these fields to the current time:

class User {
Date dateCreated
Date lastUpdated
String userId
// ...
}

This is a special case, but it’s one of the most common uses of events (along with audit logging), so it’s handy having it as a built-in feature. We took advantage of this in our Post object when we created it in chapter 3, and it’s been a real timesaver.

Now that we’ve taken you through some of the corner cases of domain modeling, it’s time to explore a few tricks and tips to improve your query performance.

13.2. Caching kung fu: moving from 2 users to 2^10

In previous chapters, we’ve issued complex queries without much concern for how hard the underlying database might be working to catch up. That’s fine if your site gets only a few hundred hits a day; but when you’re aiming your sights higher, you need to explore caching and performance tuning.

In this section, we’ll look at how GORM handles caching and see how you can determine which parts of your application would benefit from caching and when to tune the knobs. It all starts with understanding GORM’s use of Hibernate second-level caching.

13.2.1. Hibernate settings: should you use the second-level cache?

Whenever you interact with the object model in Hibernate, an internal first-level cache is always in play—the session. For the cases where you change multiple properties on a given object during a request (such as changing the user’s last login time and IP address), Hibernate can batch updates and execute a single SQL UPDATE.

Sometimes, the first-level cache is not enough. Take the scenario of the 1:m relationship (for example, each user having many posts). Every request in which you do a user.posts.each { } will result in a requery of the database. If your posts change infrequently (or not at all), that’s a lot of wasted querying.


Note

Calling user.posts.each twice in the same request will not requery the database because the collection is cached in the session.


For these scenarios, you can enable the second-level cache. When the second-level cache is enabled for a domain class, Hibernate first searches in this cache before looking in the database. Hibernate will also handle evicting objects from the cache when posts are added, edited, or deleted.

The only time you need to be careful about having a second-level cache in play is when you’re working with clustered application servers. In that scenario, you need to ensure that none of the servers is working with stale data by configuring your caching provider to be cluster-aware.

As a rule of thumb, you definitely want a second-level cache in play to make things more performant. But how do you configure and tune it? Before we start tuning, let’s configure the basic cache settings.

13.2.2. Cache configuration

Now that you know what a second-level cache does, it’s time to learn how to configure it for use in your Grails application. The first thing you need to get familiar with is DataSource.groovy. You saw this file before when we configured our database connection parameters, but it also includes a hibernate section that we haven’t explored yet. Check it out:

hibernate {
cache.use_second_level_cache=true
cache.use_query_cache=true
cache.provider_class='com.opensymphony.oscache.hibernate.OSCacheProvider'
}

Our Hibernate second-level cache is enabled, as is some kind of query cache (which we’ll get to later). A cache provider class is also specified—this tells Hibernate which underlying caching library to use (there are several). Grails uses OSCache by default, but we’ll switch to Ehcache because it’s a better caching library:

cache.provider_class='org.hibernate.cache.EhCacheProvider'

Note

Most Hibernate users will be familiar with Ehcache as a common underlying caching provider, so why does Grails use OSCache by default? It’s because OSCache plays more nicely when it’s reloaded by Grails in a development environment. If you’re used to using Ehcache, feel free to switch your caching provider to Ehcache when you’re ready to go to production.


Each caching provider has its own configuration mechanism. Ehcache uses a single XML file (ehcache.xml) that needs to be in the root of the classpath; place it in either the /src/java or the /grails-app/conf directory. Listing 13.5 shows you a sample ehcache.xml file to get you started.

Listing 13.5. A sample ehcache.xml configuration file

A full discussion of Ehcache is beyond the scope of this book, but these default settings will cache objects for 1 hour (3,600 seconds), after which they’ll be timed out and refreshed from the database when next queried.

Ehcache supports many configuration options including a distributed cache, the ability to persist the cache to disk to survive restarts, and various eviction policies for quiet or stale elements. The online documentation (http://ehcache.sourceforge.net/) is excellent, so check it out to learn more.

13.2.3. Caching individual domain classes

To enable caching for individual domain classes, a little more work is required. You need to add a mapping block to each domain class that you wish to cache, like this:

static mapping = {
cache true
}

Or, if you need more control over your caching options, you can pass in a map:

static mapping = {
cache: "read-write"
}

The cache setting refers to how Hibernate handles concurrent access to the underlying cache (see table 13.1). Remember that Hibernate writes to that cache too, every time you update, create, or delete a domain class instance. If you’re querying reference data, and you know there won’t be any updates to the data (if it contains a list of time zones, for example), you can safely set the cache setting to read-only for better performance.

Table 13.1. All the standard Hibernate caching strategies are supported by Grails; these are the common ones you’ll use.

Cache option

Description

true

The same as read-write caching

read-only

Only useful for static reference data; otherwise concurrency problems are inevitable

read-write

The default setting; implements concurrency on the cache

For Hubbub, our main caching requirements relate to a user’s posts. In those cases, we’ll want to employ caching strategies when we walk the object graph from User to Post. For this scenario, we’ll need to add some modifications to our User class:

static mapping = {
cache: true
posts cache:true
}

Tip

Remember that you have to enable caching in two places. First, you need to turn caching on globally in DataSource.groovy (this is true by default). Second, you have to enable caching for your individual domain classes.


But that’s only half of the story. Now that our Post object is cached, we may want to tune the timing of that particular cache. We need to revisit our ehcache.xml file and add a new cache element with a name that matches the name of the domain class:


Note

If you put your domain classes in packages, you need to put your package name in the name field too, as we did in the preceding example (com.grailsinaction.Post).


In the preceding settings, we set the idle time to 300 seconds (so that if no one accesses an instance in the cache in 300 seconds, it will be evicted) and a maximum time to live of 600 seconds (whether the object is active or not, it will always be discarded after 600 seconds). Those are fairly aggressive settings, but it’s a good starting point for our profiling exercise.


Tip

There’s no one-size-fits-all answer for caching strategy. The strategy you select is dependent on how objects are used in your system. If you’re in a low-write, high-read environment (like we are for a user’s posts in Hubbub), it makes sense to embrace caching at some level. When we cover performance profiling in the next section, you’ll learn about tools you can use to evaluate your caching needs.


Now that we’ve tuned all the settings on our cache, let’s look at profiling to see if we can find any bottlenecks in our application performance.

13.2.4. Enough talk, let’s profile

If you’re into a first-principles approach, your profiling journey may start with inspecting some of the SQL queries that your application generates. You can modify your DataSource.groovy file to print GORM queries to the console:

development {
dataSource {
logSql = "true"
}
}

That will give you some insight into query generation, but it won’t tell you how long your database took to satisfy the query. For that, you’ll need to do some query profiling, and the easiest way to do that in Grails is via P6Spy.

P6Spy is a small JDBC driver that wraps your real driver to provide a basic stopwatch. It then logs how many milliseconds the database took to satisfy your query. There’s even a Grails plugin for P6Spy, so let’s install it:

grails install-plugin p6spy

When the plugin is installed, it modifies your DataSource.groovy file to add a conveniently commented version of P6Spy as a potential database driver. Let’s update our development dataSource class to use the new P6Spy version:

Notice that we don’t change the URL—P6Spy will proxy using the username, password, and URL of our database. But we have to tell it the real driver to use, and for that we need to modify the P6Spy config file: spy.properties. The plugin creates a template file for us in the grails-app/conf directory. Have a look at it now. Notice the realdriver setting, which tells P6Spy which database it’s proxying:

realdriver=org.hsqldb.jdbcDriver

It has defaulted to our sample HSQLDB database, so we’re in business.

With our database profiling in place, let’s run Hubbub and see what profiling data we can collect. You’ll notice that you now have a spy.log file in the root directory of your project. Inside, you’ll find the output and timing of every query that went to your database. Here’s a single-line extract from spy.log:

13:40:23|0|1|statement||create table account_account (user_followers_id bigint, user_id bigint, user_following_id bigint)

This is good, but we want something more readable. Enter SQL Profiler: a small Swing application that processes P6Spy log files and shows which queries take the longest. You can download it from http://sourceforge.net/projects/sqlprofiler.

SQL Profiler listens on a socket for log data from P6Spy, so you have to reconfigure p6spy.properties to tell it where to log. You can find the customized p6spy.properties file in the Hubbub source code.

Once you have the logging configured, start SQL Profiler before you run grails run-app, and watch the logs flow as you interact with your application. Figure 13.1 shows the stream of SQL queries being processed by the profiler. Click the Pause button at the top of the window to see the results of the analysis.

Figure 13.1. The P6Spy profiler in action

SQL Profiler’s top pane shows the queries and how many milliseconds they took to run. Using HSQLDB as an in-memory database can mask performance issues, but using a real database (like PostgreSQL or MySQL) will generate values that are closer to what you’ll see in production. Clicking an entry in the top window populates the bottom pane with the full SQL of the query, so you can see which joins are used.

To get profiling data, use the Play and Pause buttons to profile just a few queries. Click Play, interact with a particular screen in your application, click Pause, and view the results in the middle pane. The middle pane tells you which queries took the longest time. The rightmost column tells you which table columns are used in the query (which is useful for identifying which columns would benefit from index creation).

To simulate how your database might perform under a real load, consider exploring a load-generation tool such as Apache JMeter (http://jakarta.apache.org/jmeter/). JMeter offers ways of simulating hundreds (or thousands) of users accessing your application concurrently, and it can be a great way to identify how your app performs under pressure.

P6Spy can give you basic database profiling information, and it’s great for working out which queries in your application could most benefit from caching or tuning. But once you have some insight into where the bottlenecks are, you’ll want to add some indexes. Let’s explore how that’s done in Grails.

13.2.5. Improving performance with indexed fields

Once your profiling efforts give you some insight into which retrievals are taking the most time, you should add indexes to your database to make lookups more efficient. Grails handles custom indexing via the versatile mapping closure (you saw this closure when configuring caching).

Suppose our reporting process does a lookup on the User table and creates some reports around dateCreated—the date the user signed up for our service. Given the popularity of our website, it makes sense to index on the dateCreated field. Creating the index involves giving the index a name (in our case, date_created_idx to keep the DBAs happy) and then attaching it to the domain class field we wish to index on:

static mapping = {
dateCreated index:'date_created_idx'
}

Using indexing and profiling with P6Spy can be a great way to identify which queries in your application might benefit from caching.

It’s now time to explore what facilities Grails offers to accelerate query performance through caching.


What if I need distributed caching?

Second-level caches are great, but traditionally they haven’t had the best support for clustering, making them significantly less useful in high-availability settings. Replication technologies were typically chatty and not very performant, but great progress has been made in this area. Commercial-strength distributed caches like the open source Terracotta platform now have excellent Grails support through plugins. Ehcache now has distributed cache options, too. If you’re running in a cluster, be sure to check them out.


13.2.6. What about query caching?

So far, we’ve talked a lot about second-level caching and how that can improve performance when navigating the object graph. But what about when you just want to cache queries?

Options are available for this, but it’s not likely they’ll be helpful. GORM will only cache query results if none of the tables included in the query have changed since your last query. If GORM has changed any of the items in any of the tables, the cached results will be discarded, and you’ll hit the database directly. In fact, you’ll also incur the overhead of the cache check.

But if you still want to make use of query caching, you have a few options. The first is to use a cache argument to dynamic finders, like this:

def entries = Post.findAllByUser(user, [cache: true])

This works great if the query that you need to execute can be expressed in a dynamic finder. Unfortunately, that’s often not the case, so you’re left with two options: criteria queries and HQL.

Criteria queries are the neatest solution and the one we recommend. Imagine you want to cache a user’s recent posts; specify the cacheable(true) setting, and you’re ready to go:

def entries = Post.createCriteria().list {
eq('user', user)
cacheable(true)
}

But remember, if either the Posts or Users table changes (not just for this user, but for anyone), this query will be evicted from the cache.

Criteria queries make this straightforward. But if you’re already using HQL, you can use Hibernate directly:

def recentPosts = {
def posts = Post.withSession { session ->
session.createQuery(
"select p from $Post.name p where p.user.id=:userid")
.setCacheable(true)
.setString("userid", params.userid)
.list()
}

[posts : posts ]
}

As you can see, this technique is more powerful but less elegant than dynamic finders or criteria queries, which we prefer for all caching queries (where practical).

By default, all queries are cached in a single cache called org.hibernate.cache.StandardQueryCache. You can define a cache with this name in your ehcache.xml file if you want to control how long the cache should live. If you don’t, it will use your defaultCache settings. Alternatively, you can use different cache regions for each of your cached queries.

To use cache regions, define a cache element in ehcache.xml with the name of the cache (such as hourlyCache). With this cache region defined, pass its name into your criteria:

def entries = Post.createCriteria().list {
eq('user', user)
cacheable(true)
cacheRegion('hourlyCache')
}

Alternatively, if you’re using HQL to perform your query, chain a call to setCacheRegion('hourlyCache') to get the same effect.

That’s a lot of query performance-enhancement options. But before we finish this section, there’s one server-level query performance enhancement we haven’t explored yet. JNDI data sources, provided by most servers, give you an efficient way to manage database connections. It’s time to explore this and other benefits they offer.

13.2.7. JNDI? That’s so old school...

If you’ve always lumped Java Naming and Directory Interface (JNDI) into the category of “old-school J2EE stuff that’s long past,” it’s time to have a second look. There are some compelling reasons to use JNDI data sources in your next Grails project—particularly for the production data source:

  • You don’t need to hardcode usernames, passwords, database names, or even types, making for a source-control-friendly check-in for your project even on public sites like GitHub, SourceForge, and Google Code.
  • It offers efficient pooling of resources by your server, including adding connections to the pool when needed and reconnecting dead instances.
  • Each developer can easily use their preferred DBMS without custom configuration and library files.

But JNDI has some drawbacks, too:

  • The JNDI naming format can differ from container to container.
  • Configuration for Jetty is problematic and requires a custom jetty-env.xml file in your /WEB-INF/ directory.

There’s almost no good reason to not use a JNDI data source for your production data source, and we encourage you to make this your first change after doing grails create-app. We’ve deployed small apps to production accidentally using an embedded HSQLDB, and we don’t want you to suffer the same pain.

Listing 13.6 shows an example of how JNDI data sources are configured for the Glassfish application server in DataSource.groovy. This configuration varies from server to server, so check the documentation.

Listing 13.6. Configuring JNDI data sources for use in Glassfish

Once you’ve updated your DataSource.groovy file, you’ll need to define the JNDI data source itself in your application server. Usually, your server admin console lets you create JNDI data sources and configure the database username, password, connection pools size, and so on.

That completes our survey of query and data source tricks and tips. It’s time now to look at legacy concerns. Integrating with legacy databases is an important part of enterprise development, so we’re going to spend the second half of this chapter exploring the common issues you’re likely to encounter when integrating your new Grails applications with historical data.

13.3. Legacy integration kung fu: dealing with multiple data sources

The Grails data source mechanism makes it easy to configure a single data source, but what if your application needs more than one? Take the example where you need to access a reference data table of countries and their codes in another team’s database.

The best way to handle this is via Burt Beckwith’s Datasources plugin, which lets you map different domain classes to different databases. You can still use dynamic finders, and Grails routes the query to the appropriate data source based on your configuration.

The first step is installing the plugin:

grails install-plugin datasources

Next, you need to specify which domain classes map to which data sources. The plugin uses a special file called /grails-app/conf/Datasources.groovy (note the plural) to define the alternative data sources and indicate which domain classes map to which data sources.

Listing 13.7 shows a sample definition of Datasources.groovy that loads two reference table classes from a separate PostgreSQL database. Most of the settings will be familiar, because you’ve seen them specified in the standard DataSource.groovy file.

Listing 13.7. Configuring multiple data sources in /grails-app/conf/Datasources.groovy

You can configure as many data sources as you like in this file and make appropriate entries in the domainClasses component to list each class. Once your definition is in place, you can then access your domain objects with the usual dynamic finders, such as CountryCodes.findByCode('au'). Save and update operations work as well.

The plugin is still under development, but it shows great promise as an easy way of integrating multiple data sources into one Grails application. Consult the online documentation for the plugin at http://grails.org/plugin/datasources for more information on the available configuration options.

Now that you know how to handle multiple data sources, the next legacy integration challenge is dealing with databases that have horrible table structures.

13.4. Dealing with difficult legacy databases

It would be nice if we could start with a clean data model each time, but life is rarely that simple. You’ll often have an existing database that your DBA won’t modify. Like all decent frameworks, Grails is comfortable playing nicely with your existing environment.

In this section, we’ll examine two techniques for migrating an existing database application to Grails. First, we’ll look at how you can reuse your existing Hibernate mappings and Java domain objects, and build a Grails UI in front. After that, we’ll look at the situation where you have an existing legacy database but no existing Hibernate mappings. In that case, you can skip the Hibernate step entirely and use the GORM legacy mapping DSL to map your Grails domain objects directly to your existing database schema.

But first, we’ll introduce you to a poorly constructed legacy database. This is a real data model found in a real enterprise (with the names changed to protect the innocent). This model has all the nasty surprises you’d expect: string-based natural keys, auto-incrementing keys not based on sequences, many-to-many tables with link attributes, several data types used for table keys (including char), and more. The basic layout of the data model is shown in figure 13.2.

Figure 13.2. The sample legacy database

Let’s step through the process of writing a simple one-page UI that lets us browse the data model. If you have Hibernate mappings for the database, it would be nice to reuse them. Let’s explore that option first.

13.4.1. Recycling Hibernate mappings

If you’re coming to GORM after using a lot of Hibernate, you’ll be familiar with Hibernate annotations and mapping files. You probably have .hbm.xml files and matching domain classes. There’s no need to throw them away.

Lurking quietly in the grails-app/conf/hibernate directory is a space for you to place your existing mapping files. Drop your domain classes in /src/java/ (or possibly as a JAR file in your /lib directory), and you’re in business. If you name your Hibernate configuration file /grails-app/conf/hibernate/hibernate.cfg.xml, there’s nothing more to do.

Listing 13.8 shows a sample hibernate.cfg.xml file for the legacy database in figure 13.2.

Listing 13.8. Reusing existing Hibernate XML mapping files
<?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 resource="org.hbm.xml"/>
</session-factory>
</hibernate-configuration>

Notice that there’s no data-source configuration like you typically see in hibernate.cfg.xml—this is already configured in the data-source definitions in Data-Source.groovy. But we do need to reference our existing org.hbm.xml file, which contains our Hibernate domain class mapping definitions for the sample database. We can use annotations to specify these mappings, but because this is a legacy setup, let’s assume we’re lost in a world of pointy XML.

Our org.hbm.xml file is a standard Hibernate mapping file. It specifies the mapping from a series of bizarrely named underlying tables to a set of standard Java classes. Listing 13.9 shows an extract of our mapping file to give you a feel for it.

Listing 13.9. An extract from a legacy Hibernate mapping file
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping auto-import="true" default-lazy="false"
package="com.grailsinaction.legacy.db">
<class name="Branch"
table="BK_BRANCH" mutable="false">
<cache usage="read-write"/>
<id name="name" column="BRANCH_NM">
<generator class="assigned"/>
</id>

<set name="sections" table="BK_BRANCH_TO_SECTION"
cascade="save-update">
<key column="BRANCH_NM"/>
<many-to-many class="Section" column="SECTION_ID"/>
</set>

<set name="files" table="BK_BRANCH_TO_FILE"
cascade="save-update">
<key column="BRANCH_NM"/>
<many-to-many class="File" column="FILE_ID" />
</set>

<one-to-one name="manager" class="Manager" cascade="save-update"/>

</class>

The syntax is ugly, but it does a lot of heavy lifting. The full set of mapping files for this domain is included in the book’s source code.

Pimp My Data Model: Adding an Ajax Interface

Suppose we’ve got our legacy model running by importing our existing Hibernate mappings, and we have access to our Java DTOs too. Let’s write some controller logic so we have a basic UI for browsing our domain model.

We don’t have scaffolding options because the standard scaffolds make some assumptions about the existence of ID fields, which we don’t have. We could customize the scaffolding templates, but that sounds like a lot of work for what we want. Let’s create a quick Ajax-based controller to browse the database.

Here’s our browser controller:

import com.grailsinaction.legacy.db.*

class BrowserController {

def index = { redirect(action: 'list') }

def list = {
// Pass through to Ajax browser form
}
}

There’s no rocket science here—we’re exposing a list endpoint to pass through to our Ajax form. In listing 13.10, we create our list.gsp with our Ajax-populated drop-down list.

Listing 13.10. Implementing a list.gsp with Ajax

Not much exciting stuff is happening there. We have a combo box full of available branch names and a Show Details button that submits the request to the controller and replaces a DIV with the returned value. Figure 13.3 shows the UI so far.

Figure 13.3. The Branch browser combo box launches an Ajax submit.

Let’s implement our controller logic to handle the submit and return some well-formed HTML. Using markup builder is the simplest way to generate the HTML, so let’s implement some code to return the manager for the selected branch. Listing 13.11 shows the initial backend code.

Listing 13.11. Implementing the Ajax backend to show branch details

One thing to notice in listing 13.11 is that we’re using a dynamic finder, Branch.findByName(), on our legacy DTO class. Grails has decorated our DTO with a proxy that implements the persistence and querying logic that Grails needs, so we get all this for free on our legacy classes.

With our markup builder in place to navigate our data model, let’s take the UI for a spin in figure 13.4.

Figure 13.4. Our basic manager details are in place.

Fantastic! We have our Ajax interface in action, but we haven’t exercised all the potential relationships that could be navigated via the model. Let’s fix that right now with a much more complete markup builder example. Listing 13.12 shows code that navigates the entire legacy model.

Listing 13.12. A more complete showDetails that navigates all relationships
def showDetails = {
def branch = Branch.findByName(params.id)

def writer = new StringWriter()
def html = new groovy.xml.MarkupBuilder(writer)

// Could do all this directly in a render() call
// but it's harder to debug
html.div {
div(id: 'manager') {
fieldset {
legend('Manager Details')
dl {
dt('Name:')
dd(branch.manager.name)
dt('Rating:')
dd(branch.manager.managementRating)
}
}
}

div(id: 'sections') {
branch.sections.each { section ->
fieldset {
legend('Section: ' + section.name)
dl {
dt('Start Date:')
dd(section.start)

dt('Files:')
dd(section.files.size())

section.files.each { sectToFile ->
dl(style:
'padding: 1em; border: 1px dotted black') {
dt('File Name: ')
dd(sectToFile.file.name)
dt('Type:')
dd(sectToFile.file.resourceType.name)
dt('Created:')
dd(sectToFile.start)
dt('Owner:')
dd(sectToFile.file.owner.name)
}
}
dt('Locations:')
dd(section.locations.size())
ul {
section.locations.each { sectToLoc ->
li(sectToLoc.location.name)
}
}
}
}
}
}
}
render(writer.toString())
}

That’s a much bigger builder, but this time we’ve got a full navigation graph to make sure everything is working correctly. Check out the updated UI in figure 13.5.

Figure 13.5. Navigating all relationships from the UI

We know that our legacy relationships are all in order, but we don’t want to settle for read-only access to our data. It’s time to explore our options for saving new objects to our legacy database, and it’s also time to investigate how we can retrofit some validation constraints in the process.


Scaffolding on legacy Hibernate domain classes (POJOs)

You may be wondering whether the UI scaffolding features we covered in chapter 4 apply to these Java classes that we’ve pulled in via Hibernate legacy mappings. Amazingly, this does work! You can set up your scaffolding from your Grails controller with the full name of the Java DTO class:

def scaffold = com.grailsinaction.legacy.db.User

Grails will have a go at scaffolding it. Be warned, though, that this almost never works without some tweaking. Grails scaffolding code makes assumptions about your domain classes (including that they’ll have an ID field called id). You can work around that by generating the template code for the scaffold controller and view (grails generate-all) and then editing by hand. It’ll still save you lots of time.


Adding Constraints to Legacy POJO Classes

Now that we have our legacy classes mapped, it’s time to look at some options for adding constraints. Even though our legacy classes are POJOs, Grails still gives us a convention-based facility for specifying legacy mappings.

You can’t add constraints closures to Java classes (at least not in JDK6), but Grails lets you place Groovy constraint scripts alongside your Java classes following the convention of DomainClassNameConstraints.groovy. Here’s a sample BranchConstraints.groovy file:

package com.grailsinaction.legacy.db

def constraints = {
name(size: 4..30)
}

Note that these classes are placed alongside your Java POJOs in /src/java (and not in /src/groovy, as you might expect). Also note that the constraints closure isn’t static as in GORM classes.

Let’s write a test case to confirm that our constraint is working as anticipated. Listing 13.13 shows a sample test.

Listing 13.13. Testing validation of legacy classes

If you run grails test-app, you’ll find this test passes, telling us that our constraint is working fine.

With our exploration of constraints complete, we’re now on top of all the options for Hibernate-based legacy integration. But what if you don’t have legacy Hibernate mappings? It’s time to look at what the GORM DSL offers for doing legacy mappings without all that XML.

13.4.2. Using GORM DSL to access existing database table structures

All this work around integrating legacy Hibernate mappings is fantastic if you have legacy Hibernate mappings to work with. But you might need to develop a snazzy UI for a legacy database that powers an old PHP application. For these situations, GORM offers a DSL for working with legacy databases.

By adding a mapping section to your domain classes (which you’ve already seen in section 13.2), you can change the name of the tables or columns, specify join tables, use custom key-generation strategies, and more. Behind the scenes, the GORM DSL configures the Hibernate mapping dynamically, so you can get to work with your domain classes and not get caught up in XML.

In this section, we’ll redevelop our legacy database example from the previous section using the GORM DSL. First, we’ll create a set of domain class objects in /grailsapp/domains, because we’re now going to be dealing with first-class Grails domain classes. We’ll keep the same package names, because we can reuse the controller code we’ve already developed to browse the new object model. Figure 13.6 shows the reworked domain classes, this time rewritten in Groovy.

Figure 13.6. Our new domain structure

With our domain model in place, it’s time to get acquainted with some of the mapping options available for hooking up legacy tables.

The Basics: Changing Table and Field Names

DBAs have a long history of creating bizarre naming standards, so the first thing we need to learn is mapping to custom field and table names. You can map to fields named whatever you like by taking advantage of the mapping block.

Let’s tackle something simple first, like the FileOwner object, which doesn’t have any relationships. Listing 13.14 shows the reworked domain class.

Listing 13.14. A domain class with a legacy mapping block

Our mapping closure introduces a few new constructs that you haven’t seen before. We can change the name of the backing table ; and by specifying property names in the block, we can remap them to their corresponding database columns.

Another thing to note is version false . Hibernate uses the version column to track object versions for optimistic locking. This isn’t implemented in our legacy tables, so we turned the feature off.

We have also defined an int-based id field (the default type is long) to make sure everything matches up to our int-based id field in the database . Alternatively, you can specify the type on the mapping itself (id column: 'FILE_OWNER_ID', type: 'integer'), which is convenient for some tricky timestamp formats or char fields (such as DB2’s yes_no character field to handle Booleans).

The id field uses one of the built-in Hibernate generators for generating key values. In this case, we’re using the increment strategy , which uses the next incrementing number, but you’re free to use any of the generators (which include sequences, hilo, or even assigned natural keys, which we’ll cover shortly).

That gets us through the basics of mapping custom field and table names, but what about the more thorny issue of navigating relationships? It’s time to dig a little deeper.

Adding One-to-Many Relationships

One of the most common relationship types is the one-to-many (or many-to-one) relationship. Our File object has several many-to-one relationships mapped.

Listing 13.15 demonstrates mapping this complex class using mapping DSL entries.

Listing 13.15. Mapping a complex relationship using the mapping DSL

FileType and FileOwner are the many-to-one relationships in this class. All that’s required is a mapping from the field name to the database column for the foreign key. Once your mapping is in place, you’re free to navigate the object graph like a standard Grails relationship.

Many-to-Many: Handling Join Tables

In our sample application, we need to model explicit join objects from SectionToFile. That’s because there are custom attributes on the join itself (in our case, start and end dates for the file’s ownership lifetime). Often, there are no attributes on the join, and you have a classic many-to-many with a join table containing only the two IDs for each object in the relationship.

We have pure join tables with no other attributes when linking sections to locations. The join table, BK_LOCATION_SECTION_MAP, has IDs for each side and no other attributes. Mapping this in the GORM DSL requires specifying a hasMany block and then adding an appropriate joinTable mapping. Listing 13.16 shows a mapping via a join table.

Listing 13.16. Mapping relationships that use join tables

Take special note of our locations attribute. It uses the joinTable attribute to specify the name of the many-to-many link table, and it also specifies the column (SECTION_ID) of this side of the relationship.

Corner Case: Handling Natural and Composite Keys

We still have some key-related territory to cover. Not all tables have the luxury of a surrogate primary key; many depend on either a natural key or a collection of fields that form a composite key. GORM DSL has support for both, in varying degrees.

In our example application, we have a SectionToFile join object that models attributes of the join between Section and File. Being a join table, it has neither a natural nor a surrogate key, but we can model the composite of both fields as our key. This is shown in listing 13.17. Note that Hibernate requires these linking objects to implement Serializable.

Listing 13.17. Handling existing link tables in many-to-many relationships

One thing that you lose with composite IDs is the ability to do get()s based on the composite ID. You need to use query by example to get back to your original object.

More tricky is the case of natural keys. In our Branch class, the name property forms the primary key for the table. Convincing GORM that this is a good thing involves a little sleight of hand, as shown in listing 13.18.

Listing 13.18. Handling natural keys with some GORM workarounds

GORM depends on having an id field, and it’s insistent that it appear in your mapping. In listing 13.18, we have a name field rather than id, so we rely on getters and setters to map our internal id field to a more public name field. It’s not pretty, but it does the job.

You have to be careful when saving objects mapped in this way, because Grails views a non-null id as indicating a persistent instance. You need to use the branch.save(insert: true) option when using this approach.

That rounds out our exploration of legacy data sources. We took you through a worst-case database, so you should feel confident that you can tackle any structure that a DBA can throw at you.


When to use GORM DSL, and when to use Hibernate mappings

Now that you’ve had a good look at both GORM DSL and Hibernate mappings for the same nasty database, you may be wondering what the best option is. Obviously, if you have existing Hibernate mappings, it makes sense to reuse them. But what if you have a brand-new web application that needs to integrate with a crazy legacy database? As with most things, the answer is “it depends.”

If your database makes extensive use of natural (non-integer) keys or join tables with attributes, using Hibernate mappings is usually cleaner. That said, if your team isn’t invested in Hibernate mappings, there’s almost always a GORM DSL solution (but it may require some compromises in your object model—you may end up introducing join objects with composite keys to map the relationship).

If your legacy database is fairly modern with lots of surrogate keys and not too much special sauce, GORM DSL is definitely the way to go. You’ll end up with a clean set of object classes, and you can live your life without the tedious pointy XML of Hibernate mappings.


13.5. Summary and best practices

We’ve introduced a lot of advanced GORM functionality in this chapter. Some of the less-common relationship types (including inheritance) can prove to be real timesavers (and code neateners), but you have to remember the pros and cons of each choice.

We’ve also taken you through the details of query and second-level caching, and we covered cache tuning in some detail.

We looked at some performance-measurement options for the data tier and explored a profiling tool to help you work out what to tune. Finally, we undertook a detailed range of options for getting Grails working with legacy databases (including reuse of existing Hibernate mapping files and the GORM legacy mapping DSL).

Before we move on to exploring Spring and transactions in chapter 14, let’s review a few key best practices from this chapter:

  • Understand inheritance. When evaluating your inheritance options, understand the potential performance cost of using a one-table-per-class strategy, and weigh that against the relaxed validation constraints required when using the one-table-per-hierarchy approach.
  • Use JNDI data sources. Use JNDI for production data sources (and change your database setting for production to JNDI straight after running create app).
  • Don’t guess, profile. P6Spy will give you good insight into your query timings. Combined with a load tool like JMeter, you can get pretty good simulations of how your app will perform if you get Slashdotted.
  • Don’t be overzealous with query caching, but do use second-level caches. Sometimes you will be better off rolling your own caching mechanism for high-churn data (such as for summary tables that update regularly but are backed by an expensive query). Always enable second-level caching (although there are always caveats to this, particularly when running in clusters).
  • Reuse Hibernate mappings. If you have existing Hibernate mappings for your legacy database, use them. GORM DSL requires compromises that aren’t worth it if you’ve already done the hard work of writing real Hibernate mappings.
  • Favor GORM DSL for well-designed database and Hibernate mappings for evil ones. For brand-new development on well-designed legacy databases, favor GORM DSL. There’s less to maintain, and it means your team doesn’t need to learn Hibernate. When working with legacy databases that have bizarre schemas, favor raw Hibernate mappings (particularly if you have existing Hibernate mapping files). Hibernate mappings have much more power than straight GORM DSL, and you’ll end up with a much cleaner solution.

In the next chapter, we’ll dive deep into the heart of Grails with a thorough exploration of Spring and transactions.

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

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