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 database tables that will store them. See Appendix E for some pointers about how to explore other approaches, and Chapter 11 if you’d like to use Hibernate with Eclipse.

People who are more used to working with Java objects than abstract schema representations may have a little trouble with this approach until they’re used to it, and sometimes you just don’t want to bother with an external XML file. Chapter 7 shows how to use Java 5 annotations to embed mapping information right inside your data model classes. It’s important to get a handle on XML based mapping, though, so that is what we’re starting with.

In our examples, we’re going to be working with a database that could power an interface to a large personal collection of music, allowing users to search, browse, and listen in a natural way. (You might well have guessed this from the names of the database files that were created at the end of the first chapter.)

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 Chapter 11 shows how easy some of them are to use in Eclipse).

Tip

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 Chapter 7.

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).

Why do I care?

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.

How do I do that?

Note

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 in the src/com/oreilly/hh/data directory you created in Setting Up a Project Hierarchy.” (If you skipped that section, 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 shown in Example 2-1.

Example 2-1. The mapping document for tracks, Track.hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
          "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> 1

<hibernate-mapping>

  <class name="com.oreilly.hh.data.Track" table="TRACK"> 2
    <meta attribute="class-description"> 3
      Represents a single playable track in the music database.
      @author Jim Elliott (with help from Hibernate)
    </meta>

    <id name="id" type="int" column="TRACK_ID"> 4
      <meta attribute="scope-set">protected</meta>
      <generator class="native"/> 5
    </id>

    <property name="title" type="string" not-null="true"/> 6

    <property name="filePath" type="string" not-null="true"/>

    <property name="playTime" type="time"> 7
      <meta attribute="field-description">Playing time</meta>
    </property>

    <property name="added" type="date">
      <meta attribute="field-description">When the track was created</meta>
    </property>

    <property name="volume" type="short" not-null="true">
      <meta attribute="field-description">How loud to play the track</meta>
    </property>

  </class>
</hibernate-mapping>
1

The first three lines are a required preamble to make this a valid XML document and announce that it conforms to the document type definition used by Hibernate for mappings. The actual mappings are inside the hibernate-mapping tag.

2

We’re defining a mapping for a single class, com.oreilly.hh.data.Track, and the name and package of this class are related to the name and location of the file we’ve created. This relationship isn’t necessary; you can define mappings for any number of classes in a single mapping document and name it anything and place in any location you want, as long as you tell Hibernate how to find it. The advantage of following the convention of naming the mapping file after the class it maps, and placing it in the same place on the class path as that class, is that this allows Hibernate to automatically locate the mapping when you want to work with the class. This simplifies the configuration and use of Hibernate for a small number of classes.

Tip

If you’ll be working with a lot of classes, you probably want to use an XML-format configuration file for Hibernate, and reference all of the mapping documents from within it, rather than having to mention them all in example source code as we did in the first book—you’ll see us switch to this approach starting with the next chapter. Also, when using XML-based configuration, it may make more sense to keep your mapping documents next to the Hibernate configuration file, rather than floating alongside the mapped classes.

In the opening of the class element, we have also specified that this class is stored in a database table named TRACK.

3

This meta tag doesn’t directly affect the mapping. Instead, it provides additional information that can be used by different tools. In this case, by specifying an attribute value of class-description, we are telling the Java code-generation tool what JavaDoc text we want associated with the Track class. This is entirely optional, and you’ll see the result of including it in “Generating Some Class” later in this chapter.

4

The remainder of the mapping sets up the pieces of information we want to keep track of, as properties in the class and their associated columns in the database table. Even though we didn’t mention it in the introduction to this example, each track is going to need an id. Following database best practices, we’ll use a meaningless surrogate key (a value with no semantic meaning, serving only to identify a specific database row). In Hibernate, the key/id mapping is set up using an id tag. We’re choosing to use an int to store our id in the column Track_id. It contains another meta tag to communicate with the Java code generator, telling it that the set method for the id property should be protected; there’s no need for application code to go changing track IDs.

5

The generator tag determines how Hibernate creates id values for new instances. (Note that this tag relates to normal Object/Relational mapping operation, not to the Java code generator, which is often not even used; generator is more fundamental than the optional meta tags.) There are a number of different ID generation strategies to choose from, and you can even write your own. In this case, we’re telling Hibernate to use whatever is most natural for the underlying database (we’ll see later on how it learns what database we’re using). In the case of HSQLDB, an identity column is used.

6

After the ID, we just enumerate the various track properties we care about. The title is a string and cannot be null. The filePath has the same characteristics, while the remaining properties other than volume are allowed to be null.

7

playTime is a time, added is a date, and volume is a short. These last three properties use a new kind of meta attribute, field-description, which specifies JavaDoc text for the individual properties, with some limitations in the current code generator.

So this mapping file contains a rigorous and compact specification of the data we want to use to represent a track of music, in a format with which Hibernate can work. Now let’s see what Hibernate can actually do with it. (Hibernate can represent much more complex kinds of information, including relationships between objects, which we’ll cover in upcoming chapters. Appendix E discusses ways you can learn even more than we can fit in the book.)

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.

How do I do that?

The Hibernate Tools you installed in Chapter 1 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.

Example 2-2. 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" 1
           classname="org.hibernate.tool.ant.HibernateToolTask"
           classpathref="project.class.path"/>

  <target name="db" description="Runs HSQLDB database management UI 
against the database file--use when application is not running">
      <java classname="org.hsqldb.util.DatabaseManager"
            fork="yes">
         <classpath refid="project.class.path"/>
         <arg value="-driver"/>
         <arg value="org.hsqldb.jdbcDriver"/>
         <arg value="-url"/>
         <arg value="jdbc:hsqldb:${data.dir}/music"/>
         <arg value="-user"/>
         <arg value="sa"/>
      </java>
  </target>

  <!-- Generate the java code for all mapping files in our source tree -->
  <target name="codegen" 2
          description="Generate Java source from the O/R mapping files">
    <hibernatetool destdir="${source.root}">
      <configuration>
        <fileset dir="${source.root}">
          <include name="**/*.hbm.xml"/>
        </fileset>
      </configuration>
      <hbm2java/>
    </hibernatetool>
  </target>

</project>

We added a new taskdef (task definition), and target to the build file:

1

The task definition teaches Ant a new trick: it tells it how to use the hibernatetool task that is part of the Hibernate Tools, with the help of a class provided for this purpose. Note that it also specifies the class path to be used when invoking this tool, using the project.class.path definition which includes all the dependencies that the Maven Ant Tasks have managed for us. This is how the hibernate tools can be found by Ant when they are needed.

2

The codegen target uses the Hibernate Tools hbm2java mode to run Hibernate’s code generator on any mapping documents found in the src tree, writing the corresponding Java source. The pattern **/*.hbm.xml means “any file ending in .hbm.xml, within the specified directory, or any subdirectory, however deeply nested.” Again, we have to tell the tool how to find the mapping files because we’re not yet using an XML configuration file to more powerfully configure Hibernate; you’ll see this change in the next chapter.

Let’s try it! From within your project directory, type the following command:

            ant codegen
          

You should see output like this (assuming you ran the ant db example in the previous chapter, which will have downloaded all the necessary dependencies; if you haven’t, there will be a bunch more lines at the beginning as they are fetched):

Buildfile: build.xml

codegen:
[hibernatetool] Executing Hibernate Tool with a Standard Configuration
[hibernatetool] 1. task: hbm2java (Generates a set of .java files)
[hibernatetool] log4j:WARN No appenders could be found for logger (org.hibernate
.cfg.Environment).
[hibernatetool] log4j:WARN Please initialize the log4j system properly.

BUILD SUCCESSFUL
Total time: 2 seconds

The warnings are griping about the fact that we configured our build to install log4j in Chapter 1, but haven’t yet taken the trouble to set up the configuration file that it expects. We’ll see how to do that in Cooking Up a Schema.” For now, if you look in the directory src/com/oreilly/hh/data, you’ll see that a new file named Track.java has appeared, with content similar to Example 2-3.

Example 2-3. Code generated from the Track mapping document
package com.oreilly.hh.data;
// Generated Sep 2, 2007 10:27:53 PM by Hibernate Tools 3.2.0.b9

import java.util.Date;

/**
 *       Represents a single playable track in the music database. 1
 *       @author Jim Elliott (with help from Hibernate)
 *     
 */
public class Track  implements java.io.Serializable {

     2
     private int id;
     private String title;
     private String filePath;
     /**
      * Playing time
     */
     private Date playTime;
     /**
      * When the track was created
     */
     private Date added;
     /**
      * How loud to play the track
     */
     private short volume;

    3
    public Track() {
    }

    public Track(String title, String filePath, short volume) {
        this.title = title;
        this.filePath = filePath;
        this.volume = volume;
    }
    public Track(String title, String filePath, Date playTime, Date added, short
 volume) {
       this.title = title;
       this.filePath = filePath;
       this.playTime = playTime;
       this.added = added;
       this.volume = volume;
    }
    
    public int getId() {
        return this.id;
    }
    
    protected void setId(int id) {  4
        this.id = id;
    }
    public String getTitle() {
        return this.title;
    }
    
    public void setTitle(String title) {
        this.title = title;
    }
    public String getFilePath() {
        return this.filePath;
    }
    
    public void setFilePath(String filePath) {
        this.filePath = filePath;
    }
    /**       
     *      * Playing time
     */
    public Date getPlayTime() {
        return this.playTime;
    }
    
    public void setPlayTime(Date playTime) {
        this.playTime = playTime;
    }
    /**       
     *      * When the track was created
     */
    public Date getAdded() {
        return this.added;
    }
    
    public void setAdded(Date added) {
        this.added = added;
    }
    /**       
     *      * How loud to play the track
     */
    public short getVolume() {
        return this.volume;
    }
    
    public void setVolume(short volume) {
        this.volume = volume;
    }

}

How did this work? Ant found all files in our source tree ending in .hbm.xml (just one, so far) and fed them to the Hibernate code generator, which analyzed them and wrote a Java class meeting the specifications we provided for the Track mapping. Clearly this can save a lot of time and repetitive work!

Note

Hibernate can save a lot of time and fairly tedious activity. We were quickly spoiled by it.

You may find it worthwhile to compare the generated Java source with the mapping specification from which it arose (Example 2-1). The source starts out with the proper package declaration, which is easy for hbm2java to figure out from the fully qualified class name required in the mapping file:

1

The class-level JavaDoc should look familiar, since it comes right from the class-description meta tag in our mapping document.

2

The field declarations are derived from the id and property tags defined in the mapping. The Java types used are derived from the property types in the mapping document. To learn the full set of value types supported by Hibernate, look to the resources mentioned in Appendix E. For now, the relationship between the types in the mapping document and the Java types used in the generated code should be fairly clear.

3

After the field declarations comes a trio of constructors. The first allows instantiation without any arguments (this is required if you want the class to be usable as a bean, such as on a Java Server Page, a very common use for data classes like this); the second fills in just the values we’ve indicated must not be null; and the last establishes values for all properties. Notice that none of the constructors set the value of id; this is the responsibility of Hibernate when we get the object out of the database, or insert it for the first time.

4

Consistent with that, the setId() method is protected, as requested in our id mapping. The rest of the getters and setters are not surprising; this is all pretty much boilerplate code (which we’ve all written too many times), which is why it’s so nice to be able to have the Hibernate tools generate it for us.

Warning

If you want to use Hibernate’s generated code as a starting point and then add some business logic or other features to the generated class, be aware that all your changes will be silently discarded the next time you run the code generator. In such a project, you will want to be sure the hand-tweaked classes are not regenerated by any Ant build target. One technique people use is to have their hand-tooled classes extend the Hibernate-generated ones. This fact is one of the reasons we have segregated our mapped classes into their own data package and subdirectory.

Even though we’re having Hibernate generate our data classes in this example, it’s important to point out that the getters and setters it creates are more than a nice touch. You need to put these in your persistent classes for any properties you want to persist, since Hibernate’s fundamental persistence architecture is based on reflective access to JavaBeans–style properties. They don’t need to be public if you don’t want them to be—Hibernate has ways of getting at even properties declared protected or private—but they do need accessor methods. Think of it as enforcing good object design; the Hibernate team wants to keep the implementation details of actual instance variables cleanly separated from the persistence mechanism.

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.

How do I do that?

The first step is something we alluded to in Chapter 1. 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 org.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 in the class path. Create this file at the top level of your src directory, with the content shown in Example 2-4.

Example 2-4. 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 music. The username and empty password (indeed, all these values) should be familiar from the experiment we ran at the end of Chapter 1. Finally, we tell Hibernate that it needs to shut down the database connection explicitly when it is done working with it; this is an artifact of working with HSQLDB in embedded mode. Without this shutdown, changes are not necessarily flushed when the tools exit, so your schema stays frustratingly empty.

Tip

As noted earlier, you can use an XML format for the configuration information as well, but for the simple needs we have here, it doesn’t buy us anything. We’ll show you the XML configuration approach in Chapter 3.

You can put the properties file in other places and give it other names, or use entirely different ways of getting the properties into Hibernate, but this is the default place Hibernate will look, so it’s the path of least resistance (or, I guess, of least runtime configuration).

We also need to add some new pieces to our build file, as we’ll show. Add the targets in Example 2-5 right before the closing </project> tag at the end of build.xml.

Example 2-5. Ant build file additions for schema generation
  <!-- Create our runtime subdirectories and copy resources into them -->
  <target name="prepare" description="Sets up build structures"> 1
    <mkdir dir="${class.root}"/>

    <!-- Copy our property files and O/R mappings for use at runtime -->
    <copy todir="${class.root}" >
      <fileset dir="${source.root}" >
        <include name="**/*.properties"/>
        <include name="**/*.xml"/> 2
      </fileset>
    </copy>
  </target>

  <!-- Generate the schemas for all mapping files in our class tree -->
  <target name="schema" depends="prepare" 3
          description="Generate DB schema from the O/R mapping files">

    <hibernatetool destdir="${source.root}">
      <configuration>
        <fileset dir="${class.root}">
          <include name="**/*.hbm.xml"/>
        </fileset>
      </configuration>
    <hbm2ddl drop="yes" /> 4
    </hibernatetool>
  </target>
1

First we add a prepare target that is intended to be used by other targets more than from the command line. Its purpose is to create, if necessary, the classes directory into which we’re going to compile Java code, and then copy any properties and mapping files found in the src directory hierarchy to corresponding directories in the classes hierarchy. This hierarchical copy operation (using the special **/* pattern) is a nice feature of Ant, enabling us to define and edit resources alongside the source files that use them, while making those resources available at runtime via the class loader.

2

This copies all XML files it finds, not just mapping documents. Although it’s more than we need right now, it will become important when we switch to XML-based configuration for Hibernate.

3

The schema target depends on prepare to copy the mapping documents to the right place for runtime use. It invokes the Hibernate tools in hbm2ddl mode, telling them to generate the database schema associated with any mapping documents found in the classes tree. (As noted previously, this will get simpler when we change to using a more-powerful XML configuration file for Hibernate in the next chapter.)

4

There are a number of parameters you can give the schema-generation tool to configure the way it works. In this example, we’re telling it to get rid of any previous definition of the tables that might exist before generating a new one based on the mapping document (drop=yes). For more details about this and other configuration options, consult the Hibernate Tools reference manual. Hibernate can even look at an existing database and try to figure out how the schema needs to change to reflect a new mapping file.

With these additions, we’re ready to generate the schema for our TRACK table. Hibernate is going to do a lot of fancy things for us to accomplish this, and it might be neat to see what they all are. It is fairly easy to watch; all we need to do is set up logging for the right messages. To achieve that, we need to configure Log4j, the logging environment used by Hibernate. The easiest way to do this is to make a log4j.properties file available at the root of the class path. We can take advantage of our existing prepare target to copy the properties file from the src to the classes directory at the same time Ant copies Hibernate’s properties.

Create a file named log4j.properties in the src directory with the content shown in Example 2-6. (An easy way to do this is to copy the file out of the doc/tutorial/src directory in the Hibernate distribution you downloaded, since it’s provided for use by the examples included in the distribution. If you’re typing it in yourself, you can skip the blocks which are commented out; they are provided to suggest useful logging alternatives.)

Example 2-6. The logging configuration file, log4j.properties
### direct log messages to stdout ###
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n

### direct messages to file hibernate.log ###
#log4j.appender.file=org.apache.log4j.FileAppender
#log4j.appender.file.File=hibernate.log
#log4j.appender.file.layout=org.apache.log4j.PatternLayout
#log4j.appender.file.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n

### set log levels - for more verbose logging change 'info' to 'debug' ###

log4j.rootLogger=warn, stdout

log4j.logger.org.hibernate=info
#log4j.logger.org.hibernate=debug

### log HQL query parser activity
#log4j.logger.org.hibernate.hql.ast.AST=debug

### log just the SQL
#log4j.logger.org.hibernate.SQL=debug

### log JDBC bind parameters ###
log4j.logger.org.hibernate.type=info
#log4j.logger.org.hibernate.type=debug

### log schema export/update ###
log4j.logger.org.hibernate.tool.hbm2ddl=debug

### log HQL parse trees
#log4j.logger.org.hibernate.hql=debug

### log cache activity ###
#log4j.logger.org.hibernate.cache=debug

### log transaction activity
#log4j.logger.org.hibernate.transaction=debug

### log JDBC resource acquisition
#log4j.logger.org.hibernate.jdbc=debug

### enable the following line if you want to track down connection ###
### leakages when using DriverManagerConnectionProvider ###
#log4j.logger.org.hibernate.connection.DriverManagerConnectionProvider=trace

Tip

With the log configuration in place, you might want to edit the codegen target in build.xml so that it, too, depends on our new prepare target. This will ensure logging is configured and the hibernate configuration is available whenever codegen is invoked, eliminating the gripe we saw when first using it.

Time to make a schema! From the project directory, execute the command ant prepare and then follow it up with ant schema. You’ll see output similar to Example 2-7 as the classes directory is created and populated with resources; then the schema generator is run.

Example 2-7. Building the schema using HSQLDB’s embedded database server
% ant prepare
Buildfile: build.xml

prepare:
    [mkdir] Created dir: /Users/jim/svn/oreilly/hib_dev_2e/current/examples/ch02
/classes
     [copy] Copying 3 files to /Users/jim/svn/oreilly/hib_dev_2e/current/example
s/ch02/classes

BUILD SUCCESSFUL
Total time: 0 seconds



% ant schema
Buildfile: build.xml

prepare:

schema:
[hibernatetool] Executing Hibernate Tool with a Standard Configuration
[hibernatetool] 1. task: hbm2ddl (Generates database schema)
[hibernatetool] 22:38:21,858  INFO Environment:514 - Hibernate 3.2.5
[hibernatetool] 22:38:21,879  INFO Environment:532 - loaded properties from reso
urce hibernate.properties: {hibernate.connection.username=sa, hibernate.connecti
on.password=****, hibernate.dialect=org.hibernate.dialect.HSQLDialect, hibernate
.connection.shutdown=true, hibernate.connection.url=jdbc:hsqldb:data/music, hibe
rnate.bytecode.use_reflection_optimizer=false, hibernate.connection.driver_class
=org.hsqldb.jdbcDriver}
[hibernatetool] 22:38:21,897  INFO Environment:681 - Bytecode provider name : cg
lib
[hibernatetool] 22:38:21,930  INFO Environment:598 - using JDK 1.4 java.sql.Time
stamp handling
[hibernatetool] 22:38:22,108  INFO Configuration:299 - Reading mappings from fil
e: /Users/jim/Documents/Work/OReilly/svn_hibernate/current/examples/ch02/classes
/com/oreilly/hh/data/Track.hbm.xml
[hibernatetool] 22:38:22,669  INFO HbmBinder:300 - Mapping class: com.oreilly.hh
.data.Track -> TRACK
[hibernatetool] 22:38:22,827  INFO Dialect:152 - Using dialect: org.hibernate.di
alect.HSQLDialect
[hibernatetool] 22:38:23,186  INFO SchemaExport:154 - Running hbm2ddl schema exp
ort
[hibernatetool] 22:38:23,194 DEBUG SchemaExport:170 - import file not found: /im
port.sql
[hibernatetool] 22:38:23,197  INFO SchemaExport:179 - exporting generated schema
 to database
[hibernatetool] 22:38:23,232  INFO DriverManagerConnectionProvider:41 - Using Hi
bernate built-in connection pool (not for production use!)
[hibernatetool] 22:38:23,234  INFO DriverManagerConnectionProvider:42 - Hibernat
e connection pool size: 20
[hibernatetool] 22:38:23,241  INFO DriverManagerConnectionProvider:45 - autocomm
it mode: false
[hibernatetool] 22:38:23,255  INFO DriverManagerConnectionProvider:80 - using dr
iver: org.hsqldb.jdbcDriver at URL: jdbc:hsqldb:data/music
[hibernatetool] 22:38:23,258  INFO DriverManagerConnectionProvider:86 - connecti
on properties: {user=sa, password=****, shutdown=true}
[hibernatetool] drop table TRACK if exists;
[hibernatetool] 22:38:23,945 DEBUG SchemaExport:303 - drop table TRACK if exists
;
[hibernatetool] create table TRACK (TRACK_ID integer generated by default as ide
ntity (start with 1), title varchar(255) not null, filePath varchar(255) not nul
l, playTime time, added date, volume smallint not null, primary key (TRACK_ID));
[hibernatetool] 22:38:23,951 DEBUG SchemaExport:303 - create table TRACK (TRACK_
ID integer generated by default as identity (start with 1), title varchar(255) n
ot null, filePath varchar(255) not null, playTime time, added date, volume small
int not null, primary key (TRACK_ID));
[hibernatetool] 22:38:23,981  INFO SchemaExport:196 - schema export complete
[hibernatetool] 22:38:23,988  INFO DriverManagerConnectionProvider:147 - cleanin
g up connection pool: jdbc:hsqldb:data/music

BUILD SUCCESSFUL
Total time: 2 seconds

Toward the end of the schema export section, you can see the actual SQL used by Hibernate to create the TRACK table. If you look at the music.script file in the data directory, you’ll see it’s been incorporated into the database. For a slightly more friendly (and perhaps convincing) way to see it, execute ant db to fire up the HSQLDB graphical interface, as shown in Figure 2-1.

Alert readers are probably wondering why Ant was invoked twice, to first run prepare and then run schema, when the schema target already depends on prepare. This is one of those bootstrapping issues that comes up only the first time you’re creating an environment. The problem is that Ant decides what goes into the project class path during startup when it processes that property definition. Until prepare is run at least once, the classes directory doesn’t exist and doesn’t contain hibernate.properties, so it’s not included in the class path. Once prepare is run, this file is in the right place to be in the class path from now on, but Ant won’t re-evaluate the property definition until the next time it is run. So if you try running schema in one step the very first time, Hibernate will fail, complaining that you didn’t specify a database dialect, because it can’t find its configuration properties. This kind of situation can lead to a lot of frustrated head scratching. However, from now on, it’s safe to run schema and rely on it invoking prepare to copy in new versions of the property file as they are needed, because they will be on the class path from the beginning.

Database interface with our new TRACK table expanded, and a query
Figure 2-1. Database interface with our new TRACK table expanded, and a query

What just happened?

We were able to use Hibernate to create a data table in which we can persist instances of the Java class it created for us. We didn’t have to type a single line of SQL or Java! Of course, the table is still empty at this point. Let’s change that! Chapter 3 will look at the stuff you probably want to see the most: using Hibernate from within a Java program to turn objects into database entries and vice versa.

Before diving into that cool task, it’s worth taking a moment to reflect on how much we’ve been able to accomplish with a couple of XML and properties files. Hopefully you’re starting to see the power and convenience that make Hibernate so exciting.

What about…

…other approaches to ID generation? Keys that are globally unique across a database or the world? Hibernate can support a variety of methods for picking the keys for objects it stores in the database. This is controlled using the generator tag, bullet 5 in Example 2-1. In this example we told Hibernate to use the most natural kind of keys for the type of database that it happens to be using. Other alternatives include the popular hi/lo algorithm, global UUIDs, leaving it entirely up to your Java code, and more. See the “generator” section in the Basic O/R Mapping chapter of the Hibernate reference documentation for details. And, as usual, if none of the built-in choices are perfect for your needs, you can supply your own class to do it exactly how you’d like, implementing the interface org.hibernate.id.IdentifierGenerator and supplying your class name in the generator tag.

If you want to see an example of connecting Hibernate to a database you may be more familiar with, Chapter 10 shows how to work with MySQL.

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

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