it in the id field. The version field is
configured to store a Hibernate-main-tained version number. They both
persist the name field and specify that
the accounts field represents a one-to-many relationship.
XML and annotations both have defaults for table and column names. The
table name defaults to the name of the
class and the column name defaults to
the name of the property. You can override these defaults using extra annotations or XML attributes and elements.
For example, you can specify the table
name using the @Table annotation
or the name attribute of the <class>
element.
Each approach has benefits and
drawbacks. One advantage that XML
has over annotations is that it separates
the O/R mapping from the Java code,
which decouples the domain classes
from Hibernate. One problem with this
separation is that it can be more difficult to keep the mapping and code in
sync. XML also tends to be more verbose
than annotations. Moreover, the XML
mapping must explicitly list all of the
persistent properties of a class, whereas fields of certain basic types such as
Customer.name are automatically
persistent when using annotations.
Another problem is that regardless of whether you are using XML or
annotations, you often need to add
fields to store the primary key and a
version number. The primary-key field
is usually required by Hibernate or by
a domain object’s clients. The version
number is used for optimistic locking.
The trouble with these fields, however,
is that typically the application’s business logic does not require them. They
must be added to every domain class
solely to support persistence.
O/R mapping in GORM. Grails relies
heavily on Convention over Configuration when defining ORM. It automatically treats classes in the grails app/
domain directory as being persistent.
GORM automatically persists the properties of each class. It defaults table
and column names from the class and
property names. GORM also adds primary-key and version-number properties to each class.
The following is an example domain class. The Customer class has a
field called name. Also, because this
field has default visibility, Groovy automatically defines the name property by
defining getName() and setName()
methods.
class Customer {
String name
}
you must write code to do this when using vanilla Hibernate.
GORM also makes it easy to map relationships by using static properties
to supply metadata in a similar fashion
to annotations in other languages. For
example, the static property hasMany
defines the one-to-many relationships
for a domain class. The value of the
hasMany property is a map. Each map
entry defines a one-to-many relationship: its key is the name of the property
that stores the collection, and its value
is the class of the collection elements.
For each one-to-many relationship
GORM adds a property to store the collection of objects, as well as methods
for maintaining the relationship.
The following is an example of how
to map a one-to-many relationship between the Customer class and the Account class.
GORM automatically maps the Customer class to the customer table and
maps the name property to the name
column. GORM adds an id property to
the class and maps it to a primary-key
column called id. It also adds a version property and maps it to a version column. Unlike a traditional ORM
framework, GORM requires very little
configuration, provided that the database schema matches the defaults.
Another nice feature of GORM is
that it will maintain creation and last
updated times for domain model classes. You simply have to define lastUp-dated and dateCreated properties
on your classes, and GORM will automatically update them. In comparison,
class Customer {
static hasMany =
[accounts : Account]
}
class Account {
static belongsTo =
Customer
Customer customer
}
figure 3.
(a)
Long pk = …
Session session = sessionFactory.getCurrentSession()
Account account = (Account) session.get( Account.class, pk)
(b)
interface AccountDao {
Account get(long accountId);
…
}
class AccountDaoImpl implements AccountDao {
public Account get(long accountId) {
Session session = sessionFactory.getCurrentSession()
return (Account) session.get( Account.class, pk)
}
}
The collection of accounts is stored
in a property called accounts, which
GORM adds to the Customer class at
runtime. The relationship is mapped using a foreign key called customer _ id
in the account table. The belongsTo
property specifies that a Customer
owns the account and it should be deleted if the customer is deleted.
GORM also dynamically defines a
couple of methods for managing this
relationship. The addToAccounts()
method adds an account to the collection, and the removeFromAccounts()
method removes an account. These
methods also maintain the inverse relationship from Account to Customer.
By automatically defining these methods, which would otherwise have to be
written by hand, GORM simplifies the
code and makes it less error prone.
Configuring the mapping. CoC reduces the amount of configuration
that is required. Sometimes, however,
you need to specify some aspects of the
ORM. For example, table or column
APriL2009 | voL. 53 | no. 4 | communicAtionS of the Acm
51