BUY THIS BOOK
Add to Cart

Print Book $39.99


Add to Cart

PDF $31.99

Safari Books Online

What is this?

Add to UK Cart

Print Book £24.99

What is this?

Looking to Reprint or License this content?


Harnessing Hibernate
Harnessing Hibernate

By James Elliott, Timothy M. O'Brien, Ryan Fowler
Book Price: $39.99 USD
£24.99 GBP
PDF Price: $31.99

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 at the turn of the millennium, 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, once I discovered Hibernate, we used that for new development instead of my own familiar system (toward which I’ll cheerfully admit bias). That should tell you how compelling it is!
If you’re looking at this book, you’re likely interested in a powerful and convenient way to bridge the worlds of Java objects and relational databases. Hibernate fills that role very nicely, without being so 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.
Later chapters will look at using Hibernate as part of more complex environments such as Spring and Stripes, as well as using it with other databases. The goal of this first chapter is to show you how easy it is to put together a basic, self-contained environment in which you can explore Hibernate and do real things with it.
Although it might be surprising, the first few things you need to get Hibernate running have nothing to do with Hibernate itself. First, you must set up an environment in which the remaining examples work. This will have the pleasant side effect of building a solid foundation for any actual project you might be undertaking.
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 run them 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.
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
Although it might be surprising, the first few things you need to get Hibernate running have nothing to do with Hibernate itself. First, you must set up an environment in which the remaining examples work. This will have the pleasant side effect of building a solid foundation for any actual project you might be undertaking.
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 run them 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.
The examples use Apache Ant for several reasons. It’s convenient and powerful; it’s one of the standard build tools for Java-based development; it’s free; and it’s cross-platform. If you use Ant, the examples will work equally well anywhere there’s a Java environment, which means readers of this book won’t be frustrated or annoyed. Fortunately, it also means we can do many more cool things with less effort—especially since several Hibernate tools have explicit Ant support, which I’ll show you how to leverage. (I should note that these days more complex Java projects often use Maven, which adds many other project management capabilities. Since I had to pick one, in the spirit of keeping things simple and true to what I find useful, I went with Ant for these examples.)
If you are currently using Maven as a build tool, you will notice that we are using Maven’s Ant Tasks to manage dependencies from our Ant builds. Although Maven is gaining momentum, Ant continues to be the most widely used build tool in Java development. Every chapter’s example code folder also has a Maven pom.xml file and can be compiled with Maven. In many cases, the Maven build file provides the same functionality as the Ant
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Check Your Java Version
Of course, I’m also assuming you’ve got a Java software development kit (SDK). These days you should be using Java 5 at the very least, since it gives you some useful new language features. Go with the most recent stable SDK if you can. Either Java 5 or Java 6 should work with all of the examples in this book. It was possible to use most of Hibernate 2 with Java 1.3, but you generally had to rebuild the Hibernate JAR file using your 1.3 compiler. We can only imagine more recent releases depend even more strongly on current Java versions, and Java 5 has been out long enough, and has enough benefits of its own, that we’re not even going to investigate compatibility with prior JDKs. Our examples are written assuming you’ve got at least Java 5—and will need some serious tweaking if you don’t:
java -version
java version "1.6.0_02"
Java(TM) SE Runtime Environment (build 1.6.0_02-b06)
Java HotSpot(TM) Client VM (build 1.6.0_02-b06, mixed mode, sharing)
You should also be using a real licensed Java distribution (such as those from Sun or Apple); our technical reviewers found that the GNU “workalike” implementation did not run these tools and examples properly at the time of this writing. This GNU environment is the default Java shipped with many Linux distributions. If you’re working with a Linux distribution, you may need to download Sun’s JDK yourself and make sure it’s the version you’re invoking (by running java -version). Now that Sun has open sourced Java, this will hopefully improve in the future, and we’ll get the Sun JRE and JDK by default even in purist distributions. Until that day, you may need to do some of your own downloading.
At the time of this writing, Debian-based distributions allow you to install a Sun JDK using their package management utilities (both 5 and 6 are available in Ubuntu’s “Feisty Fawn” and “Gutsy Gibbon” releases). Red Hat–derived distributions still need to download Java directly from Sun Microsystems. Your mileage may vary.
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 Maven Tasks for Ant
Wait—didn’t I just finish telling you that we’re using Ant for the example projects in this book? I did. But that wasn’t the whole story. Although Ant remains the foundation for the examples in this book, we’ve decided in this second version also to leverage Maven’s excellent dependency management features via the Maven Tasks for Ant. The initial version of this book spent precious pages providing instructions for downloading and arranging a whole host of third-party libraries: everything from Jakarta Commons Lang to CGLIB. (And from your perspective, it meant you had to spend many precious minutes carefully and tediously following those instructions.) In this version, we’re declaring our project’s dependencies in our build.xml file and letting Maven take care of downloading and managing our dependencies. This saves a ton of steps and time. Now it’s time to install the Maven Tasks for Ant.
There are two ways to integrate the Maven Tasks for Ant: the first is to drop the required JAR in Ant’s lib directory, and the second is to include the antlib via a <typedef> declaration in an Ant build file. We’re going to use the former method, and drop the maven-ant-tasks-2.0.8.jar into Ant’s lib directory because it requires the least amount of work in our example’s build.xml file, and makes it easier to share between multiple projects. We’ll get to creating build.xml later. First, let’s download the necessary JAR file from the Maven web site. On the front page you should see a link to download (see ).
Figure : Download link for Maven Tasks for Ant on Maven site
At the time of this writing, the Maven Tasks for Ant are at version 2.0.8. Clicking on the link and choosing a mirror will download a JAR file named maven-ant-tasks-2.0.8.jar. Save this file to a local directory.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Installing the Maven Tasks for Ant
Next, copy the maven-ant-tasks-2.0.8.jar file that you just downloaded to your ANT_HOME/lib directory. If you are following this chapter from start to finish, you just downloaded and installed Ant. You also should have set the environment variable ANT_HOME, and you should be familiar with where you just installed Ant. Once you’ve copied the maven-ant-tasks-2.0.8.jar to your ANT_HOME/lib directory, any build.xml file can include the appropriate namespace to use the Maven Tasks for Ant.
If you are running these examples on a multiuser development machine, and you don’t have administrative rights to put a JAR file into the ANT_HOME/lib directory, don’t worry. You can put the maven-ant-tasks-2.0.8.jar file in the ~/.ant/lib directory. Ant will also automatically look for any JAR files in this directory.
Once you’ve copied the maven-ant-tasks-2.0.8.jar to your ANT_HOME/lib directory, you should be able to run the following command to see if maven-ant-tasks-2.0.8.jar is included in the class path on Unix:
ant -diagnostics | grep maven | grep bytes
maven-ant-tasks-2.0.8.jar (960232 bytes)
In Windows, run ant -diagnostics and inspect the output for the presence of maven-ant-tasks-2.0.8.jar in the list of libraries on the class path.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Using 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 easy, in fact, that we can let Maven take care of it for us in this new version of the book—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 stumble across http://hsql.sourceforge.net/ and it seems like the project has been shut down. That’s the wrong address; it’s talking about the predecessor to the current HSQLDB project. shows the correct home page of the current, quite lively project.
Examples based on a database that everyone can download and easily experiment with mean you won’t have to translate any of our 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 is new to you, chances are good you’ll be impressed and intrigued, and you may well end up using it in your own projects. As it says on the project home page:
HSQLDB is the leading SQL relational database engine written in Java. It has a JDBC driver and supports a rich subset of ANSI-92 SQL (BNF tree format) plus SQL 99 and 2003 enhancements. It offers a small (less than 100 k in one version for applets), fast database engine which offers both in-memory and disk-based tables and supports embedded and server modes. 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.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Using Hibernate Core
This section doesn’t need much motivation! You picked up this book because you wanted to learn how to use Hibernate. The part that provides core object/relational mapping services for your applications is called, perhaps not too surprisingly, Hibernate Core. When you build the examples in this book, Hibernate and all of its dependencies are downloaded automatically. Even though this new version of the book’s examples take care of getting Hibernate through the Maven Ant Tasks, you might want to download the latest Hibernate distribution yourself to explore the source, or just view the online documentation, forum, and other support materials. If you’re ready to dive in to trying things out instead, you can skip to the next section, .”
Start at the Hibernate home page at http://hibernate.org/ and explore from there. To get a complete copy of the distribution, find the link, which is on the left side as shown in .
Figure : Download link on the Hibernate home page
The section will tell you which version of Hibernate Core is recommended for downloading; follow that advice. (If you want to be brave, you can try a Development release, but the safest bet is to stick with the latest Production version.) Once you’ve made your choice, click the link in the corresponding row (see ).
Figure : Hibernate binary releases
This takes you to a SourceForge downloads page containing your chosen release, in a choice of archive formats. Pick your favorite and download it. The filename will look something like hibernate-3.x.y.tar.gz or hibernate-3.x.y.zip. (At the time of this writing, the filenames start with hibernate-3.2.5.ga, since the first generally available release of Hibernate 3.2.5 is the current production release.)
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 in this walk-through, once we begin 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, we want to make certain we are very organized from the beginning. Between the tools we’ve downloaded and their supporting libraries, there are already a significant number of files to organize. Luckily for us, the Maven Ant Tasks download and manage all of our external dependencies.
If you end up building something cool by extending 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 we give you throughout the examples will make sense and actually work; many examples 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. We strongly recommend that you download the examples and use them as a reference while you read this book.
Here’s how to set up an empty project hierarchy if you’re not downloading the “finished” examples:
  1. Pick a location on your hard drive where you want to work through these examples, 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 and data. The hierarchy of Java source and related resources will be in the
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 the big picture, lest we remain lost in the reeds of installation and configuration. Object-oriented languages such as 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 myriad ways and 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 away.
For information we need to keep around between runs or share between different programs and systems, relational databases have proven difficult 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” label means Hibernate 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 with which you need to interact, 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.
In this book, we’re going to show how you can start a brand-new project, with no existing classes or data, and have Hibernate help you build both. When starting from scratch like this, the most convenient place to begin is in the middle, with an abstract definition of the mapping we’re going to make between program objects and the tables that will store them. See for some pointers about how to explore other approaches, and if you’d like to use Hibernate with Eclipse.
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 traditionally 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 Computer Aided Software Engineering (CASE) tools (like Together, Rose, or Poseidon) to build UML diagrams representing your data model and feed these into AndroMDA, turning them into Hibernate mappings. The Hibernate Tools package mentioned earlier also gives you several of these capabilities (and shows how easy some of them are to use in Eclipse).
Bear in mind that Hibernate and its extensions let you work in other ways, starting with classes or data if you’ve got them. We’ll look at one of the newer approaches, Hibernate Annotations, which lets you do away with the XML mapping document entirely, in .
Here, we’ll write an XML document by hand, showing it’s quite practical. Let’s start by creating a mapping for tracks, pieces of music that can be listened to individually, or as part of an album or playlist. 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.
You may be thinking there’s a lot of dense information in this file. That’s true, and as you’ll see, it can be used to create a bunch of useful project resources.
Fire up your favorite text editor, and create the file Track.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!
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 Tools 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 . The Ant build file updated for code generation
<?xml version="1.0"?>
<project name="Harnessing Hibernate 3 (Developer's Notebook Second Edition)"
         default="db" basedir="."
         xmlns:artifact="antlib:org.apache.maven.artifact.ant">

  <!-- Set up properties containing important project directories -->
  <property name="source.root" value="src"/>
  <property name="class.root" value="classes"/>
  <property name="data.dir" value="data"/>

  <artifact:dependencies pathId="dependency.class.path">
    <dependency groupId="hsqldb" artifactId="hsqldb" version="1.8.0.7"/>
    <dependency groupId="org.hibernate" artifactId="hibernate"
                version="3.2.5.ga">
      <exclusion groupId="javax.transaction" artifactId="jta"/>
    </dependency>
    <dependency groupId="org.hibernate" artifactId="hibernate-tools"
                version="3.2.0.beta9a"/>
    <dependency groupId="org.apache.geronimo.specs"
                artifactId="geronimo-jta_1.1_spec" version="1.1"/>
    <dependency groupId="log4j" artifactId="log4j" version="1.2.14"/>
  </artifact:dependencies>

  <!-- Set up the class path for compilation and execution -->
  <path id="project.class.path">
      <!-- Include our own classes, of course -->
      <pathelement location="${class.root}" />
      <!-- Add the dependencies classpath -->
      <path refid="dependency.class.path"/>
  </path>

  <!-- Teach Ant how to use the Hibernate Tools -->
  <taskdef name="hibernatetool" 
           classname="org.hibernate.tool.ant.HibernateToolTask"
           classpathref="project.class.path"/>
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 from the mapping 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 which 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 . 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 in the class path. Create this file at the top level of your src directory, with the content shown in .
Example . Setting up hibernate.properties
hibernate.dialect=org.hibernate.dialect.HSQLDialect
hibernate.connection.driver_class=org.hsqldb.jdbcDriver
hibernate.connection.url=jdbc:hsqldb:data/music
hibernate.connection.username=sa
hibernate.connection.password=
hibernate.connection.shutdown=true
In addition to establishing the SQL dialect we are using, the properties file 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
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.
Before we can continue working with Hibernate, we need to get some busy work out of the way. In the previous chapter, we configured Hibernate’s JDBC connection using a hibernate.properties file in the src directory. In this chapter we introduce a way to configure the JDBC connection, SQL dialect, and much more, using a Hibernate XML Configuration file. Just like the hibernate.properties file, we’ll place this file in the src directory. Enter the content shown in into a file called hibernate.cfg.xml within src, and delete the hibernate.properties file.
Example . Configuring Hibernate using XML: hibernate.cfg.xml
<?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>
    <!-- SQL dialect -->
    <property name="dialect">org.hibernate.dialect.HSQLDialect</property> 

    <!-- Database connection settings --> 
    <property name="connection.driver_class">org.hsqldb.jdbcDriver</property> 
    <property name="connection.url">jdbc:hsqldb:data/music</property>
    <property name="connection.username">sa</property>
    <property name="connection.password"></property>
    <property name="connection.shutdown">true</property>
        
    <!-- JDBC connection pool (use the built-in one) -->
    <property name="connection.pool_size">1</property> 
        
    <!-- Enable Hibernate's automatic session context management -->
    <property name="current_session_context_class">thread</property>
        
    <!-- Disable the second-level cache  --> 
    <property
     name="cache.provider_class">org.hibernate.cache.NoCacheProvider</property>

    <!-- disable batching so HSQLDB will propagate errors correctly. -->
    <property name="jdbc.batch_size">0</property> 
        
    <!-- Echo all executed SQL to stdout -->
    <property name="show_sql">true</property> 

    <!-- List all the mapping documents we're using --> 
    <mapping resource="com/oreilly/hh/data/Track.hbm.xml"/>
  </session-factory>
