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.)
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).
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).
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 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.
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <class name="com.oreilly.hh.data.Track" table="TRACK"> <meta attribute="class-description"> 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"> <meta attribute="scope-set">protected</meta> <generator class="native"/> </id> <property name="title" type="string" not-null="true"/> <property name="filePath" type="string" not-null="true"/> <property name="playTime" type="time"> <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>
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.
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.
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
.
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.
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.
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.
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
.
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.)
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 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.
<?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"/>
<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"
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:
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.
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.
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. * @author Jim Elliott (with help from Hibernate) * */ public class Track implements java.io.Serializable { 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; 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) { 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!
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:
The class-level JavaDoc should look familiar, since it comes
right from the class-description
meta
tag in our mapping
document.
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.
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.
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.
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.
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 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.
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.
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.
<!-- Create our runtime subdirectories and copy resources into them --> <target name="prepare" description="Sets up build structures"> <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"/> </fileset> </copy> </target> <!-- Generate the schemas for all mapping files in our class tree --> <target name="schema" depends="prepare" 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" /> </hibernatetool> </target>
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.
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.
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.)
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.)
### 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
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.
% 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.
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.
…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.