Table of Contents
| Ch. 1 | Introduction to mapping objects to relational databases | 1 |
| Ch. 2 | Introduction to Hibernate | 15 |
| Ch. 3 | Hibernate development environment | 23 |
| Ch. 4 | Database connecting and schema generation | 55 |
| Ch. 5 | Creating persistent classes | 87 |
| Ch. 6 | Working with collections | 127 |
| Ch. 7 | Using persistent objects | 161 |
| Ch. 8 | Hibernate query language | 185 |
| Ch. 9 | Hibernate caching | 207 |
| Ch. 10 | Hibernate transactions and locking | 221 |
| Ch. 11 | J2EE and Hibernate | 235 |
| Ch. 12 | Hibernate and DAO design pattern | 247 |
| Ch. 13 | Hibernate and XDoclet | 261 |
| Ch. 14 | Hibernate and Maven | 307 |
| Ch. 15 | Hibernate extensions | 329 |
| Ch. 16 | Hibernate and Eclipse | 363 |
| Ch. 17 | Using Velocity, Struts, and Hibernate | 373 |
| Ch. 18 | Hibernate and AspectJ | 389 |
| Ch. 19 | Hibernate interceptors | 407 |
| App. A | Hibernate database connectivity | 413 |
| App. B | Getting involved with Hibernate | 419 |
Read a Sample Chapter
Professional Hibernate
By Eric Pugh Joseph D. Gradecki John Wiley & Sons
ISBN: 0-7645-7677-1
Chapter One
Introduction to Mapping Objects to Relational Databases In the computer industry, we commonly have discussions, disagreements, and even shouting matches over which of the latest languages are the best and where they should be used. Discussions turn to the best platform currently available and who's suing whom. However, each of us is also thinking about the latest project we've been given and its strict deadline. Overall, the project isn't complex, but we have to support numerous database backend systems. This means we need to incorporate a persistence layer to handle the database differences. That part isn't too difficult, but what about the data itself? Do we just store it in some proprietary database and deal with the details later? No, we need to have a strategy that works with our application and the language the application is written in.
Today's programming languages take advantage of the latest in object-oriented techniques. As you know, an object is a construct to enforce encapsulation. The problem is how to store an object for use by the same application at a later time or by another application. Some of the common solutions to this problem are:
Serialization
XML
Object-oriented database systems mapping
Let's consider these possible solutions and determine their advantages and disadvantage before looking at the solution around which this book is written.
Serialization
You can save data stored in an object by creating a flat-file representation of the object. In the Java language, this process is called serialization. An object that is serialized has all its attributes and their class types saved into a file or a buffer. The resulting information string can be saved to a file, placed in a column in a traditional relational database, or sent to another machine. An example is the CD class:
public class CD implements Serializable { String title; String artist; public CD(String title, String artist) { this.title = title; this.artist = artist; } }
The CD class has two attributes that need to be saved when the object is serialized. In Java, all primitive types as well as many of the foundational classes are defined such that they implement the Serializable interface. The system automatically recurses into each of the attributes as needed.
The serialization of the CD class results in a binary representation of the data currently contained in the represented object. The binary representation could be placed in a BLOB column type of a relational database; this process would allow other applications to access the data. However, if a legacy application has been tweaked to access the column where the object is held, it won't be able to make sense of the data unless the legacy application can deserialize Java objects. Even worse, the serialization process doesn't migrate well from Java application to Java application.
Further, the serialization process isn't fast, and a large object can take a considerable amount of time to be put into its binary representation. Thus, serialization as a practice has a specific place in the development process; but as a mechanism for persisting data, it should be avoided.
XML
In the past few years, XML has been one of the hottest technologies. With this popularity comes the issue of using XML in both objects and mapping to a database. First, consider an XML document like the following:
Grace Under Pressure Rush
A database to handle the XML document can be created with the following schema:
create table cd ( title varchar, artist varchar );
From the XML, we can easily build a class like this:
public class cd { String title; String artist; }
Having the XML representation creates an additional level of complexity and processing required to go from an object to the database. This processing can be extensive, given the complexity of the objects in an application.
Object-Oriented Database Systems
When object-oriented programming first began to be used, the issue of storing objects for later use was an important topic. The most widely used component for storage is a database, so several companies started down the path of developing a new database technology used specifically to store objects. The object-oriented database system handles the storing and loading of objects in a transparent manner. The complexity in the system comes from querying the database. For example, we might have a query like this:
select x from user x where x.name = \"John Doe\"");
The database will access all the user objects in the database and return those whose name attribute is equal to "John Doe". The database needs to be designed in a manner that automatically allows the query to access the attributes of the stored objects.
Although these new database systems can transparently store and load objects to Java or other object-oriented languages, there is typically an issue when a legacy system or a quick RAD application needs to access the same information. In addition, the OO databases haven't made a large impact in the database market; they're still coming of age when compared to those offered by Oracle, Microsoft, and others.
Mapping
The three solutions we've just covered can work, but they present issue when put into the mix of legacy applications and traditional relational database systems. If you're working with an application that uses a database, you'll most likely need to use databases having names like Oracle, MySQL, Sybase, Microsoft SQL Server, and others. These databases are based on the traditional relational model; somehow we need to use them along with our Java objects.
An object can be placed in a relational database through the process of mapping. Mapping is a technique that places an object's attributes in one or more fields of a database table. For example, the earlier CD class has two attributes that would need to be mapped to a relational database table for permanent storage. The title and artist fields can be mapped to a schema like the following:
create table CD ( ID int not null primary key auto_increment, title varchar(256), artist varchar(256) );
The ID field is used to create unique rows in the database. Each title and artist field holds the appropriate value from the CD object. This is an example of a one-to-one mapping between the class and the database. Figure 1.1 shows the appropriate UML diagram to accompany the class and the resulting database table.
From a database standpoint, consider a CD object instantiated using the following constructor:
new CD("Grace Under Pressure", "Rush");
When the object is mapped to the database table defined earlier, the values in the database might look like this:
For any additional CD objects that are instantiated, new rows are created in the database; the ID column maintains their uniqueness.
Typically, the classes you're dealing with in a production application will include more than just simple attributes. Consider the following CD class:
public class CD implements Serializable { String title; String artist;
ArrayList tracks;
public CD(String title, String artist) { this.title = title; this.artist = artist;
tracks = new ArrayListO; }
public void addTrack(String track) { tracks. add(track); } }
In this new CD class, we've added an ArrayList to hold the name of each title on the CD. As you might expect, this additional attribute introduces a hidden complexity to the mapping between the objects instantiated from the CD class and the permanent storage: The ArrayList attribute can contain no values or hundreds of values. There might be 8 to 10 tracks on a CD; an MP3 CD could include hundreds of songs. In either case, we need to be able to map the ArrayList attribute into a database structure so it can be permanently recorded when the entire CD is committed.
A common solution is to create a second database table just for the attribute. For example, we might create a table like this:
create table CD_tracks ( ID int not null primary key. track varchar(256) );
Using the Rush CD object created earlier, we can make a couple of calls to the addTrack() method and fill the tracks ArrayList:
rush.addTrack("Distant Early Warning"); rush.addTrack("Afterimage"); rush.addTrack("Red Sector A");
When the CD object is saved to the database, information is placed in two different tables: a CD table
ID Title Artist
1 Grace Under Pressure Rush
and a CD_tracks table
ID Track
1 Distant Early Warning 2 Afterimage 3 Red Sector A
If we have another CD to store, should another track table be added to the system, or should the tracks be added to the existing CD_tracks table? If we add a track to the CD_tracks table, how do we keep track of the fact that some of the tracks relate to specific CDs in the CD table?
We need to add a foreign key to the CD_tracks table; it will relate to the primary key in the CD table. Here's what the new CD_tracks table looks like:
create table CD_tracks ( ID int not null primary key auto_increment, cd_id int, track varchar(256) );
Using this schema, the Rush CD's tracks appear as follows:
ID cd_id Track
1 1 Distant Early Warning 2 2 Afterimage 3 3 Red Sector A
With the addition of the cd_id column, we can relate the two tables needed to fully map the CD object to permanent storage. The addition of the new CD_tracks table expands our UML diagram, as shown in Figure 1.2.
Our CD class can be further complicated by adding an attribute based on another class type. For example:
public class CD implements Serializable { String title; String artist; ArrayList tracks;
public CD(String title, String artist) { this.title = title; this.artist = artist;
tracks = new ArrayList(); }
public void addTrack(Track track) { tracks.add(track); }
private class Track { String name; int length;
public track(String name, int length) { this.name = name; this.length = length; } } }
We've added another class called Track, which is added to the track ArrayList instead of a single string. With the Track class added to the class model, we have a new situation that needs to be handled through an appropriate mapping. The most obvious choice is to add another database table for mapping between the class and the permanent storage. For example:
create table tracks ( ID int not null primary key auto_increment, name varchar(256), length int );
The new database table looks similar to the CD_tracks database created in the previous example but includes a little more information. (We could have used the CD_tracks schema and added the length column.)
After these examples, it should be clear that saving a set of objects to permanent storage isn't a simple task. In order to correctly put an object's attributes in a database, you must have a clear plan in place with the proper databases and mappings. As you can see, though, once a class has been defined, the database tables and mappings become clear. Thus, in most design situations, the database modeling should occur after the classes have been defined.
Primary Keys, Timestamps, and Version Numbers
In the examples presented so far, all the database tables have included a primary key that isn't part of the original object being mapped. The primary key is needed in order for the database server to uniquely distinguish and manage the objects stored in the database. Without the primary key, the database might have duplicate rows in a table because two objects in the system have identical attribute values. The primary key also gives us the ability to determine whether or not an object has actually been added to the database and if the object needs to be updated.
Depending on the system used to handle the mapping from objects to permanent storage, there are different ways to determine whether an object is up to date. One way is to use a timestamp in the database table. When the persistence layer needs to determine whether an object should be persisted to the database, it can check the timestamp in the table row for the object. If the timestamp is less than a timestamp kept in the object itself, the persistence layer knows the object should be persisted. If the timestamps are the same, the object hasn't been changed and doesn't need to be saved.
Another technique involves a version number stored in the database. When the object is pulled from the database, it has an associated version number. If the application changes the object in any way, the persistence layer updates the version number by a single digit. The layer can then use the version number to determine whether the object needs to be persisted.
Handling Inheritance
Obtaining efficiencies in the software development process means using all of a methodology's features. This is especially true when you're developing the classes for an application. During the development of the class structure, a clear hierarchy can sometimes be created through inheritance. For example, for our CD class, we might have another class called SpecialEditionCD that inherits its foundational attributes from the CD class:
public class SpecialEditionCD extends CD { String newFeatures; int cdCount;
public SpecialEditionCD( String title, String artist, String newFeatures, int cdCount) { this.title = title; this.artist = artist; this.newFeatures = newFeatures; this.cdCount = cdCount; } }
The SpecialEditionCD class adds two more attributes that need to be persistent to our permanent storage in addition to the attributes from the CD parent class. We can't easily store the SpecialEditionCD information in the CD database table because of these new attributes. How do we perform the mapping? There are several solutions:
Create a single table using the attributes from the lowest child.
Create a table per class.
Create a table per concrete class.
Let's consider each of these inheritance mappings using the CD and SpecialEditionCD classes as examples.
Lowest-Child Inheritance Mapping
If we have a hierarchy of classes, our mapping needs to be able to handle both the CD and SpecialEditionCD classes using a single table.
Continues...
Excerpted from Professional Hibernate by Eric Pugh Joseph D. Gradecki Excerpted by permission.
All rights reserved. No part of this excerpt may be reproduced or reprinted without permission in writing from the publisher.
Excerpts are provided by Dial-A-Book Inc. solely for the personal use of visitors to this web site.