</hibernate-configuration>
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Configuring Hibernate
Before we can continue working with Hibernate, we need to get some busy work out of the way. In the previous chapter, we configured Hibernate’s JDBC connection using a hibernate.properties file in the src directory. In this chapter we introduce a way to configure the JDBC connection, SQL dialect, and much more, using a Hibernate XML Configuration file. Just like the hibernate.properties file, we’ll place this file in the src directory. Enter the content shown in into a file called hibernate.cfg.xml within src, and delete the hibernate.properties file.
Example . Configuring Hibernate using XML: hibernate.cfg.xml
<?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>
    <!-- SQL dialect -->
    <property name="dialect">org.hibernate.dialect.HSQLDialect</property> 

    <!-- Database connection settings --> 
    <property name="connection.driver_class">org.hsqldb.jdbcDriver</property> 
    <property name="connection.url">jdbc:hsqldb:data/music</property>
    <property name="connection.username">sa</property>
    <property name="connection.password"></property>
    <property name="connection.shutdown">true</property>
        
    <!-- JDBC connection pool (use the built-in one) -->
    <property name="connection.pool_size">1</property> 
        
    <!-- Enable Hibernate's automatic session context management -->
    <property name="current_session_context_class">thread</property>
        
    <!-- Disable the second-level cache  --> 
    <property
     name="cache.provider_class">org.hibernate.cache.NoCacheProvider</property>

    <!-- disable batching so HSQLDB will propagate errors correctly. -->
    <property name="jdbc.batch_size">0</property> 
        
    <!-- Echo all executed SQL to stdout -->
    <property name="show_sql">true</property> 

    <!-- List all the mapping documents we're using --> 
    <mapping resource="com/oreilly/hh/data/Track.hbm.xml"/>
  </session-factory>
