BUY THIS BOOK
Add to Cart

Print Book $24.95


Add to Cart

Print+PDF $32.44

Add to Cart

PDF $19.99

Safari Books Online

What is this?

Add to UK Cart

Print Book £17.50

What is this?

Looking to Reprint or License this content?


Hibernate: A Developer's Notebook
Hibernate: A Developer's Notebook By James Elliott
May 2004
Pages: 190

Cover | Table of Contents | Colophon


Table of Contents

Chapter 1: Installation and Setup
It continues to amaze me how many great, free, open source Java™ tools are out there. When I needed a lightweight object/relational mapping service for a JSP e-commerce project several years ago, I had to build my own. It evolved over the years, developed some cool and unique features, and we've used it in a wide variety of different contexts. But now that I've discovered Hibernate, I expect that I'll be using it on my next project instead of my own familiar system (toward which I'll cheerfully admit bias). That should tell you how compelling it is!
Since you're looking at this book, you're interested in a powerful and convenient way to bridge between the worlds of Java objects and relational databases. Hibernate fills that role very nicely, without being so big and complicated that learning it becomes a daunting challenge in itself. To demonstrate that, this chapter guides you to the point where you can play with Hibernate and see for yourself why it's so exciting.
In this chapter.
  • Getting an Ant Distribution
  • Getting the HSQLDB Database Engine
  • Getting Hibernate
  • Setting Up a Project Hierarchy
If you're not already using Ant to manage the building, testing, running, and packaging of your Java projects, now is the time to start. The examples in this book are Ant driven, so you'll need a working Ant installation to follow along and experiment with variations on your own system, which is the best way to learn.
First of all, get an Ant binary and install it.
We chose to structure our examples around Ant for several reasons. It's convenient and powerful, it's increasingly (almost universally) the standard build tool for Java-based development, it's free and it's cross-platform. Many of the example and helper scripts in the current Hibernate distribution are Windows batch files, which don't do any good for people like me who live in a Unix world. Using Ant means our examples can work equally well anywhere there's a Java environment, which means we don't have to frustrate or annoy any readers of this book. Happily, it also means we can do many more cool things with less effort—especially since several Hibernate tools have explicit Ant support, which we'll show how to leverage.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Getting an Ant Distribution
If you're not already using Ant to manage the building, testing, running, and packaging of your Java projects, now is the time to start. The examples in this book are Ant driven, so you'll need a working Ant installation to follow along and experiment with variations on your own system, which is the best way to learn.
First of all, get an Ant binary and install it.
We chose to structure our examples around Ant for several reasons. It's convenient and powerful, it's increasingly (almost universally) the standard build tool for Java-based development, it's free and it's cross-platform. Many of the example and helper scripts in the current Hibernate distribution are Windows batch files, which don't do any good for people like me who live in a Unix world. Using Ant means our examples can work equally well anywhere there's a Java environment, which means we don't have to frustrate or annoy any readers of this book. Happily, it also means we can do many more cool things with less effort—especially since several Hibernate tools have explicit Ant support, which we'll show how to leverage.
I used to wonder why people bothered with Ant when they could use Make. Now that I've seen how well it manages Java builds, I feel lost without it.
To take advantage of all this, you need to have Ant installed and working on your system.
Download a binary release of Ant from http://ant.apache.org/bindownload.cgi. Scroll down to find the current release of Ant, and download the archive in a format that's convenient for you to work with. Pick an appropriate place for it to live, and expand the archive there. The directory into which you've expanded the archive is referred to as ANT_HOME. Let's say you've expanded the archive into the directory /usr/local/apache-ant-1.5.1; you may want to create a symbolic link to make it easier to work with, and to avoid the need to change any environment configuration when you upgrade to a newer version:
	/usr/local $ ln -s apache-ant-1.5.1 ant
