hibernate-orm/reference/en/modules/tutorial.xml

1203 lines
53 KiB
XML

<chapter id="tutorial">
<title>Introduction to Hibernate</title>
<sect1 id="tutorial-intro">
<title>Preface</title>
<para>
This chapter is an introductory tutorial for new users of Hibernate. We start
with a simple command line application using an in-memory database and develop
it in easy to understand steps.
</para>
<para>
This tutorial is intended for new users of Hibernate but requires Java and
SQL knowledge. It is based on a tutorial by Michael Gloegl, the third-party
libraries we name are for JDK 1.4 and 5.0. You might need others for JDK 1.3.
</para>
</sect1>
<sect1 id="tutorial-firstapp">
<title>Part 1 - The first Hibernate Application</title>
<para>
First, we'll create a simple console-based Hibernate application. We use an
in-memory database (HSQL DB), so we do not have to install any database server.
</para>
<para>
Let's assume we need a small database application that can store events we want to
attend, and information about the hosts of these events.
</para>
<para>
The first thing we do, is set up our development directory and put all the
Java libraries we need into it. Download the Hibernate distribution from the
Hibernate website. Extract the package and place all required libraries
found in <literal>/lib</literal> into into the <literal>/lib</literal> directory
of your new development working directory. It should look like this:
</para>
<programlisting><![CDATA[.
+lib
antlr.jar
cglib-full.jar
asm.jar
asm-attrs.jars
commons-collections.jar
commons-logging.jar
ehcache.jar
hibernate3.jar
jta.jar
dom4j.jar
log4j.jar ]]></programlisting>
<para>
This is the minimum set of required libraries (note that we also copied
hibernate3.jar, the main archive) for Hibernate. See the <literal>README.txt</literal> file
in the <literal>lib/</literal> directory of the Hibernate distribution for more information
about required and optional third-party libraries. (Actually, Log4j is not
required but preferred by many developers.)
</para>
<para>
Next we create a class that represents the event we want to store in database.
</para>
<sect2 id="tutorial-firstapp-firstclass">
<title>The first class</title>
<para>
Our first persistent class is a simple JavaBean class with some properties:
</para>
<programlisting><![CDATA[import java.util.Date;
public class Event {
private Long id;
private String title;
private Date date;
Event() {}
public Long getId() {
return id;
}
private void setId(Long id) {
this.id = id;
}
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
}]]></programlisting>
<para>
You can see that this class uses standard JavaBean naming conventions for property
getter and setter methods, as well as private visibility for the fields. This is
a recommended design - but not required. Hibernate can also access fields directly,
the benefit of accessor methods is robustness for refactoring.
</para>
<para>
The <literal>id</literal> property holds a unique identifier value for a particular event.
All persistent entity classes (there are less important dependent classes as well) will need
such an identifier property if we want to use the full feature set of Hibernate. In fact,
most applications (esp. web applications) need to distinguish objects by identifier, so you
should consider this a feature rather than a limitation. However, we usually don't manipulate
the identity of an object, hence the setter method should be private. Only Hibernate will assign
identifiers when an object is saved. You can see that Hibernate can access public, private,
and protected accessor methods, as well as (public, private, protected) fields directly. The
choice is up to you and you can match it to fit your application design.
</para>
<para>
The no-argument constructor is a requirement for all persistent classes; Hibernate
has to create objects for you, using Java Reflection. The constructor can be
private, however, package visibility is required for runtime proxy generation and
efficient data retrieval without bytecode instrumentation.
</para>
<para>
Place this Java source file in a directory called <literal>src</literal> in the
development folder. The directory should now look like this:
</para>
<programlisting><![CDATA[.
+lib
<Hibernate and third-party libraries>
+src
Event.java]]></programlisting>
<para>
In the next step, we tell Hibernate about this persistent class.
</para>
</sect2>
<sect2 id="tutorial-firstapp-mapping">
<title>The mapping file</title>
<para>
Hibernate needs to know how to load and store objects of the persistent class.
This is where the Hibernate mapping file comes into play. The mapping file
tells Hibernate what table in the database it has to access, and what columns
in that table it should use.
</para>
<para>
The basic structure of a mapping file looks like this:
</para>
<programlisting><![CDATA[<?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>
[...]
</hibernate-mapping>]]></programlisting>
<para>
Note that the Hibernate DTD is very sophisticated. You can use it for
auto-completion of XML mapping elements and attributes in your editor or
IDE. You also should open up the DTD file in your text editor - it's the
easiest way to get an overview of all elements and attributes and to see
the defaults, as well as some comments. Note that Hibernate will not
load the DTD file from the web, but first look it up from the classpath
of the application. The DTD file is included in <literal>hibernate3.jar</literal>
as well as in the <literal>src/</literal> directory of the Hibernate distribution.
</para>
<para>
We will omit the DTD declaration in future examples to shorten the code. It is
of course not optional.
</para>
<para>
Between the two <literal>hibernate-mapping</literal> tags, include a
<literal>class</literal> element. All persistent entity classes (again, there
might be dependent classes later on, which are not first-class entities) need
such a mapping, to a table in the SQL database:
</para>
<programlisting><![CDATA[<hibernate-mapping>
<class name="Event" table="EVENTS">
</class>
</hibernate-mapping>]]></programlisting>
<para>
So far we told Hibernate how to persist and load object of class <literal>Event</literal>
to the table <literal>EVENTS</literal>, each instance represented by a row in that table.
Now we continue with a mapping of the unique identifier property to the tables primary key.
In addition, as we don't want to care about handling this identifier, we configure Hibernate's
identifier generation strategy for a surrogate primary key column:
</para>
<programlisting><![CDATA[<hibernate-mapping>
<class name="Event" table="EVENTS">
<id name="id" column="EVENT_ID">
<generator class="increment"/>
</id>
</class>
</hibernate-mapping>]]></programlisting>
<para>
The <literal>id</literal> element is the declaration of the identifer property,
<literal>name="id"</literal> declares the name of the Java property -
Hibernate will use the getter and setter methods to access the property.
The column attribute tells Hibernate which column of the
<literal>EVENTS</literal> table we use for this primary key. The nested
<literal>generator</literal> element specifies the identifier generation strategy,
in this case we used <literal>increment</literal>, which is a very simple in-memory
number increment method useful mostly for testing (and tutorials). Hibernate also
supports database generated, globally unique, as well as application assigned
identifiers (or any strategy you have written an extension for).
</para>
<para>
Finally we include declarations for the persistent properties of the class in
the mapping file. By default, no properties of the class are considered
persistent:
</para>
<programlisting><![CDATA[
<hibernate-mapping>
<class name="Event" table="EVENTS">
<id name="id" column="EVENT_ID">
<generator class="increment"/>
</id>
<property name="date" type="timestamp" column="EVENT_DATE"/>
<property name="title"/>
</class>
</hibernate-mapping>]]></programlisting>
<para>
Just as with the <literal>id</literal> element, the <literal>name</literal>
attribute of the <literal>property</literal> element tells Hibernate which getter
and setter methods to use.
</para>
<para>
Why does the <literal>date</literal> property mapping include the
<literal>column</literal> attribute, but the <literal>title</literal>
doesn't? Without the <literal>column</literal> attribute Hibernate
by default uses the property name as the column name. This works fine for
<literal>title</literal>. However, <literal>date</literal> is a reserved
keyword in most database, so we better map it to a different name.
</para>
<para>
The next interesting thing is that the <literal>title</literal> mapping also lacks
a <literal>type</literal> attribute. The types we declare and use in the mapping
files are not, as you might expect, Java data types. They are also not SQL
database types. These types are so called <emphasis>Hibernate mapping types</emphasis>,
converters which can translate from Java to SQL data types and vice versa. Again,
Hibernate will try to determine the correct conversion and mapping type itself if
the <literal>type</literal> attribute is not present in the mapping. In some cases this
automatic detection (using Reflection on the Java class) might not have the default you
expect or need. This is the case with the <literal>date</literal> property. Hibernate can't
know if the property will map to a SQL <literal>date</literal>, <literal>timestamp</literal>
or <literal>time</literal> column. We declare that we want to preserve full date
and time information by mapping the property with a <literal>timestamp</literal>.
</para>
<para>
This mapping file should be saved as <literal>Event.hbm.xml</literal>, right in
the directory next to the <literal>Event</literal> Java class source file.
The naming of mapping files can be arbitrary, however the <literal>hbm.xml</literal>
suffix became convention in the Hibernate developer community. The directory structure
should now look like this:
</para>
<programlisting><![CDATA[.
+lib
<Hibernate and third-party libraries>
+src
Event.java
Event.hbm.xml]]></programlisting>
<para>
We continue with the main configuration of Hibernate.
</para>
</sect2>
<sect2 id="tutorial-firstapp-configuration">
<title>Hibernate configuration</title>
<para>
We now have a persistent class and its mapping file in place. It is time to configure
Hibernate. Before we do this, we will need a database. HSQL DB, a java-based in-memory SQL DBMS,
can be downloaded from the HSQL DB website. Actually, you only need the <literal>hsqldb.jar</literal>
from this download. Place this file in the <literal>lib/</literal> directory of the
development folder.
</para>
<para>
Create a directory called <literal>data</literal> in the root of the development directory -
this is where HSQL DB will store its data files.
</para>
<para>
Hibernate is the layer in your application which connects to this database, so it needs
connection information. The connections are made through a JDBC connection pool, which we
also have to configure. The Hibernate distribution contains several open source JDBC connection
pooling tools, but will use the Hibernate built-in connection pool for this tutorial. Note that
you have to copy the required library into your classpath and use different
connection pooling settings if you want to use a production-quality third party
JDBC pooling software.
</para>
<para>
For Hibernate's configuration, we can use a simple <literal>hibernate.properties</literal> file, a
slightly more sophisticated <literal>hibernate.cfg.xml</literal> file, or even complete
programmatic setup. Most users prefer the XML configuration file:
</para>
<programlisting><![CDATA[<?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>
<!-- Database connection settings -->
<property name="connection.driver_class">org.hsqldb.jdbcDriver</property>
<property name="connection.url">jdbc:hsqldb:data/tutorial</property>
<property name="connection.username">sa</property>
<property name="connection.password"></property>
<!-- JDBC connection pool (use the built-in) -->
<property name="connection.pool_size">1</property>
<!-- SQL dialect -->
<property name="dialect">org.hibernate.dialect.HSQLDialect</property>
<!-- Echo all executed SQL to stdout -->
<property name="show_sql">true</property>
<!-- Drop and re-create the database schema on startup -->
<property name="hbm2ddl.auto">create</property>
<mapping resource="Event.hbm.xml"/>
</session-factory>
</hibernate-configuration>]]></programlisting>
<para>
Note that this XML configuration uses a different DTD. We configure
Hibernate's <literal>SessionFactory</literal> - a global factory responsible
for a particular database. If you have several databases, use several
<literal>&lt;session-factory&gt;</literal> configurations, usually in
several configuration files (for easier startup).
</para>
<para>
The first four <literal>property</literal> elements contain the necessary
configuration for the JDBC connection. The dialect <literal>property</literal>
element specifies the particular SQL variant Hibernate generates.
The <literal>hbm2ddl.auto</literal> option turns on automatic generation of
database schemas - directly into the database. This can of course also be turned
off (by removing the config option) or redirected to a file with the help of
the <literal>SchemaExport</literal> Ant task. Finally, we add the mapping file(s)
for persistent classes.
</para>
<para>
Copy this file into the source directory, so it will end up in the
root of the classpath. Hibernate automatically looks for a file called
<literal>hibernate.cfg.xml</literal> in the root of the classpath, on startup.
</para>
</sect2>
<sect2 id="tutorial-firstapp-ant">
<title>Building with Ant</title>
<para>
We'll now build the tutorial with Ant. You will need to have Ant installed - get
it from the <ulink url="http://ant.apache.org/bindownload.cgi">Ant download page</ulink>.
How to install Ant will not be covered here. Please refer to the
<ulink url="http://ant.apache.org/manual/index.html">Ant manual</ulink>. After you
have installed Ant, we can start to create the buildfile. It will be called
<literal>build.xml</literal> and placed directly in the development directory.
</para>
<note>
<title>Fixing Ant</title>
<para>
Note that the Ant distribution is by default broken (as described in the Ant FAQ) and
has to be fixed by you, for example, if you'd like to use JUnit from inside your
build file. To make the JUnit task work (we won't need it in this tutorial), either copy
junit.jar to <literal>ANT_HOME/lib</literal> or remove the
<literal>ANT_HOME/lib/ant-junit.jar</literal> plugin stub.
</para>
</note>
<para>
A basic build file looks like this:
</para>
<programlisting><![CDATA[<project name="hibernate-tutorial" default="compile">
<property name="sourcedir" value="${basedir}/src"/>
<property name="targetdir" value="${basedir}/bin"/>
<property name="librarydir" value="${basedir}/lib"/>
<path id="libraries">
<fileset dir="${librarydir}">
<include name="*.jar"/>
</fileset>
</path>
<target name="clean">
<delete dir="${targetdir}"/>
<mkdir dir="${targetdir}"/>
</target>
<target name="compile" depends="clean, copy-resources">
<javac srcdir="${sourcedir}"
destdir="${targetdir}"
classpathref="libraries"/>
</target>
<target name="copy-resources">
<copy todir="${targetdir}">
<fileset dir="${sourcedir}">
<exclude name="**/*.java"/>
</fileset>
</copy>
</target>
</project>]]></programlisting>
<para>
This will tell Ant to add all files in the lib directory ending with <literal>.jar</literal>
to the classpath used for compilation. It will also copy all non-Java source files to the
target directory, e.g. configuration and Hibernate mapping files. If you now run Ant, you
should get this output:
</para>
<programlisting><![CDATA[C:\hibernateTutorial\>ant
Buildfile: build.xml
copy-resources:
[copy] Copying 2 files to C:\hibernateTutorial\bin
compile:
[javac] Compiling 1 source file to C:\hibernateTutorial\bin
BUILD SUCCESSFUL
Total time: 1 second ]]></programlisting>
</sect2>
<sect2 id="tutorial-firstapp-helpers">
<title>Startup and helpers</title>
<para>
It's time to load and store some <literal>Event</literal> objects, but first
we have to complete the setup with some infrastructure code. We have to startup
Hibernate. This startup includes building a global <literal>SessionFactory</literal>
object and to store it somewhere for easy access in application code.
A <literal>SessionFactory</literal> can open up new <literal>Session</literal>'s.
A <literal>Session</literal> represents a single-threaded unit of work, the
<literal>SessionFactory</literal> is a thread-safe global object, instantiated once.
</para>
<para>
We'll create a <literal>HibernateUtil</literal> helper class which takes care
of startup and makes <literal>Session</literal> handling convenient. The so called
<emphasis>ThreadLocal Session</emphasis> pattern is useful here, we keep the current
unit of work associated with the current thread. Let's have a look at the implementation:
</para>
<programlisting><![CDATA[import org.hibernate.*;
import org.hibernate.cfg.*;
public class HibernateUtil {
public static final SessionFactory sessionFactory;
static {
try {
// Create the SessionFactory from hibernate.cfg.xml
sessionFactory = new Configuration().configure().buildSessionFactory();
} catch (Throwable ex) {
// Make sure you log the exception, as it might be swallowed
System.err.println("Initial SessionFactory creation failed." + ex);
throw new ExceptionInInitializerError(ex);
}
}
public static final ThreadLocal session = new ThreadLocal();
public static Session currentSession() throws HibernateException {
Session s = (Session) session.get();
// Open a new Session, if this thread has none yet
if (s == null) {
s = sessionFactory.openSession();
// Store it in the ThreadLocal variable
session.set(s);
}
return s;
}
public static void closeSession() throws HibernateException {
Session s = (Session) session.get();
if (s != null)
s.close();
session.set(null);
}
}]]></programlisting>
<para>
This class does not only produce the global <literal>SessionFactory</literal> in
its static initializer (called once by the JVM when the class is loaded), but also
has a <literal>ThreadLocal</literal> variable to hold the
<literal>Session</literal> for the current thread. No matter when you call
<literal>HibernateUtil.currentSession()</literal>, it will always return the same
Hibernate unit of work in the same thread. A call to <literal>HibernateUtil.closeSession()</literal>
ends the unit of work currently associated with the thread.
</para>
<para>
Make sure you understand the Java concept of a thread-local variables before you
use this helper. A more powerful <literal>HibernateUtil</literal> helper can be found
in <literal>CaveatEmptor</literal> on http://caveatemptor.hibernate.org/ - as well as
in the book "Hibernate in Action". Note that this class is not necessary if you deploy
Hibernate in a J2EE application server: a <literal>Session</literal> will be
automatically bound to the current JTA transaction and you can look up the
<literal>SessionFactory</literal> through JNDI. If you use JBoss AS, Hibernate can
be deployed as a managed system service and will automatically bind the
<literal>SessionFactory</literal> to a JNDI name.
</para>
<para>
Place <literal>HibernateUtil.java</literal> in the development source directory, next
to <literal>Event.java</literal>:
</para>
<programlisting><![CDATA[.
+lib
<Hibernate and third-party libraries>
+src
Event.java
Event.hbm.xml
HibernateUtil.java
hibernate.cfg.xml
+data
build.xml]]></programlisting>
<para>
This should again compile without problems. We finally need to configure a logging
system - Hibernate uses commons logging and leaves you the choice between Log4j and
JDK 1.4 logging. Most developers prefer Log4j: copy <literal>log4j.properties</literal>
from the Hibernate distribution (it's in the <literal>etc/</literal> directory) to
your <literal>src</literal> directory, next to <literal>hibernate.cfg.xml</literal>.
Have a look at the example configuration and change the settings if you like to have
more verbose output. By default, only Hibernate startup message are shown on stdout.
</para>
<para>
The tutorial infrastructure is complete - and we are ready to do some real work with
Hibernate.
</para>
</sect2>
<sect2 id="tutorial-firstapp-workingpersistence">
<title>Loading and storing objects</title>
<para>
Finally, we can use Hibernate to load and store objects. We write an
<literal>EventManager</literal> class with a <literal>main()</literal> method:
</para>
<programlisting><![CDATA[import org.hibernate.Transaction;
import org.hibernate.Session;
import java.util.Date;
public class EventManager {
public static void main(String[] args) {
EventManager mgr = new EventManager();
if (args[0].equals("store")) {
mgr.createAndStoreEvent("My Event", new Date());
}
HibernateUtil.sessionFactory.close();
}
}]]></programlisting>
<para>
We read some arguments from the command line, and if the first argument is
"store", we create and store a new Event:
</para>
<programlisting><![CDATA[private void createAndStoreEvent(String title, Date theDate) {
Session session = HibernateUtil.currentSession();
Transaction tx = session.beginTransaction();
Event theEvent = new Event();
theEvent.setTitle(title);
theEvent.setDate(theDate);
session.save(theEvent);
tx.commit();
HibernateUtil.closeSession();
}]]></programlisting>
<para>
We create a new <literal>Event</literal> object, and hand it over to Hibernate.
Hibernate now takes care of the SQL and executes <literal>INSERT</literal>s
on the database. Let's have a look at the <literal>Session</literal> and
<literal>Transaction</literal>-handling code before we run this.
</para>
<para>
A <literal>Session</literal> is a single unit of work. You might be surprised that we
have an additional API, <literal>Transaction</literal>. This implies that a unit of work
can be "longer" than a single database transaction - imagine a unit of work that spans
several Http request/response cycles (e.g. a wizard dialog) in a web application.
Separating database transactions from "unit of work from the application
user's point of view" is one of Hibernates basic design concepts. We call a long
unit of work <emphasis>Application Transaction</emphasis>, usually encapsulating
several short database transactions. For now we'll keep things simple and assume a
one-to-one granularity between a <literal>Session</literal> and <literal>Transaction</literal>.
</para>
<para>
What does <literal>Transaction.begin()</literal> and <literal>commit()</literal> do? Where
is the <literal>rollback()</literal> in case something goes wrong? The Hibernate
<literal>Transaction</literal> API is actually optional, but we use it for convenience
and portability. If you'd handle the database transaction yourself (e.g. by calling
<literal>session.connection.commit()</literal>), you'd bind the code to a particular
deployment environment, in this direct unmanaged JDBC. By setting the factory for
<literal>Transaction</literal> in your Hibernate configuration you can deploy your
persistence layer anywhere. Have a look at <xref linkend="transactions"/> for more information
about transaction handling and demarcation. We also skipped any error handling and
rollback in this example.
</para>
<para>
To run this first routine we have to add a callable target to the Ant build file:
</para>
<programlisting><![CDATA[<target name="run" depends="compile">
<java fork="true" classname="EventManager" classpathref="libraries">
<classpath path="${targetdir}"/>
<arg value="${action}"/>
</java>
</target>]]></programlisting>
<para>
The value of the <literal>action</literal> argument is set on the command line when
calling the target:
</para>
<programlisting><![CDATA[C:\hibernateTutorial\>ant run -Daction=store]]></programlisting>
<para>
You should see, after compilation, Hibernate starting up and, depending on your
configuration, lots of log output. At the end you will find the following line:
</para>
<programlisting><![CDATA[[java] Hibernate: insert into EVENTS (EVENT_DATE, title, EVENT_ID) values (?, ?, ?)]]></programlisting>
<para>
This is the <literal>INSERT</literal> executed by Hibernate, the question marks
represent JDBC bind parameters. To see the values bound as arguments, or to reduce
the verbosity of the log, check your <literal>log4j.properties</literal>.
</para>
<para>
Now we'd like to list stored events as well, so we add an option to the main method:
</para>
<programlisting><![CDATA[if (args[0].equals("store")) {
mgr.createAndStoreEvent("My Event", new Date());
}
else if (args[0].equals("list")) {
List events = mgr.listEvents();
for (int i = 0; i < events.size(); i++) {
Event theEvent = (Event) events.get(i);
System.out.println("Event: " + theEvent.getTitle() +
" Time: " + theEvent.getDate());
}
}]]></programlisting>
<para>
We also add a new <literal>listEvents() method</literal>:
</para>
<programlisting><![CDATA[private List listEvents() {
Session session = HibernateUtil.currentSession();
Transaction tx = session.beginTransaction();
List result = session.createQuery("from Event").list();
tx.commit();
session.close();
return result;
}]]></programlisting>
<para>
What we do here is use an HQL (Hibernate Query Language) query to load all existing
<literal>Event</literal> objects from the database. Hibernate will generate the
appropriate SQL, send it to the database and populate <literal>Event</literal> objects
with the data. You can create more complex queries with HQL, of course.
</para>
<para>
If you now call Ant with <literal>-Daction=list</literal>, you should see the events
you have stored so far. You might be surprised that this doesn't work, at least if you
followed this tutorial step by step - the result will always be empty. The reason for
this is the <literal>hbm2ddl.auto</literal> switch in the Hibernate configuration: Hibernate
will re-create the database on every run. Disable it by removing the option, and you will
see results in your list after you called the <literal>store</literal> action a few
times. Automatic schema generation and export is mostly useful in unit testing.
</para>
</sect2>
</sect1>
<sect1 id="tutorial-associations">
<title>Part 2 - Mapping associations</title>
<para>
We mapped a persistent entity class to a table. Let's build on this and add some class associations.
First we'll add people to our application, and store a list of events they participate in.
</para>
<sect2 id="tutorial-associations-mappinguser">
<title>Mapping the Person class</title>
<para>
The first cut of the <literal>Person</literal> class is simple:
</para>
<programlisting><![CDATA[public class Person {
private Long id;
private int age;
private String firstname;
private String lastname;
Person() {}
// Accessor methods for all properties, private setter for 'id'
}]]></programlisting>
<para>
Create a new mapping file called <literal>Person.hbm.xml</literal>:
</para>
<programlisting><![CDATA[<hibernate-mapping>
<class name="Person" table="PERSON">
<id name="id" column="PERSON_ID">
<generator class="increment"/>
</id>
<property name="age"/>
<property name="firstname"/>
<property name="lastname"/>
</class>
</hibernate-mapping>]]></programlisting>
<para>
Finally, add the new mapping to Hibernate's configuration:
</para>
<programlisting><![CDATA[ <mapping resource="Event.hbm.xml"/>
<mapping resource="Person.hbm.xml"/>
]]></programlisting>
<para>
We'll now create an association between these two entities. Obviously, persons
can participate in events, and events have participants. The design questions
we have to deal with are: directionality, multiplicity, and collection
behavior.
</para>
</sect2>
<sect2 id="tutorial-associations-unidirset">
<title>A unidirectional Set-based association</title>
<para>
We'll add a collection of events to the <literal>Person</literal> class. That way we can
easily navigate to the events for a particular person, without executing an explicit query -
by calling <literal>aPerson.getEvents()</literal>. We use a Java collection, a <literal>Set</literal>,
because the collection will not contain duplicate elements and the ordering is not relevant for us.
</para>
<para>
So far we designed a unidirectional, many-valued associations, implemented with a <literal>Set</literal>.
Let's write the code for this in the Java classes and then map it:
</para>
<programlisting><![CDATA[public class Person {
private Set events = new HashSet();
public Set getEvents() {
return events;
}
public void setEvents(Set events) {
this.events = events;
}
}]]></programlisting>
<para>
Before we map this association, think about the other side. Clearly, we could just keep this
unidirectional. Or, we could create another collection on the <literal>Event</literal>, if we
want to be able to navigate it bi-directional, i.e. <literal>anEvent.getParticipants()</literal>.
This is a design choice left to you, but what is clear from this discussion is the multiplicity
of the association: "many" valued on both sides, we call this a <emphasis>many-to-many</emphasis>
association. Hence, we use Hibernate's many-to-many mapping:
</para>
<programlisting><![CDATA[<class name="Person" table="PERSON">
<id name="id" column="PERSON_ID">
<generator class="increment"/>
</id>
<property name="age"/>
<property name="firstname"/>
<property name="lastname"/>
<set name="events" table="PERSON_EVENT">
<key column="PERSON_ID"/>
<many-to-many column="EVENT_ID" class="Event"/>
</set>
</class>]]></programlisting>
<para>
Hibernate supports all kinds of collection mappings, a <literal>&lt;set&gt;</literal> being most
common. For a many-to-many association (or <emphasis>n:m</emphasis> entity relationship), an
association table is needed. Each row in this table represents a link between a person and an event.
The table name is configured with the <literal>table</literal> attribute of the <literal>set</literal>
element. The identifier column name in the association, for the person's side, is defined with the
<literal>&lt;key&gt;</literal> element, the column name for the event's side with the
<literal>column</literal> attribute of the <literal>&lt;many-to-many&gt;</literal>. You also
have to tell Hibernate the class of the objects in your collection (correct: the class on the
other side of the collection of references).
</para>
<para>
The database schema for this mapping is therefore:
</para>
<programlisting><![CDATA[
_____________ __________________
| | | | _____________
| EVENTS | | PERSON_EVENT | | |
|_____________| |__________________| | PERSON |
| | | | |_____________|
| *EVENT_ID | <--> | *EVENT_ID | | |
| EVENT_DATE | | *PERSON_ID | <--> | *PERSON_ID |
| TITLE | |__________________| | AGE |
|_____________| | FIRSTNAME |
| LASTNAME |
|_____________|
]]></programlisting>
</sect2>
<sect2 id="tutorial-associations-working">
<title>Working the association</title>
<para>
Let's bring some people and events together in a new method in <literal>EventManager</literal>:
</para>
<programlisting><![CDATA[private void addPersonToEvent(Long personId, Long eventId) {
Session session = HibernateUtil.currentSession();
Transaction tx = session.beginTransaction();
Person aPerson = (Person) session.load(Person.class, personId);
Event anEvent = (Event) session.load(Event.class, eventId);
aPerson.getEvents().add(anEvent);
tx.commit();
HibernateUtil.closeSession();
}]]></programlisting>
<para>
After loading a <literal>Person</literal> and an <literal>Event</literal>, simply
modify the collection using the normal collection methods. As you can see, there is no explicit call
to <literal>update()</literal> or <literal>save()</literal>, Hibernate automatically
detects the collection has been modified and needs to be saved. This is called <emphasis>automatic
dirty checking</emphasis>, and you can also try it by modifying the name or the date property of
any of your objects. As long as they are in <emphasis>persistent</emphasis> state, that is, bound
to a particular Hibernate <literal>Session</literal> (i.e. they have been just loaded or saved in
a unit of work), Hibernate monitors any changes and executes SQL in a write-behind fashion. The
process of synchronizing the memory state with the database, usually only at the end of a unit of
work, is called <emphasis>flushing</emphasis>.
</para>
<para>
You might of course load person and event in different units of work. Or you modify an object
outside of a <literal>Session</literal>, when it is not in persistent state (if it was persistent
before, we call this state <emphasis>detached</emphasis>). In (not very realistic) code, this might
look as follows:
</para>
<programlisting><![CDATA[ private void addPersonToEvent(Long personId, Long eventId) {
Session session = HibernateUtil.currentSession();
Transaction tx = session.beginTransaction();
Person aPerson = (Person) session.load(Person.class, personId);
Event anEvent = (Event) session.load(Event.class, eventId);
tx.commit();
HibernateUtil.closeSession();
aPerson.getEvents().add(anEvent); // aPerson is detached
Session session2 = HibernateUtil.currentSession();
Transaction tx2 = session.beginTransaction();
session2.update(aPerson); // Reattachment of aPerson
tx2.commit();
HibernateUtil.closeSession();
}
]]></programlisting>
<para>
The call to <literal>update</literal> makes a detached object persistent again, you could
say it binds it to a new unit of work, so any modifications you made to it while detached
can be saved to the database.
</para>
<para>
Well, this is not much use in our current situation, but it's an important concept you can
design into your own application. For now, complete this exercise by adding a new action
to the <literal>EventManager</literal>'s main method and call it from the command line. If
you need the identifiers of a person and an event - the <literal>save()</literal> method
returns it.
</para>
<para>
This was an example of an association between two equally important classes, two entities.
As mentioned earlier, there are other classes and types in a typical model, usually "less
important". Some you have already seen, like an <literal>int</literal> or a <literal>String</literal>.
We call these classes <emphasis>value types</emphasis>, and their instances <emphasis>depend</emphasis>
on a particular entity. Instances of these types don't have their own identity, nor are they
shared between entities (two persons don't reference the same <literal>firstname</literal>
object, even if they have the same first name). Of course, value types can not only be found in
the JDK (in fact, in a Hibernate application all JDK classes are considered value types), but
you can also write dependent classes yourself, <literal>Address</literal> or <literal>MonetaryAmount</literal>,
for example.
</para>
<para>
You can also design a collection of value types. This is conceptually very different from a
collection of references to other entities, but looks almost the same in Java.
</para>
</sect2>
<sect2 id="tutorial-associations-valuecollections">
<title>Collection of values</title>
<para>
We add a collection of value typed objects to the <literal>Person</literal> entity. We want to
store email addresses, so the type we use is <literal>String</literal>, and the collection is
again a <literal>Set</literal>:
</para>
<programlisting><![CDATA[private Set emailAddresses = new HashSet();
public Set getEmailAddresses() {
return emailAddresses;
}
public void setEmailAddresses(Set emailAddresses) {
this.emailAddresses = emailAddresses;
}]]></programlisting>
<para>
The mapping of this <literal>Set</literal>:
</para>
<programlisting><![CDATA[<set name="emailAddresses" table="PERSON_EMAIL_ADDR">
<key column="PERSON_ID"/>
<element type="string" column="EMAIL_ADDR"/>
</set>]]></programlisting>
<para>
The difference compared with the earlier mapping is the <literal>element</literal> part, which tells Hibernate that the collection
does not contain references to another entity, but a collection of elements of type
<literal>String</literal> (the lowercase name tells you it's a Hibernate mapping type/converter).
Once again, the <literal>table</literal> attribute of the <literal>set</literal> element determines
the table name for the collection. The <literal>key</literal> element defines the foreign-key column
name in the collection table. The <literal>column</literal> attribute in the <literal>element</literal>
element defines the column name where the <literal>String</literal> values will actually be stored.
</para>
<para>
Have a look at the updated schema:
</para>
<programlisting><![CDATA[
_____________ __________________
| | | | _____________
| EVENTS | | PERSON_EVENT | | | ___________________
|_____________| |__________________| | PERSON | | |
| | | | |_____________| | PERSON_EMAIL_ADDR |
| *EVENT_ID | <--> | *EVENT_ID | | | |___________________|
| EVENT_DATE | | *PERSON_ID | <--> | *PERSON_ID | <--> | *PERSON_ID |
| TITLE | |__________________| | AGE | | *EMAIL_ADDR |
|_____________| | FIRSTNAME | |___________________|
| LASTNAME |
|_____________|
]]></programlisting>
<para>
You can see that the primary key of the collection table is in fact a composite key,
using both columns. This also implies that there can't be duplicate email addresses
per person, which is exactly the semantics we need for a set in Java.
</para>
<para>
You can now try and add elements to this collection, just like we did before by
linking persons and events. It's the same code in Java.
</para>
</sect2>
<sect2 id="tutorial-associations-bidirectional">
<title>Bi-directional associations</title>
<para>
Next we are going to map a bi-directional association - making the association between
person and event work from both sides in Java. Of course, the database schema doesn't
change, we still have many-to-many multiplicity. A relational database is more flexible
than a network programming language, so it doesn't need anything like a navigation
direction - data can be viewed and retrieved in any possible way.
</para>
<para>
First, add a collection of participants to the <literal>Event</literal> Event class:
</para>
<programlisting><![CDATA[private Set participants = new HashSet();
public Set getParticipants() {
return participants;
}
public void setParticipants(Set participants) {
this.participants = participants;
}]]></programlisting>
<para>
Now map this side of the association too, in <literal>Event.hbm.xml</literal>.
</para>
<programlisting><![CDATA[<set name="participants" table="PERSON_EVENT" inverse="true">
<key column="EVENT_ID"/>
<many-to-many column="PERSON_ID" class="Person"/>
</set>]]></programlisting>
<para>
As you see, these are normal <literal>set</literal> mappings in both mapping documents.
Notice that the column names in <literal>key</literal> and <literal>many-to-many</literal> are
swapped in both mapping documents. The most important addition here is the
<literal>inverse="true"</literal> attribute in the <literal>set</literal> element of the
<literal>Event</literal>'s collection mapping.
</para>
<para>
What this means is that Hibernate should take the other side - the <literal>Person</literal> class -
when it needs to find out information about the link between the two. This will be a lot easier to
understand once you see how the bi-directional link between our two entities is created .
</para>
</sect2>
<sect2 id="tutorial-associations-usingbidir">
<title>Working bi-directional links</title>
<para>
First, keep in mind that Hibernate does not affect normal Java semantics. How did we create a
link between a <literal>Person</literal> and an <literal>Event</literal> in the unidirectional
example? We added an instance of <literal>Event</literal> to the collection of event references,
of an instance of <literal>Person</literal>. So, obviously, if we want to make this link working
bi-directional, we have to do the same on the other side - adding a <literal>Person</literal>
reference to the collection in an <literal>Event</literal>. This "setting the link on both sides"
is absolutely necessary and you should never forget doing it.
</para>
<para>
Many developers program defensive and create a link management methods to
correctly set both sides, e.g. in <literal>Person</literal>:
</para>
<programlisting><![CDATA[protected Set getEvents() {
return events;
}
protected void setEvents(Set events) {
this.events = events;
}
public void addToEvent(Event event) {
this.getEvents().add(event);
event.getParticipants().add(this);
}
public void removeFromEvent(Event event) {
this.getEvents().remove(event);
event.getParticipants().remove(this);
}]]></programlisting>
<para>
Notice that the get and set methods for the collection are now protected - this allows classes in the
same package and subclasses to still access the methods, but prevents everybody else from messing
with the collections directly (well, almost). You should probably do the same with the collection
on the other side.
</para>
<para>
What about the <literal>inverse</literal> mapping attribute? For you, and for Java, a bi-directional
link is simply a matter of setting the references on both sides correctly. Hibernate however doesn't
have enough information to correctly arrange SQL <literal>INSERT</literal> and <literal>UPDATE</literal>
statements (to avoid constraint violations), and needs some help to handle bi-directional associations
properly. Making one side of the association <literal>inverse</literal> tells Hibernate to basically
ignore it, to consider it a <emphasis>mirror</emphasis> of the other side. That's all that is necessary
for Hibernate to work out all of the issues when transformation a directional navigation model to
a SQL database schema. The rules you have to remember are straightforward: All bi-directional associations
need one side as <literal>inverse</literal>. In a one-to-many association it has to be the many-side,
in many-to-many association you can pick either side, there is no difference.
</para>
<!--
<para>
In the next section we integrate Hibernate with Tomcat and WebWork - the <literal>EventManager</literal>
doesn't scale anymore with our growing application.
</para>
-->
</sect2>
</sect1>
<sect1 id="tutorial-summary">
<title>Summary</title>
<para>
This tutorial covered the basics of writing a simple standalone Hibernate application.
</para>
<para>
If you already feel confident with Hibernate, continue browsing through the reference
documentation table of contents for topics you find interesting - most asked are
transactional processing (<xref linkend="transactions"/>), fetch
performance (<xref linkend="performance"/>), or the usage of the API (<xref linkend="objectstate"/>)
and the query features (<xref linkend="objectstate-querying"/>).
</para>
<para>
Don't forget to check the Hibernate website for more (specialized) tutorials.
</para>
</sect1>
</chapter>