</hibernate-configuration>
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 new Track instances 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 configuration file, it’s extremely easy to configure the Hibernate session factory and get things rolling.
This discussion assumes you’ve created the schema and generated Java code by following the preceding examples. If you haven’t, you can start by downloading the examples archive from this book’s web site, jumping into the ch03 directory, and using the commands ant prepare and ant codegen followed by ant schema to automatically fetch the Hibernate and HSQLDB libraries and 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 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 with which we started. Type the source of in the directory src/com/oreilly/hh.
Example . Data creation test, CreateTest.java
package com.oreilly.hh;

import org.hibernate.*; 
import org.hibernate.cfg.Configuration;

import com.oreilly.hh.data.*;

import java.sql.Time;
import java.util.Date;

/**
 * Create sample data, letting Hibernate persist it for us.
 */
public class CreateTest {

    public static void main(String args[]) throws Exception {
        // Create a configuration based on the XML file we've put
        // in the standard place.
        Configuration config = new Configuration(); 
        config.configure();

        // Get the session factory we can use for persistence
        SessionFactory sessionFactory = config.buildSessionFactory(); 

        // Ask for a session using the JDBC information we've configured
        Session session = sessionFactory.openSession(); 
        Transaction tx = null;
        try {
            // Create some data and persist it
            tx = session.beginTransaction(); 

            Track track = new Track("Russian Trance",
                                    "vol2/album610/track02.mp3",
                                    Time.valueOf("00:03:30"), new Date(),
                                    (short)0);
            session.save(track);

            track = new Track("Video Killed the Radio Star",
                              "vol2/album611/track12.mp3",
                              Time.valueOf("00:03:49"), new Date(),
                              (short)0);
            session.save(track);

            
             track = new Track("Gravity's Angel",
                              "vol2/album175/track03.mp3",
                               Time.valueOf("00:06:06"), new Date(),
                               (short)0);
            session.save(track);

            // We're done; make our changes permanent
            tx.commit(); 

        } catch (Exception e) {
            if (tx != null) {
                // Something went wrong; discard all partial changes
                tx.rollback();
            }
            throw new Exception("Transaction failed", e);
        } finally {
            // No matter what, close the session
            session.close();
        }

        // Clean up after ourselves
        sessionFactory.close(); 
    }
}
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.
Let’s see how it works, using Hibernate Query Language to get an object-oriented view of the contents of mapped database tables. These might have started out as objects persisted in a previous session or 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 in the previous program.
Example . Data retrieval test, QueryTest.java
package com.oreilly.hh;