Once Ant is situated, you need to do a couple of things to make it work right. You need to add its bin directory in the distribution (in our example,
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Getting the HSQLDB Database Engine
Hibernate works with a great many relational databases; chances are, it will work with the one you are planning to use for your next project. We need to pick one to focus on in our examples, and luckily there's an obvious choice. The free, open source, 100% Java HSQLDB project is powerful enough that it forms the backing storage for several of our commercial software projects. Surprisingly, it's also incredibly self-contained and simple to install, so it's perfect to discuss here. (If you've heard of Hypersonic SQL, this is its current incarnation. Much of the Hibernate documentation uses the older name.)
Don't panic if you end up at http://hsql.sourceforge.net/ and it seems like the project has been shutdown. That's the wrong address—it's talking about the predecessor to the current HSQLDB project. Use the address below to find the current version of the database engine.
Examples based on a database that everyone can download and easily experiment with mean you won't have to translate any of the SQL dialects or operating system commands to work with your available databases (and may even mean you can save a day or two learning how to download, install, and configure one of the more typical database environments). Finally, if hsqldb i new to you, chances are good you'll be impressed and intrigued, and may well end up using it in your own projects. As it says on the project home page (at http://hsqldb.sourceforge.net):
hsqldb is a relational database engine written in Java, with a JDBC driver, supporting a rich subset of ANSI-92 SQL (BNF tree format). It offers a small (less than 160k), fast database engine which offers both in memory and disk based tables. Embedded and server modes are available. Additionally, it includes tools such as a minimal web server, in-memory query and management tools (can be run as applets), and a number of demonstration examples.
Go on, download HSQLDB. Heck, take two, they're small!
Getting the database is simply a matter of visiting the project page at http://hsqldb.sourceforge.net" and clicking the link to download the current stable version. This will take you to a typical SourceForge downloads page with the current release highlighted. Pick your mirror and download the zip archive. There's nothing to install or configure; we'll show you how to use it shortly.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Setting Up a Project Hierarchy
Although we're going to start small, once we start designing data structures and building Java classes and database tables that represent them, along with all the configuration and control files to glue them together and make useful things happen, we're going to end up with a lot of files. So let's start out with a good organization from the beginning. As you'll see in this process, between the tools you've downloaded and their supporting libraries, there are already a significant number of files to organize.
If you end up building something cool by following the examples in this book, and want to turn it into a real application, you'll be in good shape from the beginning. More to the point, if you set things up the way we describe here, the commands and instructions we give you throughout the examples will make sense and actually work. Many examples also build on one another throughout the book, so it's important to get on the right track from the beginning.
If you want to skip ahead to a later example, or just avoid typing some of the longer sample code and configuration files, you can download "finished" versions of the chapter examples from the book's web site. These downloads will all be organized as described here.
Here's how:
  1. Pick a location on your hard drive where you want to play with Hibernate, and create a new folder, which we'll refer to from now on as your project directory.
  2. Move into that directory, and create subdirectories called src, lib, and data. The hierarchy of Java source and related resources will be in the src directory. Our build process will compile it into a classes directory it creates, as well as copy any runtime resources there. The data directory is where we'll put the HSQLDB database, and any Data Definition Language (DDL) files we generate in order to populate it.
    The lib directory is where we'll place third-party libraries we use in the project. For now, copy the HSQLDB and Hibernate JAR files into the lib directory.
  3. If you haven't already done so, expand the HSQLDB distribution file you downloaded earlier in this chapter. You'll find
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Chapter 2: Introduction to Mapping
Now that we're in a position to work with Hibernate, it's worth pausing to reflect on why we wanted to in the first place, lest we remain lost in the details of installation and configuration. Object-oriented languages like Java provide a powerful and convenient abstraction for working with information at runtime in the form of objects that instantiate classes. These objects can link up with each other in a myriad of ways, and they can embody rules and behavior as well as the raw data they represent. But when the program ends, all the objects swiftly and silently vanish.
In this chapter:
  • Writing a Mapping Document
  • Generating Some Class
  • Cooking Up a Schema
  • Connecting Hibernate to MySQL
For information we need to keep around between runs, or share between different programs and systems, relational databases have proven to be hard to beat. They're scalable, reliable, efficient, and extremely flexible. So what we need is a means of taking information from a SQL database and turning it into Java objects, and vice versa.
There are many different ways of doing this, ranging from completely manual database design and coding, to highly automated tools. The general problem is known as Object/Relational Mapping, and Hibernate is a lightweight O/R mapping service for Java.
The "lightweight" designation means it is designed to be fairly simple to learn and use, and to place reasonable demands on system resources, compared to some of the other available tools. Despite this, it manages to be broadly useful and deep. The designers have done a good job of figuring out the kinds of things that real projects need to accomplish, and supporting them well.
You can use Hibernate in many different ways, depending on what you're starting with. If you've got a database that you need to interact with, there are tools that can analyze the existing schema as a starting point for your mapping, and help you write the Java classes to represent the data. If you've got classes that you want to store in a new database, you can start with the classes, get help building a mapping document, and generate an initial database schema. We'll look at some of these approaches later.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Writing a Mapping Document
Hibernate uses an XML document to track the mapping between Java classes and relational database tables. This mapping document is designed to be readable and hand-editable. You can also start by using graphical CASE tools (like Together, Rose, or Poseidon) to build UML dia grams representing your data model, and feed these into AndroMDA (http://www.andromda.org/), turning them into Hibernate mappings.
Don't forget that Hibernate and its extensions let you work in other ways, starting with classes or data if you've got them.
We'll write one by hand, showing it's quite practical.
We're going to start by writing a mapping document for tracks, pieces of music that can be listened to individually or as part of an album or play list. To begin with, we'll keep track of the track's title, the path to the file containing the actual music, its playing time, the date on which it was added to the database, and the volume at which it should be played (in case the default volume isn't appropriate because it was recorded at a very different level than other music in the database).
You might not have any need for a new system to keep track of your music, but the concepts and process involved in setting up this mapping will translate to the projects you actually want to tackle.
Fire up your favorite text editor, and create the file Track.hbm.xml in the src/com/oreilly/hh directory you set up in the previous chapter. (If you skipped that chapter, you'll need to go back and follow it, because this example relies on the project structure and tools we set up there.) Type in the mapping document as shown in . Or, if you'd rather avoid all that typing, download the code examples from this book's website, and find the mapping file in the directory for .
Example 2-1. The mapping document for tracks, Track.hbm.xml
 1  <?xml version="1.0"?>
 2  <!DOCTYPE hibernate-mapping 
 3            PUBLIC "-//Hibernate/Hibernate Mapping DTD 2.0//EN"
 4            "http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd">
 5  <hibernate-mapping>
 6
 7    <class name="com.oreilly.hh.Track" table="TRACK"> 
 8      <meta attribute="class-description">
 9        Represents a single playable track in the music database.
10        @author Jim Elliott (with help from Hibernate) 
11      </meta>
12
13      <id name="id" type="int" column="TRACK_ID"> 
14        <meta attribute="scope-set">protected</meta> 
15        <generator class="native"/> 
16      </id> 
17
18      <property name="title" type="string" not-null="true"/>
19
20      <property name="filePath" type="string" not-null="true"/>
21
22      <property name="playTime" type="time">
23        <meta attribute="field-description">Playing time</meta>
24      </property>
25
26      <property name="added" type="date"> 
27        <meta attribute="field-description">When the track was created</meta>
28      </property>
29
30      <property name="volume" type="short">
31        <meta attribute="field-description">How loud to play the track</meta>
32      </property>
33
34    </class>
35  </hibernate-mapping>
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Generating Some Class
Our mapping contains information about both the database and the Java class between which it maps. We can use it to help us create both. Let's look at the class first.
The Hibernate Extensions you installed in included a tool that can write Java source matching the specifications in a mapping document, and an Ant task that makes it easy to invoke from within an Ant build file. Edit build.xml to add the portions shown in bold in .
Example 2-2. The Ant build file updated for code generation
 1  <project name="Harnessing Hibernate: The Developer's Notebook"	
 2           default="db" basedir=".">
 3    <!-- Set up properties containing important project directories -->
 4    <property name="source.root" value="src"/>
 5    <property name="class.root" value="classes"/>
 6    <property name="lib.dir" value="lib"/>
 7    <property name="data.dir" value="data"/>
 8
 9    <!-- Set up the class path for compilation and execution -->
10    <path id="project.class.path">
11        <!-- Include our own classes, of course -->
12        <pathelement location="${class.root}" />
13        <!-- Include jars in the project library directory -->
14        <fileset dir="${lib.dir}">
15          <include name="*.jar"/>
16        </fileset>
17    </path>
18
19    <target name="db" description="Runs HSQLDB database management UI
20  against the database file--use when application is not running">
21        <java classname="org.hsqldb.util.DatabaseManager"
22              fork="yes">
23           <classpath refid="project.class.path"/>
24           <arg value="-driver"/>
25           <arg value="org.hsqldb.jdbcDriver"/>
26           <arg value="-url"/>
27           <arg value="jdbc:hsqldb:${data.dir}/music"/>
28           <arg value="-user"/>
29           <arg value="sa"/>
30        </java>
31    </target>
32
33    <!-- Teach Ant how to use Hibernate's code generation tool -->
34    <taskdef name="hbm2java"
35             classname="net.sf.hibernate.tool.hbm2java.Hbm2JavaTask"
36             classpathref="project.class.path"/>
37
38    <!-- Generate the java code for all mapping files in our source tree -->
39    <target name="codegen"
40            description="Generate Java source from the O/R mapping files">
41      <hbm2java output="${source.root}">
42        <fileset dir="${source.root}">
43            <include name="**/*.hbm.xml"/>
44        </fileset>
45      </hbm2java>
46    </target>
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Cooking Up a Schema
That was pretty easy, wasn't it? You'll be happy to learn that creating database tables is a very similar process. As with code generation, you've already done most of the work in coming up with the mapping document. All that's left is to set up and run the schema generation tool.
The first step is something we alluded to in . We need to tell Hibernate the database we're going to be using, so it knows the specific "dialect" of SQL to use. SQL is a standard, yes, but every database goes beyond it in certain directions and has a specific set of features and limitations that affect real-life applications. To cope with this reality, Hibernate provides a set of classes that encapsulate the unique features of common database environments, in the package net.sf.hibernate.dialect. You just need to tell it which one you want to use. (And if you want to work with a database that isn't yet supported "out of the box," you can implement your own dialect.)
In our case, we're working with HSQLDB, so we want to use HSQLDialect. The easiest way to configure Hibernate is to create a properties file named hibernate.properties and put it at the root level some-where in the class path. Create this file at the top level of your src directory, and put the lines shown in into it.
Example 2-4. Setting up hibernate.properties
hibernate.dialect=net.sf.hibernate.dialect.HSQLDialect
hibernate.connection.driver_class=org.hsqldb.jdbcDriver
hibernate.connection.url=jdbc:hsqldb:data/music
hibernate.connection.username=sa
hibernate.connection.password=
You can use an XML format for the configuration information as well, but for the simple needs we have here, it doesn't buy you anything.
In addition to establishing the SQL dialect we are using, this tells Hibernate how to establish a connection to the database using the JDBC driver that ships as part of the HSQLDB database JAR archive, and that the data should live in the data directory we've created—in the database named music. The username and empty password (indeed, all these values) should be familiar from the experiment we ran at the end of .
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Connecting Hibernate to MySQL
If you were skimming through this chapter (or, more likely, the table of contents) you may not have even noticed that Hibernate connected to and manipulated a database in the previous section, "Cooking Up a Schema." Since working with databases is the whole point of Hibernate, it makes this as easy as possible. Once you've set up a configuration file like the one in , the schema generation tool can get in and work with your database, and your Java code can use it for persistence sessions as demonstrated in .
This example assumes you've already got a working MySQL instance installed andrunning, since explaining how to do that would be quite a detour.
In the interest of further clarifying this aspect of working with Hibernate, let's take a look at what we'd change in that example to set up a connection with the popular, free, and open source MySQL database (available from http://www.mysql.com).
Connect to your MySQL server and set up a new database to play with, along the lines of .
Example 2-8. Setting up the MySQL database notebook_db as a Hibernate playground
% mysql -u root -p
Enter password:
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 764 to server version: 3.23.44-Max-log

Type 'help;' or '\h' for help. Type '\c' to clear the buffer.

mysql> CREATE DATABASE notebook_db;
Query OK, 1 row affected (0.00 sec)

mysql> GRANT ALL ON notebook_db.* TO jim IDENTIFIED BY "s3cret";
Query OK, 0 rows affected (0.20 sec)

mysql> quit;
Bye
Hopefully you'll use a less guessable password than this in your real databases!
Make a note of the database name you create, as well as the username and password that can access to it. These will need to be entered into hibernate.properties, as shown in .
Next, you'll need a JDBC driver capable of connecting to MySQL. If you're already using MySQL for your Java projects, you'll have one. Otherwise, you can download Connector/J from http://www.mysql.com/downloads/api-jdbc-stable.html. However you obtain it, copy the driver library jar (which will be named something like mysql-connector-java-3.0.10-stable-bin.jar)
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Chapter 3: Harnessing Hibernate
All right, we've set up a whole bunch of infrastructure, defined an object/relational mapping, and used it to create a matching Java class and database table. But what does that buy us? It's time to see how easy it is to work with persistent data from your Java code.
In this chapter:
  • Creating Persistent Objects
  • Finding Persistent Objects
  • Better Ways to Build Queries
Let's start by creating some objects in Java and persisting them to the database, so we can see how they turn into rows and columns for us. Because of the way we've organized our mapping document and properties file, it's extremely easy to configure the Hibernate session factory and get things rolling.
To get started, set up the Hibernate environment and use it to turn some new Track instances into corresponding rows in the database table.
This discussion assumes you've created the schema and generated Java code by following the examples in . If you haven't, you can start by downloading the examples archive from this book's web site, jumping in to the ch03 directory, and copying in the third-party libraries as instructed in . Once you've done that, use the commands ant codegen followed by ant schema to set up the generated Java code and database schema on which this example is based. As with the other examples, these commands should be issued in a shell/command window whose current working directory is the top of your project tree, containing Ant's build.xml file.
We'll start with a simple example class, CreateTest, containing the necessary imports and housekeeping code to bring up the Hibernate environment and create some Track instances that can be persisted using the XML mapping document we started with. The source is shown in .
The examples in most chapters build on the previous ones, so if you are skipping around, you'll really want to download the sample code.
Example 3-1. CreateTest.java
 1   package com.oreilly.hh;
 2
 3   import net.sf.hibernate.*;
 4   import net.sf.hibernate.cfg.Configuration;
 5
 6   import java.sql.Time;
 7   import java.util.Date;
 8
 9   /**
10    * Create sample data, letting Hibernate persist it for us.
11    */
12   public class CreateTest {
13
14       public static void main(String args[]) throws Exception {
15           // Create a configuration based on the properties file we've put
16           // in the standard place.
17           Configuration config = new Configuration();
18
19           // Tell it about the classes we want mapped, taking advantage of
20           // the way we've named their mapping documents.
21           config.addClass(Track.class);
22
23           // Get the session factory we can use for persistence
24           SessionFactory sessionFactory = config.buildSessionFactory();
25
26           // Ask for a session using the JDBC information we've configured
27           Session session = sessionFactory.openSession();
28           Transaction tx = null;
29           try {
30               // Create some data and persist it
31               tx = session.beginTransaction();
32
33               Track track = new Track("Russian Trance",
34                                       "vol2/album610/track02.mp3",
35                                       Time.valueOf("00:03:30"), new Date(),
36                                       (short)0);
37               session.save(track);
38
39               track = new Track("Video Killed the Radio Star",
40                                 "vol2/album611/track12.mp3",
41                                 Time.valueOf("00:03:49"), new Date(),
42                                 (short)0);
43               session.save(track);
44
45
46              track = new Track("Gravity's Angel",
47                                "vol2/album175/track03.mp3",
48                                Time.valueOf("00:06:06"), new Date(),
49                                (short)0);
50             session.save(track);
51
52             // We're done; make our changes permanent
53             tx.commit();
54
55         } catch (Exception e) {
56             if (tx != null) {
57                 // Something went wrong; discard all partial changes
58                 tx.rollback();
59             }
60             throw e;
61         } finally {
62             // No matter what, close the session
63             session.close();
64         }
65
66         // Clean up after ourselves
67         sessionFactory.close();
68      }
69   }
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Creating Persistent Objects
Let's start by creating some objects in Java and persisting them to the database, so we can see how they turn into rows and columns for us. Because of the way we've organized our mapping document and properties file, it's extremely easy to configure the Hibernate session factory and get things rolling.
To get started, set up the Hibernate environment and use it to turn some new Track instances into corresponding rows in the database table.
This discussion assumes you've created the schema and generated Java code by following the examples in . If you haven't, you can start by downloading the examples archive from this book's web site, jumping in to the ch03 directory, and copying in the third-party libraries as instructed in . Once you've done that, use the commands ant codegen followed by ant schema to set up the generated Java code and database schema on which this example is based. As with the other examples, these commands should be issued in a shell/command window whose current working directory is the top of your project tree, containing Ant's build.xml file.
We'll start with a simple example class, CreateTest, containing the necessary imports and housekeeping code to bring up the Hibernate environment and create some Track instances that can be persisted using the XML mapping document we started with. The source is shown in .
The examples in most chapters build on the previous ones, so if you are skipping around, you'll really want to download the sample code.
Example 3-1. CreateTest.java
 1   package com.oreilly.hh;
 2
 3   import net.sf.hibernate.*;
 4   import net.sf.hibernate.cfg.Configuration;
 5
 6   import java.sql.Time;
 7   import java.util.Date;
 8
 9   /**
10    * Create sample data, letting Hibernate persist it for us.
11    */
12   public class CreateTest {
13
14       public static void main(String args[]) throws Exception {
15           // Create a configuration based on the properties file we've put
16           // in the standard place.
17           Configuration config = new Configuration();
18
19           // Tell it about the classes we want mapped, taking advantage of
20           // the way we've named their mapping documents.
21           config.addClass(Track.class);
22
23           // Get the session factory we can use for persistence
24           SessionFactory sessionFactory = config.buildSessionFactory();
25
26           // Ask for a session using the JDBC information we've configured
27           Session session = sessionFactory.openSession();
28           Transaction tx = null;
29           try {
30               // Create some data and persist it
31               tx = session.beginTransaction();
32
33               Track track = new Track("Russian Trance",
34                                       "vol2/album610/track02.mp3",
35                                       Time.valueOf("00:03:30"), new Date(),
36                                       (short)0);
37               session.save(track);
38
39               track = new Track("Video Killed the Radio Star",
40                                 "vol2/album611/track12.mp3",
41                                 Time.valueOf("00:03:49"), new Date(),
42                                 (short)0);
43               session.save(track);
44
45
46              track = new Track("Gravity's Angel",
47                                "vol2/album175/track03.mp3",
48                                Time.valueOf("00:06:06"), new Date(),
49                                (short)0);
50             session.save(track);
51
52             // We're done; make our changes permanent
53             tx.commit();
54
55         } catch (Exception e) {
56             if (tx != null) {
57                 // Something went wrong; discard all partial changes
58                 tx.rollback();
59             }
60             throw e;
61         } finally {
62             // No matter what, close the session
63             session.close();
64         }
65
66         // Clean up after ourselves
67         sessionFactory.close();
68      }
69   }
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Finding Persistent Objects
It's time to throw the giant lever into reverse and look at how you load data from a database into Java objects.
Use Hibernate Query Language to get an object-oriented view of the contents of your mapped database tables. These might have started out as objects persisted in a previous session, or they might be data that came from completely outside your application code.
shows a program that runs a simple query using the test data we just created. The overall structure will look very familiar, because all the Hibernate setup is the same as the previous program.
Example 3-5. QueryTest.java
 1   package com.oreilly.hh;
 2
 3   import net.sf.hibernate.*;
 4   import net.sf.hibernate.cfg.Configuration;
 5
 6   import java.sql.Time;
 7   import java.util.*;
 8
 9   /**
10    * Retrieve data as objects
11    */
12   public class QueryTest {
13
14       /**
15        * Retrieve any tracks that fit in the specified amount of time.
16        *
17        * @param length the maximum playing time for tracks to be returned.
18        * @param session the Hibernate session that can retrieve data.
19        * @return a list of {@link Track}s meeting the length restriction.
20        * @throws HibernateException if there is a problem.
21        */
22       public static List tracksNoLongerThan(Time length, Session session)
23           throws HibernateException
24       {
25           return session.find("from com.oreilly.hh.Track as track " +
26                               "where track.playTime <= ?",
27                               length, Hibernate.TIME);
28       }
29
30       /**
31        * Look up and print some tracks when invoked from the command line.
32        */
33       public static void main(String args[]) throws Exception {
34           // Create a configuration based on the properties file we've put
35           // in the standard place.
36           Configuration config = new Configuration();
37
38           // Tell it about the classes we want mapped, taking advantage of
39           // the way we've named their mapping documents.
40           config.addClass(Track.class);
41
42           // Get the session factory we can use for persistence
43           SessionFactory sessionFactory = config.buildSessionFactory();
44
45           // Ask for a session using the JDBC information we've configured
46           Session session = sessionFactory.openSession();
47           try {
48               // Print the tracks that will fit in five minutes
49               List tracks = tracksNoLongerThan(Time.valueOf("00:05:00"),
50                                                session);
51               for (ListIterator iter = tracks.listIterator() ;
52                    iter.hasNext() ; ) {
53                   Track aTrack = (Track)iter.next();
54                 System.out.println("Track: \"" + aTrack.getTitle() +
55                                    "\", " + aTrack.getPlayTime());
56             }
57         } finally {
58             // No matter what, close the session
59             session.close();
60         }
61
62         // Clean up after ourselves
63         sessionFactory.close();
64      }
65   }
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Better Ways to Build Queries
As mentioned earlier, HQL lets you go beyond the use of JDBC-style query placeholders to get parameters conveniently into your queries. The features discussed in this section can make your programs much easier to read and maintain.
Use named parameters to control queries and move the query text completely outside of your Java source code.
Well, I've already promised that this will make your programs easier to write, read, and update. In fact, if these features weren't available in Hibernate, I would have been less eager to adopt it, because they've been part of my own (even more) lightweight O/R layer for years.
Named parameters make code easier to understand because the purpose of the parameter is clear both within the query itself and within the Java code that is setting it up. This self-documenting nature is valuable in itself, but it also reduces the potential for error by freeing you from counting commas and question marks, and it can modestly improve efficiency by letting you use the same parameter more than once in a single query.
Keeping the queries out of Java source code makes them much easier to read and edit because they aren't giant concatenated series of Java strings spread across multiple lines and interleaved with extraneous quotation marks, backslashes, and other Java punctuation. Typing them the first time is bad enough, but if you've ever had to perform significant surgery on a query embedded in a program in this way, you will have had your fill of moving quotation marks and plus signs around to try to get the lines to break in nice places again.
If you haven't yet had to deal with this, trust me, it's well worth avoiding.
The key to both of these capabilities in Hibernate is the Query interface. We'll start by changing our query to use a named parameter (). (This isn't nearly as big a deal for a query with a single parameter like this one, but it's worth getting into the habit right away. You'll be very thankful when you start working with the light-dimming queries that power your real projects!)
Example 3-8. Revising our query to use a named parameter
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Chapter 4: Collections and Associations
No, this isn't about taxes or politics. Now that we've seen how easy it is to get individual objects into and out of a database, it's time to see how to work with groups and relationships between objects. Happily, it's no more difficult.
In this chapter:
  • Mapping Collections
  • Persisting Collections
  • Retrieving Collections
  • Using Bidirectional Associations
  • Working with Simple Collections
In any real application you'll be managing lists and groups of things. Java provides a healthy and useful set of library classes to help with this: the Collections utilities. Hibernate provides natural ways for mapping database relationships onto Collections, which are usually very convenient. You do need to be aware of a couple semantic mismatches, generally minor. The biggest is the fact that Collections don't provide "bag" semantics, which might frustrate some experienced database designers. This gap isn't Hibernate's fault, and it even makes some effort to work around the issue.
Bags are like sets, except that the same value can appear more than once.
Enough abstraction! The Hibernate reference manual does a good job of discussing the whole bag issue, so let's leave it and look at a working example of mapping a collection where the relational and Java models fit nicely. It might seem natural to build on the Track examples from and group them into albums, but that's not the simplest place to start, because organizing an album involves tracking additional information, like the disc on which the track is found (for multi-disc albums), and other such finicky details. So let's add artist information to our database.
As usual, the examples assume you followed the steps in the previous chapters. If not, download the example source as a starting point.
The information we need to keep track of for artists is, at least initially, pretty simple. We'll start with just the artist's name. And each track can be assigned a set of artists, so we know who to thank or blame for the music, and you can look up all tracks by someone we like. (It really is critical to allow more than one artist to be assigned to a track, yet so few music management programs get this right. The task of adding a separate link to keep track of composers is left as a useful exercise for the reader after understanding this example.)
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Mapping Collections
In any real application you'll be managing lists and groups of things. Java provides a healthy and useful set of library classes to help with this: the Collections utilities. Hibernate provides natural ways for mapping database relationships onto Collections, which are usually very convenient. You do need to be aware of a couple semantic mismatches, generally minor. The biggest is the fact that Collections don't provide "bag" semantics, which might frustrate some experienced database designers. This gap isn't Hibernate's fault, and it even makes some effort to work around the issue.
Bags are like sets, except that the same value can appear more than once.
Enough abstraction! The Hibernate reference manual does a good job of discussing the whole bag issue, so let's leave it and look at a working example of mapping a collection where the relational and Java models fit nicely. It might seem natural to build on the Track examples from and group them into albums, but that's not the simplest place to start, because organizing an album involves tracking additional information, like the disc on which the track is found (for multi-disc albums), and other such finicky details. So let's add artist information to our database.
As usual, the examples assume you followed the steps in the previous chapters. If not, download the example source as a starting point.
The information we need to keep track of for artists is, at least initially, pretty simple. We'll start with just the artist's name. And each track can be assigned a set of artists, so we know who to thank or blame for the music, and you can look up all tracks by someone we like. (It really is critical to allow more than one artist to be assigned to a track, yet so few music management programs get this right. The task of adding a separate link to keep track of composers is left as a useful exercise for the reader after understanding this example.)
For now, our Artist class doesn't need anything other than a name property (and its key, of course). Setting up a mapping document for it will be easy. Create the file Artist.hbm.xml
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Persisting Collections
Our first task is to beef up the CreateTest class to take advantage of the new richness in our schema, creating some artists and associating them with tracks.
To begin with, add some helper methods to CreateTest.java to simplify the task, as shown in (with changes and additions in bold).
Example 4-5. Utility methods to help find and create artists, and to link them to tracks
 1  package com.oreilly.hh;
 2
 3  import net.sf.hibernate.*;
 4
 5  import net.sf.hibernate.cfg.Configuration;
 6
 7  import java.sql.Time;
 8  import java.util.*;
 9
10  /**
11   * Create more sample data, letting Hibernate persist it for us.
12   */
13  public class CreateTest {
14
15      /**
16       * Look up an artist record given a name.
17       * @param name the name of the artist desired.
18       * @param create controls whether a new record should be created if
19       *        the specified artist is not yet in the database.
20       * @param session the Hibernate session that can retrieve data
21       * @return the artist with the specifiedname, or <code>null</code>if no
22       * such artist exists and <code>create</code> is <code>false</code>
23       * @throws HibernateException if there is a problem.
24       */
25      public static Artist getArtist(String name, boolean create,
26                                      Session session)
27          throws HibernateException
28      {
29          Query query = session.getNamedQuery(
30                            "com.oreilly.hh.artistByName");
31          query.setString("name", name);
32          Artist found = (Artist)query.uniqueResult();
33          if (found == null && create) {
34              found = new Artist(name, new HashSet());
35              session.save(found);
36          }
37          return found;
38      }
39
40      /**
41       * Utility method to associate an artist with a track
42       */
43      private static void addTrackArtist(Track track, Artist artist) {
44          track.getArtists().add(artist);
45      }
As is so often the case when working with Hibernate, this code is pretty simple and self explanatory. (Do notice that line 8 has changed—we used to import
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Retrieving Collections
Content preview·

Return to Hibernate: A Developer's Notebook