import org.hibernate.*;
import org.hibernate.cfg.Configuration;

import com.oreilly.hh.data.*;

import java.sql.Time;
import java.util.*;

/**
 * Retrieve data as objects
 */
public class QueryTest {

    /**
     * Retrieve any tracks that fit in the specified amount of time.
     *
     * @param length the maximum playing time for tracks to be returned.
     * @param session the Hibernate session that can retrieve data.
     * @return a list of {@link Track}s meeting the length restriction.
     */
    public static List tracksNoLongerThan(Time length, Session session) { 
        Query query = session.createQuery("from Track as track " +              
                                          "where track.playTime <= ?");         
        query.setParameter(0, length, Hibernate.TIME);                          
        return query.list();                                                    
    }   

    /**
     * Look up and print some tracks when invoked from the command line.
     */
    public static void main(String args[]) throws Exception {
        // Create a configuration based on the properties file we've put
        // in the standard place.
        Configuration config = new Configuration();
        config.configure();

        // Get the session factory we can use for persistence
        SessionFactory sessionFactory = config.buildSessionFactory();

        // Ask for a session using the JDBC information we've configured
        Session session = sessionFactory.openSession();
        try { 
            // Print the tracks that will fit in five minutes
            List tracks = tracksNoLongerThan(Time.valueOf("00:05:00"),
                                             session);
            for (ListIterator iter = tracks.listIterator() ;
                 iter.hasNext() ; ) {
                Track aTrack = (Track)iter.next();
                System.out.println("Track: \"" + aTrack.getTitle() +
                                   "\", " + aTrack.getPlayTime());
            }
        } finally {
            // No matter what, close the session
            session.close();
        }

        // Clean up after ourselves
        sessionFactory.close();
    }
}
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 in this chapter, HQL lets you go beyond the use of JDBC-style query placeholders to get parameters conveniently into your queries. You can use named parameters and named queries to make your programs much easier to read and maintain.
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.
Named queries let you move the queries completely out of the Java code. Keeping queries out of your Java source makes them much easier to read and edit because they aren’t giant concatenated series of Java strings spread across multiple lines and interwoven 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.
The key to both of these capabilities in Hibernate is the Query interface. We already began using this interface in because, starting with Hibernate 3, it is the only nondeprecated way of performing queries. So today it’s even less of an effort than it used to be to use the nice features described in this section.
We’ll start by changing our query to use a named parameter, as shown in . (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!)
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 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 multidisc 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 you did not, download the example source as a starting point.
The information of which we need to keep track 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 an artist you 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 multidisc 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 you did not, download the example source as a starting point.
The information of which we need to keep track 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 an artist you 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!
Persisting Collections
Our first task is to enhance 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 . Utility methods to help find and create artists, and link them to tracks
package com.oreilly.hh;

import org.hibernate.*;
import org.hibernate.cfg.Configuration;

import com.oreilly.hh.data.*;

import java.sql.Time;
import java.util.*; 

/**
 * Create more sample data, letting Hibernate persist it for us.
 */
public class CreateTest {

  /**
   * Look up an artist record given a name.
   * @param name the name of the artist desired.
   * @param create controls whether a new record should be created if
   *        the specified artist is not yet in the database.
   * @param session the Hibernate session that can retrieve data
   * @return the artist with the specified name, or <code>null</code> if no
   *         such artist exists and <code>create</code> is <code>false</code>.
   * @throws HibernateException if there is a problem.
   */
  public static Artist getArtist(String name, boolean create, Session session) 
  {
    Query query = session.getNamedQuery("com.oreilly.hh.artistByName");
    query.setString("name", name);
    Artist found = (Artist)query.uniqueResult(); 
    if (found == null && create) { 
      found = new Artist(name, new HashSet<Track>());
      session.save(found);
    }
    return found;
  }

  /**
   * Utility method to associate an artist with a track
   */
  private static void addTrackArtist(Track track, Artist artist) { 
    track.getArtists().add(artist);
  }
As is so often the case when working with Hibernate, this code is pretty simple and self-explanatory:
You might have noticed that getArtist⁠⁠(⁠ ⁠) uses a named query to retrieve the Artist record. In , we will add that 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!
Retrieving Collections
You might expect that getting the collection information back out of the database is similarly easy. You’d be right! Let’s enhance our QueryTest class so it shows us the artists associated with the tracks it displays. shows the appropriate changes and additions in bold. Little new code is needed.
Example . QueryTest.java enhanced to display artists associated with tracks
package com.oreilly.hh;

import org.hibernate.*;
import org.hibernate.cfg.Configuration;

import com.oreilly.hh.data.*;

import java.sql.Time;
import java.util.*;

/**
 * Retrieve data as objects
 */
public class QueryTest {

    /**
     * Retrieve any tracks that fit in the specified amount of time.
     *
     * @param length the maximum playing time for tracks to be returned.
     * @param session the Hibernate session that can retrieve data.
     * @return a list of {@link Track}s meeting the length restriction.
     */
    public static List tracksNoLongerThan(Time length, Session session) {
        Query query = session.getNamedQuery(
                          "com.oreilly.hh.tracksNoLongerThan");
        query.setTime("length", length);
        return query.list();
    }

    /**
     * Build a parenthetical, comma-separated list of artist names.
     * @param artists the artists whose names are to be displayed.
     * @return the formatted list, or an empty string if the set was empty.
     */
    public static String listArtistNames(Set<Artist> artists) { 
      StringBuilder result = new StringBuilder();
      for (Artist artist : artists) {
            result.append((result.length() == 0) ? "(" : ", ");                 
            result.append(artist.getName());                                    
        }                                                                       
        if (result.length() > 0) {                                              
            result.append(") ");                                                
        }                                                                       
        return result.toString();                                               
    }
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Using Bidirectional Associations
In our creation code, we established links from tracks to artists, simply by adding Java objects to appropriate collections. Hibernate did the work of translating these associations and groupings into the necessary cryptic entries in a join table it created for that purpose. It allowed us with easy, readable code to establish and probe these relationships. But, remember that we made this association bidirectional—the Artist class has a collection of Track associations, too. We didn’t bother to store anything in there.
The great news is that we don’t have to. Because we marked this as an inverse mapping in the Artist mapping document, Hibernate understands that when we add an Artist association to a Track, we’re implicitly adding that Track as an association to the Artist at the same time.
This convenience works only when you make changes to the “primary” mapping, in which case they propagate to the inverse mapping. If you make changes only to the inverse mapping—in our case, the Set of tracks in the Artist object—they will not be persisted. This unfortunately means your code must be sensitive to which mapping is the inverse.
Let’s build a simple interactive graphical application that can help us check whether the artist-to-track links really show up. When you type in an artist’s name, it will show you all the tracks associated with that artist. A lot of the code is very similar to our first query test. Create the file QueryTest2.java and enter the code shown in .
Example . Source for QueryTest2.java
package com.oreilly.hh;

import org.hibernate.*;
import org.hibernate.cfg.Configuration;

import com.oreilly.hh.data.*;

import java.sql.Time;
import java.util.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

/**
 * Provide a user interface to enter artist names and see their tracks.
 */
public class QueryTest2 extends JPanel {

    JList list;  // Will contain tracks associated with current artist
    DefaultListModel model; // Lets us manipulate the list contents

    /**
     * Build the panel containing UI elements
     */
    public QueryTest2() {
        setLayout(new BorderLayout());
        model = new DefaultListModel();
        list = new JList(model);
        add(new JScrollPane(list), BorderLayout.SOUTH);

        final JTextField artistField = new JTextField(28);
        artistField.addKeyListener(new KeyAdapter() { 
                public void keyTyped(KeyEvent e) { 
                    SwingUtilities.invokeLater(new Runnable() { 
                            public void run() {
                                updateTracks(artistField.getText());
                            }
                        });
                }
            });
        add(artistField, BorderLayout.EAST);
        add(new JLabel("Artist: "), BorderLayout.WEST);
    }

    /**
     * Update the list to contain the tracks associated with an artist
     */
    private void updateTracks(String name) { 
        model.removeAllElements();  // Clear out previous tracks
        if (name.length() < 1) return;   // Nothing to do
        try {
            // Ask for a session using the JDBC information we've configured
            Session session = sessionFactory.openSession(); 
            try {
                Artist artist = CreateTest.getArtist(name, false, session);
                if (artist == null) {  // Unknown artist
                    model.addElement("Artist not found");
                    return;
                }
                // List the tracks associated with the artist
                for (Track aTrack : artist.getTracks()) { 
                    model.addElement("Track: \"" + aTrack.getTitle() +
                                     "\", " + aTrack.getPlayTime());
                }
            } finally { 
                // No matter what, close the session
                session.close();
            }
        } catch (Exception e) {
            System.err.println("Problem updating tracks:" + e);
            e.printStackTrace();
        }
    }

    private static SessionFactory sessionFactory;  // Used to talk to Hibernate

    /**
     * Set up Hibernate, then build and display the user interface.
     */
    public static void main(String args[]) throws Exception {
        // Load configuration properties, read mappings for persistent classes
        Configuration config = new Configuration(); 
        config.configure();

        // Get the session factory we can use for persistence
        sessionFactory = config.buildSessionFactory();

        // Set up the UI
        JFrame frame = new JFrame("Artist Track Lookup"); 
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setContentPane(new QueryTest2());
        frame.setSize(400, 180);
        frame.setVisible(true);
    }
}
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Working with Simpler Collections
The collections we’ve been looking at so far have all contained associations to other objects, which is appropriate for a chapter titled “Collections and Associations,” but these aren't the only kind you can use with Hibernate. You can also define mappings for collections of simple values, like strings, numbers, and nonpersistent value classes.
Suppose we want to be able to record some number of comments about each track in the database. We want a new property called to contain the String values of each associated comment. The new mapping in Track.hbm.xml looks a lot like what we did for artists, only a bit simpler:
<set name="comments" table="TRACK_COMMENTS">
   <key column="TRACK_ID"/>
   <element column="COMMENT" type="string"/>
</set>
(You can put this right before the closing <</class>> tag in the mapping file—and you need to if you want the Track constructor to work with the rest of this example.)
Since we’re able to store an arbitrary number of comments for each Track, we’re going to need a new table in which to put them. Each comment will be linked to the proper Track through the track’s property.
Rebuilding the databases with ant schema shows how this gets built in the database:
[hibernatetool] create table TRACK_COMMENTS (TRACK_ID integer not null, COMMENT 
varchar(255));
...
[hibernatetool] 16:16:55,876 DEBUG SchemaExport:303 - alter table TRACK_COMMENTS
 add constraint FK105B26882DCBFAB5 foreign key (TRACK_ID) references TRACK;
Data modeling junkies will recognize this as a “one-to-many” relationship.
After u