Author: steve.ebersole(a)jboss.com
Date: 2009-06-10 13:17:48 -0400 (Wed, 10 Jun 2009)
New Revision: 16741
Modified:
core/trunk/documentation/manual/src/main/docbook/en-US/content/tutorial.xml
Log:
HHH-3953 - Update tutorial chapter
Modified: core/trunk/documentation/manual/src/main/docbook/en-US/content/tutorial.xml
===================================================================
--- core/trunk/documentation/manual/src/main/docbook/en-US/content/tutorial.xml 2009-06-10
14:27:12 UTC (rev 16740)
+++ core/trunk/documentation/manual/src/main/docbook/en-US/content/tutorial.xml 2009-06-10
17:17:48 UTC (rev 16741)
@@ -27,25 +27,17 @@
<!ENTITY mdash "-">
]>
- <!-- todo : need separate sections, one for each tutorial -->
-
<chapter id="tutorial">
- <title>Introduction to Hibernate</title>
-
- <sect1 id="tutorial-intro">
- <title>Preface</title>
+ <title>Tutorial</title>
- <para>
- Intended for new users, this chapter provides an introduction to Hibernate
- The chapter provides a step-by-step tutorial, starting with a simple
- application using an in-memory database. The tutorial is
- based on an earlier tutorial developed by Michael Gloegl. All
- code is contained in the <filename>tutorials/web</filename>
directory
- of the project source.
- </para>
+ <para>
+ Intended for new users, this chapter provides an step-by-step introduction
+ to Hibernate, starting with a simple application using an in-memory database.
The
+ tutorial is based on an earlier tutorial developed by Michael Gloegl. All
+ code is contained in the <filename>tutorials/web</filename> directory
of the project
+ source.
+ </para>
- </sect1>
-
<important>
<para>
This tutorial expects the user have knowledge of both Java and
@@ -57,11 +49,9 @@
<note>
<para>
- There is another tutorial/example application in the
- <filename>/tutorials/eg</filename> directory of the project
source.
- That example is console-based and, as such, would not have the
- dependency on a servlet container to execute. The basic setup is
- the same as the instructions below.
+ The distribution contains another example application under
+ the <filename>tutorial/eg</filename> project source
+ directory.
</para>
</note>
@@ -70,34 +60,56 @@
<para>
For this example, we will set up a small database application that can store
- events we want to attend, and information about the host(s) of
- these events. Although you can use whatever database you feel comfortable
using, we will use an in-memory, Java database named HSQLDB
- to avoid describing installation/setup of any particular database
- servers.
+ events we want to attend and information about the host(s) of these events.
</para>
-
- <para>
- The first thing we need to do is set up the development environment,
- including all the required dependencies to Hibernate,
- as well as other libraries. Hibernate is built using Maven which,
- amongst other features, provides <literal>dependency
management</literal>;
- moreover it provides <emphasis>transitive</emphasis>
- <literal>dependency management</literal>, which simply means that
by using
- Hibernate we can define our dependency on Hibernate. Hibernate
- itself defines the dependencies it needs which then become transitive
- dependencies of our project.
- </para>
- <programlisting><![CDATA[.
-<project
xmlns="http://maven.apache.org/POM/4.0.0"
+ <note>
+ <para>
+ Although you can use whatever database you feel comfortable using, we
+ will use <ulink
url="http://hsqldb.org/">HSQLDB</ulink> (an in-memory,
+ Java database) to avoid describing installation/setup of any particular
+ database servers.
+ </para>
+ </note>
+
+ <sect2 id="tutorial-firstapp-setup">
+ <title>Setup</title>
+
+ <para>
+ The first thing we need to do is to set up the development environment.
We
+ will be using the "standard layout" advocated by alot of build
tools such
+ as <ulink url="http://maven.org">Maven</ulink>.
Maven, in particular, has a
+ good resource describing this <ulink
url="http://maven.apache.org/guides/introduction/introduction-to-the...;.
+ As this tutorial is to be a web application, we will be creating and
making
+ use of <filename>src/main/java</filename>,
<filename>src/main/resources</filename>
+ and <filename>src/main/webapp</filename> directories.
+ </para>
+
+ <para>
+ We will be using Maven in this tutorial, taking advantage of its
+ transitive dependency management capabilities as well as the ability of
+ many IDEs to automatically set up a project for us based on the maven
descriptor.
+ </para>
+
+ <programlisting><![CDATA[<project
xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
- ...
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>org.hibernate.tutorials</groupId>
+ <artifactId>hibernate-tutorial</artifactId>
+ <version>1.0.0-SNAPSHOT</version>
+ <name>First Hibernate Tutorial</name>
+
+ <build>
+ <!-- we dont want the version to be part of the generated war file name
-->
+ <finalName>${artifactId}</finalName>
+ </build>
+
<dependencies>
<dependency>
- <groupId>${groupId}</groupId>
+ <groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
</dependency>
@@ -106,34 +118,52 @@
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
</dependency>
+
+ <!-- Hibernate uses slf4j for logging, for our purposes here use the simple
backend -->
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-simple</artifactId>
+ </dependency>
+
+ <!-- Hibernate gives you a choice of bytecode providers between cglib and
javassist -->
+ <dependency>
+ <groupId>javassist</groupId>
+ <artifactId>javassist</artifactId>
+ </dependency>
</dependencies>
</project>]]></programlisting>
- <note>
- <para>
- Essentially we are describing here the
- <filename>/tutorials/web/pom.xml</filename> file. See the
- <ulink url="http://maven.org">Maven</ulink> site
for more information.
- </para>
- </note>
+ <tip>
+ <para>
+ It is not a requirement to use Maven. If you wish to use something
else to
+ build this tutoial (such as Ant), the layout will remain the same.
The only
+ change is that you will need to manually account for all the needed
+ dependencies. If you use something like <ulink
url="http://ant.apache.org/ivy/">Ivy</ulink>
+ providing transitive dependency management you would still use the
dependencies
+ mentioned below. Otherwise, you'd need to grab
<emphasis>all</emphasis>
+ dependencies, both explicit and transitive, and add them to the
project's
+ classpath. If working from the Hibernate distribution bundle, this
would mean
+ <filename>hibernate3.jar</filename>, all artifacts in
the
+ <filename>lib/required</filename> directory and all files
from either the
+ <filename>lib/bytecode/cglib</filename> or
<filename>lib/bytecode/javassist</filename>
+ directory; additionally you will need both the servlet-api jar and
one of the slf4j
+ logging backends.
+ </para>
+ </tip>
- <tip>
<para>
- While not strictly necessary, most IDEs have integration with Maven
- to read these POM files and automatically set up a project for you.
+ Save this file as <filename>pom.xml</filename> in the project
root directory.
</para>
- </tip>
+ </sect2>
- <para>
- Next, create a class that represents the event you want to store in the
database.
- </para>
-
+
<sect2 id="tutorial-firstapp-firstclass">
<title>The first class</title>
<para>
- The first persistent class is a simple JavaBean class with some
properties:
+ Next, we create a class that represents the event we want to store in
the
+ database; it is a simple JavaBean class with some properties:
</para>
<programlisting><![CDATA[package org.hibernate.tutorial.domain;
@@ -175,57 +205,50 @@
<para>
This class uses standard JavaBean naming conventions for property
- getter and setter methods, as well as private visibility for the fields.
Although this is
- a recommended design, it is not required. Hibernate can also access
fields directly,
- the benefit of accessor methods is robustness for refactoring. The
no-argument
- constructor is required to instantiate an object of this class through
reflection.
+ getter and setter methods, as well as private visibility for the
+ fields. Although this is the recommended design, it is 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, especially web applications, need to distinguish
objects by identifier, so you
- should consider this a feature rather than a limitation. However, we
usually do not manipulate
- the identity of an object, hence the setter method should be private.
Only Hibernate will assign
- identifiers when an object is saved. Hibernate can access public,
private,
- and protected accessor methods, as well as public, private and protected
fields directly. The
- choice is up to you and you can match it to fit your application design.
+ 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, especially web applications, need to distinguish
+ objects by identifier, so you should consider this a feature rather
+ than a limitation. However, we usually do not manipulate the identity
+ of an object, hence the setter method should be private. Only Hibernate
+ will assign identifiers when an object is saved. Hibernate can access
+ public, private, and protected accessor methods, as well as public,
+ private and 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.
+ 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 or public
+ visibility is required for runtime proxy generation and efficient data
+ retrieval without bytecode instrumentation.
</para>
<para>
- Place the Java source file in a directory called
<literal>src</literal> in the
- development folder, and in its correct package. The directory should now
look like this:
+ Save this file to the
<filename>src/main/java/org/hibernate/tutorial/domain</filename>
+ directory.
</para>
-
- <programlisting><![CDATA[.
-+lib
- <Hibernate and third-party libraries>
-+src
- +events
- 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.
+ 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>
@@ -237,25 +260,28 @@
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
-<hibernate-mapping>
+<hibernate-mapping package="org.hibernate.tutorial.domain">
[...]
</hibernate-mapping>]]></programlisting>
<para>
- Hibernate DTD is sophisticated. You can use it for
- auto-completion of XML mapping elements and attributes in your editor or
- IDE. Opening up the DTD file in your text editor is the
- easiest way to get an overview of all elements and attributes, and to
view
- the defaults, as well as some comments. 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.
+ Hibernate DTD is sophisticated. You can use it for auto-completion
+ of XML mapping elements and attributes in your editor or IDE.
+ Opening up the DTD file in your text editor is the easiest way to
+ get an overview of all elements and attributes, and to view the
+ defaults, as well as some comments. 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
+ <filename>hibernate-core.jar</filename> (it is also included
in the
+ <filename>hibernate3.jar</filename>, if using the
distribution bundle).
</para>
- <para>
- We will omit the DTD declaration in future examples to shorten the code.
It is,
- of course, not optional.
- </para>
+ <important>
+ <para>
+ We will omit the DTD declaration in future examples to shorten the
code. It is,
+ of course, not optional.
+ </para>
+ </important>
<para>
Between the two <literal>hibernate-mapping</literal> tags,
include a
@@ -264,25 +290,27 @@
a mapping to a table in the SQL database:
</para>
- <programlisting><![CDATA[<hibernate-mapping>
+ <programlisting><![CDATA[<hibernate-mapping
package="org.hibernate.tutorial.domain">
- <class name="events.Event" table="EVENTS">
+ <class name="Event" table="EVENTS">
</class>
</hibernate-mapping>]]></programlisting>
<para>
- So far we have told Hibernate how to persist and load object of class
<literal>Event</literal>
- to the table <literal>EVENTS</literal>. Each instance is now
represented by a row in that table.
- Now we can continue by mapping the unique identifier property to the
tables primary key.
- As we do not want to care about handling this identifier, we configure
Hibernate's
+ So far we have told Hibernate how to persist and load object of
+ class <literal>Event</literal> to the table
+ <literal>EVENTS</literal>. Each instance is now represented
by a
+ row in that table. Now we can continue by mapping the unique
+ identifier property to the tables primary key. As we do not 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>
+ <programlisting><![CDATA[<hibernate-mapping
package="org.hibernate.tutorial.domain">
- <class name="events.Event" table="EVENTS">
+ <class name="Event" table="EVENTS">
<id name="id" column="EVENT_ID">
<generator class="native"/>
</id>
@@ -291,29 +319,43 @@
</hibernate-mapping>]]></programlisting>
<para>
- The <literal>id</literal> element is the declaration of the
identifier 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 to use for this primary key.
The nested
- <literal>generator</literal> element specifies the identifier
generation strategy.
-
- In this case you used <literal>native</literal>, which selects the best
strategy depending
- on the configured database dialect. Hibernate supports database
generated, globally
- unique, as well as application assigned, identifiers. Hibernate supports
any strategy you have written
- an extension for.
+ The <literal>id</literal> element is the declaration of the
+ identifier property. The
<literal>name="id"</literal> mapping
+ attribute declares the name of the JavaBean property and tells
+ Hibernate to use the <literal>getId()</literal> and
+ <literal>setId()</literal> methods to access the property.
The
+ column attribute tells Hibernate which column of the
+ <literal>EVENTS</literal> table holds the primary key value.
</para>
<para>
- Finally, include declarations for the persistent properties of the class
in
- the mapping file. By default, no properties of the class are considered
+ The nested <literal>generator</literal> element specifies
the
+ identifier generation strategy (aka how are identifier values
+ generated?). In this case we choose
<literal>native</literal>,
+ which offers a level of portability depending on the configured
+ database dialect. Hibernate supports database generated, globally
+ unique, as well as application assigned, identifiers. Identifier
+ value generation is also one of Hibernate's many extension points
+ and you can plugin in your own strategy.
+ </para>
+
+ <tip>
+ <para>
+ <literal>native</literal> is no longer consider the best
strategy in terms of portability. for further
+ discussion, see <xref linkend="portability-idgen"/>
+ </para>
+ </tip>
+
+ <para>
+ Lastly, we need to tell Hibernate about the remaining entity class
+ properties. By default, no properties of the class are considered
persistent:
</para>
<programlisting><![CDATA[
-<hibernate-mapping>
+<hibernate-mapping package="org.hibernate.tutorial.domain">
- <class name="events.Event" table="EVENTS">
+ <class name="Event" table="EVENTS">
<id name="id" column="EVENT_ID">
<generator class="native"/>
</id>
@@ -324,27 +366,31 @@
</hibernate-mapping>]]></programlisting>
<para>
- Similar to 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. In this case, Hibernate will search for
- <literal>getDate()/setDate()</literal>, as well as
<literal>getTitle()/setTitle()</literal>.
+ Similar to 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. In this case, Hibernate will search
+ for <literal>getDate()</literal>,
<literal>setDate()</literal>,
+ <literal>getTitle()</literal> and
<literal>setTitle()</literal>
+ methods.
</para>
- <para>
- Why does the <literal>date</literal> property mapping include
the
- <literal>column</literal> attribute, but the
<literal>title</literal>
- does not? Without the <literal>column</literal> attribute,
Hibernate
- by default uses the property name as the column name. This works for
- <literal>title</literal>, however,
<literal>date</literal> is a reserved
- keyword in most databases so you will need to map it to a different
name.
- </para>
+ <note>
+ <para>
+ Why does the <literal>date</literal> property mapping
include the
+ <literal>column</literal> attribute, but the
<literal>title</literal>
+ does not? Without the <literal>column</literal>
attribute, Hibernate
+ by default uses the property name as the column name. This works for
+ <literal>title</literal>, however,
<literal>date</literal> is a reserved
+ keyword in most databases so you will need to map it to a different
name.
+ </para>
+ </note>
<para>
- The <literal>title</literal> mapping also lacks
- a <literal>type</literal> attribute. The types declared and
used in the mapping
- files are not, as you might expect, Java data types. They are also not
SQL
- database types. These types are called <emphasis>Hibernate mapping
types</emphasis>,
- converters which can translate from Java to SQL data types and vice
versa. Again,
+ The <literal>title</literal> mapping also lacks a
<literal>type</literal> attribute. The
+ types declared and used in the mapping files are not Java data types;
they are not SQL
+ database types either. These types are 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
@@ -355,59 +401,64 @@
<literal>timestamp</literal> converter.
</para>
+ <tip>
+ <para>
+ Hibernate makes this mapping type determination using reflection when
the mapping files
+ are processed. This can take time and resources, so if startup
performance is important
+ you should consider explicitly defining the type to use.
+ </para>
+ </tip>
+
<para>
- Save the mapping file as <literal>Event.hbm.xml</literal> 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 is a convention in the Hibernate developer community. The
directory structure
- should now look like this:
+ Save this mapping file as
+
<filename>src/main/resources/org/hibernate/tutorial/domain/Event.hbm.xml</filename>.
</para>
- <programlisting><![CDATA[.
-+lib
- <Hibernate and third-party libraries>
-+src
- +events
- Event.java
- Event.hbm.xml]]></programlisting>
-
- <para>
- The next section continues with the main configuration of Hibernate.
- </para>
-
</sect2>
<sect2 id="tutorial-firstapp-configuration"
revision="2">
<title>Hibernate configuration</title>
<para>
- You should have a persistent class and its mapping file in place. It is
now time to configure
- Hibernate. Before you do this, you will need a database. HSQL DB, a
java-based SQL DBMS,
- can be downloaded from the HSQL DB
website(http://hsqldb.org/). You only
need the <literal>hsqldb.jar</literal>
- from this download. Place this file in the
<literal>lib/</literal> directory of the
- development folder.
+ At this point, you should have the persistent class and its mapping
+ file in place. It is now time to configure Hibernate. First let's
set up
+ HSQLDB to run in "server mode"
</para>
+ <note>
+ <para>
+ We do this do that the data remains between runs.
+ </para>
+ </note>
+
<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. Start the database by
running
- <literal>java -classpath ../lib/hsqldb.jar
org.hsqldb.Server</literal> in this data directory.
- You will see it start up and bind to a TCP/IP socket; this is where our
application
- will connect later. If you want to start with a fresh database during
this tutorial,
- shutdown HSQL DB (press <literal>CTRL + C</literal> in the
window), delete all files in the
- <literal>data/</literal> directory, and start HSQL DB again.
+ We will utilize the Maven exec plugin to launch the HSQLDB server
+ by running:
+ <command> mvn exec:java
-Dexec.mainClass="org.hsqldb.Server" -Dexec.args="-database.0
file:target/data/tutorial"</command>
+ You will see it start up and bind to a TCP/IP socket; this is where
+ our application will connect later. If you want to start
+ with a fresh database during this tutorial, shutdown HSQLDB, delete
+ all files in the <filename>target/data</filename> directory,
+ and start HSQLDB again.
</para>
<para>
- Hibernate is the layer in your application that connects to this
database, so it needs
- connection information. The connections are made through a JDBC
connection pool, which you
- will also have to configure. The Hibernate distribution contains several
open source JDBC connection
- pooling tools, but you will use the Hibernate built-in connection pool
for this tutorial.
- You will 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.
+ Hibernate will be connecting to the database on behalf of your
application, so it needs to know
+ how to obtain connections. For this tutorial we will be using a
standalone connection
+ pool (as opposed to a
<interfacename>javax.sql.DataSource</interfacename>). Hibernate comes with
+ support for two third-party open source JDBC connection pools:
+ <ulink
url="https://sourceforge.net/projects/c3p0">c3p0</ulink> and
+ <ulink
url="http://proxool.sourceforge.net/">proxool</ulink>. However, we
will be using the
+ Hibernate built-in connection pool for this tutorial.
</para>
+ <caution>
+ <para>
+ The built-in Hibernate connection pool is in no way intended for
production use. It
+ lacks several features found on any decent connection pool.
+ </para>
+ </caution>
+
<para>
For Hibernate's configuration, we can use a simple
<literal>hibernate.properties</literal> file, a
more sophisticated <literal>hibernate.cfg.xml</literal> file,
or even complete
@@ -445,19 +496,22 @@
<property name="show_sql">true</property>
<!-- Drop and re-create the database schema on startup -->
- <property name="hbm2ddl.auto">create</property>
+ <property name="hbm2ddl.auto">update</property>
- <mapping resource="events/Event.hbm.xml"/>
+ <mapping
resource="org/hibernate/tutorial/domain/Event.hbm.xml"/>
</session-factory>
</hibernate-configuration>]]></programlisting>
+ <note>
+ <para>Notice that this configuration file specifies a different
DTD</para>
+ </note>
+
<para>
- This XML configuration uses a different DTD. You configure
- Hibernate's <literal>SessionFactory</literal>.
SessionFactory is a global factory responsible
- for a particular database. If you have several databases, for easier
startup you should use several
- <literal><session-factory></literal>
configurations in
+ You configure Hibernate's
<literal>SessionFactory</literal>. SessionFactory is a global
+ factory responsible for a particular database. If you have several
databases, for easier
+ startup you should use several
<literal><session-factory></literal> configurations in
several configuration files.
</para>
@@ -465,9 +519,18 @@
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.
- Hibernate's automatic session management for persistence contexts is
particularly useful in this context.
+ </para>
- The <literal>hbm2ddl.auto</literal> option turns on automatic
generation of
+ <tip>
+ <para>
+ In most cases, Hibernate is able to properly determine which dialect
to use. See
+ <xref linkend="portability-dialectresolver"/> for
more information.
+ </para>
+ </tip>
+
+ <para>
+ Hibernate's automatic session management for persistence contexts is
particularly useful
+ in this context. The <literal>hbm2ddl.auto</literal> option
turns on automatic generation of
database schemas directly into the database. This can also be turned
off by removing the configuration option, or redirected to a file with
the help of
the <literal>SchemaExport</literal> Ant task. Finally, add
the mapping file(s)
@@ -475,116 +538,83 @@
</para>
<para>
- Copy the file into the source directory, so it will end up in the
- root of the classpath. On startup, Hibernate automatically searches for a
file called
- <literal>hibernate.cfg.xml</literal> in the root of the
classpath.
+ Save this file as <filename>hibernate.cfg.xml</filename> into
the
+ <filename>src/main/resources</filename> directory.
</para>
</sect2>
- <sect2 id="tutorial-firstapp-ant" revision="1">
- <title>Building with Ant</title>
+ <sect2 id="tutorial-firstapp-mvn" revision="1">
+ <title>Building with Maven</title>
<para>
- We will now build the tutorial with Ant. You will need to have Ant
installed.
- It is available from the <ulink
url="http://ant.apache.org/bindownload.cgi">Ant download page</ulink>.
- Installation instructions for Ant, however, are not be covered here.
Please refer to the
- <ulink
url="http://ant.apache.org/manual/index.html">Ant
manual</ulink> for further information. After you
- have installed Ant, you can start to create the buildfile. It will be
called
- <literal>build.xml</literal> and placed directly in the
development directory.
+ We will now build the tutorial with Maven. You will need to
+ have Maven installed; it is available from the
+ <ulink
url="http://maven.apache.org/download.html">Maven
download page</ulink>.
+ Maven will read the <filename>/pom.xml</filename> file we
created
+ earlier and know how to perform some basic project tasks. First,
+ lets run the <literal>compile</literal> goal to make sure we
can compile
+ everything so far:
</para>
- <para>
- A basic build file looks like this:
- </para>
+ <programlisting><![CDATA[[hibernateTutorial]$ mvn compile
+[INFO] Scanning for projects...
+[INFO] ------------------------------------------------------------------------
+[INFO] Building First Hibernate Tutorial
+[INFO] task-segment: [compile]
+[INFO] ------------------------------------------------------------------------
+[INFO] [resources:resources]
+[INFO] Using default encoding to copy filtered resources.
+[INFO] [compiler:compile]
+[INFO] Compiling 1 source file to
/home/steve/projects/sandbox/hibernateTutorial/target/classes
+[INFO] ------------------------------------------------------------------------
+[INFO] BUILD SUCCESSFUL
+[INFO] ------------------------------------------------------------------------
+[INFO] Total time: 2 seconds
+[INFO] Finished at: Tue Jun 09 12:25:25 CDT 2009
+[INFO] Final Memory: 5M/547M
+[INFO]
------------------------------------------------------------------------]]></programlisting>
- <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 library 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, configuration and Hibernate mapping files, for example.
If you now run Ant, you
- will 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" revision="3">
<title>Startup and helpers</title>
<para>
- It is time to load and store some <literal>Event</literal>
objects, but first
- you have to complete the setup with some infrastructure code. You have to
startup
- Hibernate by building a global
<literal>SessionFactory</literal>
- object and storing 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, that is instantiated once.
+ It is time to load and store some <literal>Event</literal>
+ objects, but first you have to complete the setup with some
+ infrastructure code. You have to startup Hibernate by building
+ a global
<interfacename>org.hibernate.SessionFactory</interfacename>
+ object and storing it somewhere for easy access in application code. A
+ <interfacename>org.hibernate.SessionFactory</interfacename>
is used to
+ obtain <interfacename>org.hibernate.Session</interfacename>
instances.
+ A <interfacename>org.hibernate.Session</interfacename>
represents a
+ single-threaded unit of work. The
+ <interfacename>org.hibernate.SessionFactory</interfacename>
is a
+ thread-safe global object that is instantiated once.
</para>
<para>
- Create a <literal>HibernateUtil</literal> helper class that
takes care
- of startup and makes accessing a
<literal>SessionFactory</literal> convenient.
- The following example illustrates the implementation:
+ We will create a <literal>HibernateUtil</literal> helper
class that
+ takes care of startup and makes accessing the
+ <interfacename>org.hibernate.SessionFactory</interfacename>
more convenient.
</para>
- <programlisting><![CDATA[package util;
+ <programlisting><![CDATA[package org.hibernate.tutorial.util;
-import org.hibernate.*;
-import org.hibernate.cfg.*;
+import org.hibernate.SessionFactory;
+import org.hibernate.cfg.Configuration;
public class HibernateUtil {
- private static final SessionFactory sessionFactory;
+ private static final SessionFactory sessionFactory = buildSessionFactory();
- static {
+ private static SessionFactory buildSessionFactory() {
try {
// Create the SessionFactory from hibernate.cfg.xml
- sessionFactory = new Configuration().configure().buildSessionFactory();
- } catch (Throwable ex) {
+ return 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);
@@ -598,39 +628,28 @@
}]]></programlisting>
<para>
- This class not only produces the global
<literal>SessionFactory</literal> in
- its static initializer that is called once by the JVM when the class is
loaded. It also
- hides the fact that it uses a static singleton. It might as well lookup
the
- <literal>SessionFactory</literal> from JNDI in an application
server.
+ Save this code as
+
<filename>src/main/java/org/hibernate/tutorial/util/HibernateUtil.java</filename>
</para>
<para>
- If you give the <literal>SessionFactory</literal> a name in
your configuration
- file, Hibernate will try to bind it to JNDI after it has been built.
- To avoid this code completely you could also use JMX deployment and let
the
- JMX-capable container instantiate and bind a
<literal>HibernateService</literal>
- to JNDI. These advanced options are discussed in the Hibernate reference
- documentation.
+ This class not only produces the global
+ <interfacename>org.hibernate.SessionFactory</interfacename>
reference in
+ its static initializer; it also hides the fact that it uses a
+ static singleton. We might just as well have looked up the
+ <interfacename>org.hibernate.SessionFactory</interfacename>
reference from
+ JNDI in an application server or any other location for that matter.
</para>
<para>
- Place <literal>HibernateUtil.java</literal> in the
development source directory in
- a package next to <literal>events</literal>:
+ If you give the
<interfacename>org.hibernate.SessionFactory</interfacename>
+ a name in your configuration, Hibernate will try to bind it to
+ JNDI under that name after it has been built. Another, better option is
to
+ use a JMX deployment and let the JMX-capable container instantiate and
bind
+ a <literal>HibernateService</literal> to JNDI. Such advanced
options are
+ discussed later.
</para>
- <programlisting><![CDATA[.
-+lib
- <Hibernate and third-party libraries>
-+src
- +events
- Event.java
- Event.hbm.xml
- +util
- HibernateUtil.java
- hibernate.cfg.xml
-+data
-build.xml]]></programlisting>
-
<para>
You now need to configure a logging
system. Hibernate uses commons logging and provides two choices: Log4j
and
@@ -652,16 +671,19 @@
<title>Loading and storing objects</title>
<para>
- You can use Hibernate to load and store objects. Write an
- <literal>EventManager</literal> class with a
<literal>main()</literal> method:
+ We are now ready to start doing some real worjk with Hibernate.
+ Let's start by writing an <literal>EventManager</literal>
class
+ with a <literal>main()</literal> method:
</para>
- <programlisting><![CDATA[package events;
+ <programlisting><![CDATA[package org.hibernate.tutorial;
+
import org.hibernate.Session;
-import java.util.Date;
+import java.util.*;
-import util.HibernateUtil;
+import org.hibernate.tutorial.domain.Event;
+import org.hibernate.tutorial.util.HibernateUtil;
public class EventManager {
@@ -676,15 +698,12 @@
}
private void createAndStoreEvent(String title, Date theDate) {
-
Session session = HibernateUtil.getSessionFactory().getCurrentSession();
-
session.beginTransaction();
Event theEvent = new Event();
theEvent.setTitle(title);
theEvent.setDate(theDate);
-
session.save(theEvent);
session.getTransaction().commit();
@@ -693,165 +712,149 @@
}]]></programlisting>
<para>
- You can create a new <literal>Event</literal> object and hand
it over to Hibernate.
- Hibernate takes care of the SQL and executes
<literal>INSERT</literal>
- on the database. We will explain the
<literal>Session</literal> and
- <literal>Transaction</literal>-handling code before we run
this.
+ In <literal>createAndStoreEvent()</literal> we created a new
+ <literal>Event</literal> object and handed it over to
Hibernate.
+ At that point, Hibernate takes care of the SQL and executes an
+ <literal>INSERT</literal> on the database.
</para>
<para>
- A <literal>Session</literal> is a single unit of work. For
now we will keep things
- simple and assume a one-to-one granularity between a Hibernate
<literal>Session</literal>
- and a database transaction. To shield our code from the actual underlying
transaction
- system we use the
- <literal>Transaction</literal> API that is available on the
Hibernate <literal>Session</literal>.
- In this case plain JDBC, but it could also run with JTA.
+ A <interface>org.hibernate.Session</interface> is designed
to
+ represent a single unit of work (a single atmoic piece of work
+ to be performed). For now we will keep things simple and assume
+ a one-to-one granularity between a Hibernate
+ <interface>org.hibernate.Session</interface> and a database
+ transaction. To shield our code from the actual underlying
+ transaction system we use the Hibernate
+ <interfacename>org.hibernate.Transaction</interfacename>
API.
+ In this particular case we are using JDBC-based transactional
+ semantics, but it could also run with JTA.
</para>
<para>
- What does
<literal>sessionFactory.getCurrentSession()</literal> do? First, you can call
it
- as many times and anywhere you like, once you get hold of your
<literal>SessionFactory</literal>.
- This is easy thanks to <literal>HibernateUtil</literal>). The
<literal>getCurrentSession()</literal>
- method always returns the "current" unit of work. Remember that
we switched the configuration
- option for this mechanism to "thread" in
<literal>hibernate.cfg.xml</literal>? The current unit of work is bound to the
current Java thread that executes the application.
- However, you also have to consider when a unit of work
- begins and when it ends.
+ What does
<literal>sessionFactory.getCurrentSession()</literal> do?
+ First, you can call it as many times and anywhere you like
+ once you get hold of your
+ <interfacename>org.hibernate.SessionFactory</interfacename>.
+ The <literal>getCurrentSession()</literal> method always
returns
+ the "current" unit of work. Remember that we switched
+ the configuration option for this mechanism to "thread" in our
+ <filename>src/main/resources/hibernate.cfg.xml</filename>?
+ Due to that setting, the context of a current unit of work is bound
+ to the current Java thread that executes the application.
</para>
+ <important>
+ <para>
+ Hibernate offers three methods of current session tracking.
+ The "thread" based method is not intended for production
use;
+ it is merely useful for prototyping and tutorials such as this
+ one. Current session tracking is discussed in more detail
+ later on.
+ </para>
+ </important>
+
<para>
- A <literal>Session</literal> begins when the first call to
- <literal>getCurrentSession()</literal> is made. It is then
bound by Hibernate to the current
- thread. When the transaction ends, either through commit or rollback,
Hibernate automatically
- unbinds the <literal>Session</literal> from the thread and
closes it for you. If you call
- <literal>getCurrentSession()</literal> again, you get a new
<literal>Session</literal> and can
- start a new unit of work. This
<emphasis>thread-bound</emphasis> programming model is the most
- popular way of using Hibernate, as it allows flexible layering of your
code. Transaction
- demarcation code can be separated from data access code. This will be
covered later in this tutorial.
+ A <interface>org.hibernate.Session</interface> begins when
the
+ first call to <literal>getCurrentSession()</literal> is made
for
+ the current thread. It is then bound by Hibernate to the current
+ thread. When the transaction ends, either through commit or
+ rollback, Hibernate automatically unbinds the
+ <interface>org.hibernate.Session</interface> from the thread
+ and closes it for you. If you call
+ <literal>getCurrentSession()</literal> again, you get a new
+ <interface>org.hibernate.Session</interface> and can start a
+ new unit of work.
</para>
<para>
- Related to the unit of work scope, should the Hibernate
<literal>Session</literal> be used to
- execute one or several database operations? The above example uses one
<literal>Session</literal>
- for one operation. This is pure coincidence, the example is just not
complex enough to show any
- other approach. The scope of a Hibernate
<literal>Session</literal> is flexible but you should
- never design your application to use a new Hibernate
<literal>Session</literal> for
- <emphasis>every</emphasis> database operation. Even though it
is used in
- the following examples, consider
<emphasis>session-per-operation</emphasis>
- an anti-pattern. A real web application is shown later in the tutorial.
+ Related to the unit of work scope, should the Hibernate
+ <interface>org.hibernate.Session</interface> be used to
execute
+ one or several database operations? The above example uses one
+ <interface>org.hibernate.Session</interface> for one
operation.
+ However this is pure coincidence; the example is just not complex
+ enough to show any other approach. The scope of a Hibernate
+ <interface>org.hibernate.Session</interface> is flexible but
you
+ should never design your application to use a new Hibernate
+ <interface>org.hibernate.Session</interface> for
+ <emphasis>every</emphasis> database operation. Even though it
is
+ used in the following examples, consider
+ <emphasis>session-per-operation</emphasis> an anti-pattern.
+ A real web application is shown later in the tutorial which will
+ help illustrate this.
</para>
<para>
See <xref linkend="transactions"/> for more information
- about transaction handling and demarcation. The previous example also
skipped any error handling and
- rollback.
+ about transaction handling and demarcation. The previous
+ example also skipped any error handling and rollback.
</para>
<para>
- To run this first routine we have to add a callable target to the Ant
build file:
+ To run this, we will make use of the Maven exec plugin to call our class
+ with the necessary classpath setup:
+ <command>mvn exec:java
-Dexec.mainClass="org.hibernate.tutorial.EventManager"
-Dexec.args="store"</command>
</para>
- <programlisting><![CDATA[<target name="run"
depends="compile">
- <java fork="true" classname="events.EventManager"
classpathref="libraries">
- <classpath path="${targetdir}"/>
- <arg value="${action}"/>
- </java>
-</target>]]></programlisting>
+ <note>
+ <para>
+ You may need to perform <command>mvn compile</command>
first.
+ </para>
+ </note>
<para>
- The value of the <literal>action</literal> argument is set on
the command line when
- calling the target:
+ You should see Hibernate starting up and, depending on your
configuration,
+ lots of log output. Towards the end, the following line will be
displayed:
</para>
- <programlisting><![CDATA[C:\hibernateTutorial\>ant run
-Daction=store]]></programlisting>
-
- <para>
- After compilation, you should see Hibernate starting up and, depending on
your
- configuration, lots of log output. At the end, the following line will be
displayed:
- </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 view the values bound as arguments, or
to reduce
- the verbosity of the log, check your
<literal>log4j.properties</literal>.
+ This is the <literal>INSERT</literal> executed by Hibernate.
</para>
<para>
To list stored events an option is added 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>
+ <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>
A new <literal>listEvents() method is also added</literal>:
</para>
- <programlisting><![CDATA[private List listEvents() {
+ <programlisting><![CDATA[ private List listEvents() {
+ Session session = HibernateUtil.getSessionFactory().getCurrentSession();
+ session.beginTransaction();
+ List result = session.createQuery("from Event").list();
+ session.getTransaction().commit();
+ return result;
+ }]]></programlisting>
- Session session = HibernateUtil.getSessionFactory().getCurrentSession();
-
- session.beginTransaction();
-
- List result = session.createQuery("from Event").list();
-
- session.getTransaction().commit();
-
- return result;
-}]]></programlisting>
-
<para>
- Use an HQL (Hibernate Query Language) query to load all existing
+ Here, we are using a Hibernate Query Language (HQL) 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.
+ with the data. You can create more complex queries with HQL. See <xref
linkend="query-hql"/>
+ for more information.
</para>
<para>
- To execute and test all of this, follow these steps:
+ Now we can call our new functionality, again using the Maven exec
plugin:
+ <command>mvn exec:java
-Dexec.mainClass="org.hibernate.tutorial.EventManager"
-Dexec.args="list"</command>
</para>
- <itemizedlist>
- <listitem>
- <para>
- Run <literal>ant run -Daction=store</literal> to
store something in the database
- and, of course, to generate the database schema before through
hbm2ddl.
- </para>
- </listitem>
- <listitem>
- <para>
- Disable hbm2ddl by commenting out the property in your
<literal>hibernate.cfg.xml</literal>
- file. It is only turned on in continuous unit testing. Another
- run of hbm2ddl would <emphasis>drop</emphasis>
everything you have stored. The
- <literal>create</literal> configuration setting
translates into "drop all
- tables from the schema, then re-create all tables, when the
SessionFactory is built".
- </para>
- </listitem>
- </itemizedlist>
-
- <para>
- If you can call Ant with <literal>-Daction=list</literal>,
you will be able to view the events
- you have stored so far. You can also call the
<literal>store</literal> action.
- </para>
-
- <para>
- Most new Hibernate users fail at this point and we regularly receive
questions relating to
- <emphasis>Table not found</emphasis> error messages. However,
if you follow the
- steps outlined above you will not have this problem, as hbm2ddl creates
the database
- schema on the first run, and subsequent application restarts will use
this schema. If
- you change the mapping and/or database schema, you will have to re-enable
hbm2ddl.
- </para>
-
</sect2>
</sect1>
@@ -860,8 +863,10 @@
<title>Part 2 - Mapping associations</title>
<para>
- You have mapped a persistent entity class to a table. You can now build on
this by adding some class associations.
- First you will add people to your application, and store a list of events
they participate in.
+ So far we have mapped a single persistent entity class to a table in
+ isolation. Let's expand on that a bit and add some class associations.
+ We will add people to the application and store a list of events in
+ which they participate.
</para>
<sect2 id="tutorial-associations-mappinguser"
revision="1">
@@ -871,7 +876,7 @@
The first cut of the <literal>Person</literal> class looks
like this:
</para>
- <programlisting><![CDATA[package events;
+ <programlisting><![CDATA[package org.hibernate.tutorial.domain;
public class Person {
@@ -887,13 +892,18 @@
}]]></programlisting>
<para>
- Create a new mapping file called
<literal>Person.hbm.xml</literal>. Do not forget the
- DTD reference at the top:
+ Save this to a file named
+
<filename>src/main/java/org/hibernate/tutorial/domain/Person.java</filename>
</para>
- <programlisting><![CDATA[<hibernate-mapping>
+ <para>
+ Next, create the new mapping file as
+
<filename>src/main/resources/org/hibernate/tutorial/domain/Person.hbm.xml</filename>
+ </para>
- <class name="events.Person" table="PERSON">
+ <programlisting><![CDATA[<hibernate-mapping
package="org.hibernate.tutorial.domain">
+
+ <class name="Person" table="PERSON">
<id name="id" column="PERSON_ID">
<generator class="native"/>
</id>
@@ -924,17 +934,16 @@
<title>A unidirectional Set-based association</title>
<para>
- By adding a collection of events to the
<literal>Person</literal> class, you can
- easily navigate to the events for a particular person, without executing
an explicit query -
- by calling <literal>aPerson.getEvents()</literal>. A Java
collection, a <literal>Set</literal>, is used
- because the collection will not contain duplicate elements and the
ordering is not relevant to our examples.
+ By adding a collection of events to the
<literal>Person</literal>
+ class, you can easily navigate to the events for a particular person,
+ without executing an explicit query - by calling
+ <literal>Person#getEvents</literal>. Multi-valued
associations
+ are represented in Hibernate by one of the Java Collection Framework
+ contracts; here we choose a
<interfacename>java.util.Set</interfacename>
+ because the collection will not contain duplicate elements and the
ordering
+ is not relevant to our examples:
</para>
- <para>
- We need unidirectional, many-valued associations, implemented with a
<literal>Set</literal>.
- Write the code for this in the Java classes and then map it:
- </para>
-
<programlisting><![CDATA[public class Person {
private Set events = new HashSet();
@@ -949,17 +958,19 @@
}]]></programlisting>
<para>
- Before you map this association, consider the other side. You could just
keep this
- unidirectional. Or, you could create another collection on the
<literal>Event</literal>, if you
- want to be able to navigate it bi-directional, i.e.,
<literal>anEvent.getParticipants()</literal>.
- This is not necessary, from a functional perspective. You can always
execute an explicit query
- to retrieve the participants for a particular event. 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
- is called a <emphasis>many-to-many</emphasis> association.
Hence, we use Hibernate's
- many-to-many mapping:
+ Before mapping this association, let's consider the other side.
+ We could just keep this unidirectional or create another
+ collection on the <literal>Event</literal>, if we wanted to
be
+ able to navigate it from both directions. This is not necessary,
+ from a functional perspective. You can always execute an explicit
+ query to retrieve the participants for a particular event. 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 is called a <emphasis>many-to-many</emphasis>
+ association. Hence, we use Hibernate's many-to-many mapping:
</para>
- <programlisting><![CDATA[<class name="events.Person"
table="PERSON">
+ <programlisting><![CDATA[<class name="Person"
table="PERSON">
<id name="id" column="PERSON_ID">
<generator class="native"/>
</id>
@@ -969,20 +980,24 @@
<set name="events" table="PERSON_EVENT">
<key column="PERSON_ID"/>
- <many-to-many column="EVENT_ID" class="events.Event"/>
+ <many-to-many column="EVENT_ID" class="Event"/>
</set>
</class>]]></programlisting>
<para>
- Hibernate supports a broad range of collection mappings, a
<literal><set></literal> being most
- common. For a many-to-many association, or
<emphasis>n:m</emphasis> entity relationship, an
- association table is required. 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
side, is defined with the
- <literal><key></literal> element, the column
name for the event's side with the
- <literal>column</literal> attribute of the
<literal><many-to-many></literal>. You also
- have to tell Hibernate the class of the objects in your collection (the
class on the
+ Hibernate supports a broad range of collection mappings, a
+ <literal>set</literal> being most common. For a
many-to-many
+ association, or <emphasis>n:m</emphasis> entity relationship,
an
+ association table is required. Each row in this table represents
+ a link between a person and an event. The table name is
+ decalred using the <literal>table</literal> attribute of the
+ <literal>set</literal> element. The identifier column name
in
+ the association, for the person side, is defined with the
+ <literal>key</literal> element, the column name for the
event's
+ side with the <literal>column</literal> attribute of the
+ <literal>many-to-many</literal>. You also have to tell
Hibernate
+ the class of the objects in your collection (the class on the
other side of the collection of references).
</para>
@@ -1013,73 +1028,77 @@
Now we will bring some people and events together in a new method in
<literal>EventManager</literal>:
</para>
- <programlisting><![CDATA[private void addPersonToEvent(Long
personId, Long eventId) {
+ <programlisting><![CDATA[ private void addPersonToEvent(Long
personId, Long eventId) {
+ Session session = HibernateUtil.getSessionFactory().getCurrentSession();
+ session.beginTransaction();
- Session session = HibernateUtil.getSessionFactory().getCurrentSession();
- session.beginTransaction();
+ Person aPerson = (Person) session.load(Person.class, personId);
+ Event anEvent = (Event) session.load(Event.class, eventId);
+ aPerson.getEvents().add(anEvent);
- Person aPerson = (Person) session.load(Person.class, personId);
- Event anEvent = (Event) session.load(Event.class, eventId);
+ session.getTransaction().commit();
+ }]]></programlisting>
- aPerson.getEvents().add(anEvent);
-
- session.getTransaction().commit();
-}]]></programlisting>
-
<para>
- After loading a <literal>Person</literal> and an
<literal>Event</literal>, simply
- modify the collection using the normal collection methods. There is no
explicit call
- to <literal>update()</literal> or
<literal>save()</literal>; Hibernate automatically
- detects that the collection has been modified and needs to be updated.
This is called <emphasis>automatic
- dirty checking</emphasis>. 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>. In our code,
the unit of work ends with a commit,
- or rollback, of the database transaction. This is defined by the
<literal>thread</literal> configuration
- option for the <literal>CurrentSessionContext</literal>
class.
+ After loading a <literal>Person</literal> and an
+ <literal>Event</literal>, simply modify the collection using
the
+ normal collection methods. There is no explicit call to
+ <literal>update()</literal> or
<literal>save()</literal>;
+ Hibernate automatically detects that the collection has been modified
+ and needs to be updated. This is called
+ <emphasis>automatic dirty checking</emphasis>. 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
+ <interfacename>org.hibernate.Session</interfacename>,
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>. In our code, the unit of
work
+ ends with a commit, or rollback, of the database transaction.
</para>
<para>
- You can load person and event in different units of work. Or you can
modify an object
- outside of a <literal>Session</literal>, when it is not in
persistent state (if it was persistent
- before, this state is called <emphasis>detached</emphasis>).
You can even modify a collection when
- it is detached:
+ You can load person and event in different units of work. Or
+ you can modify an object outside of a
+ <interfacename>org.hibernate.Session</interfacename>, when
it
+ is not in persistent state (if it was persistent before, this
+ state is called <emphasis>detached</emphasis>). You can
even
+ modify a collection when it is detached:
</para>
- <programlisting><![CDATA[private void addPersonToEvent(Long
personId, Long eventId) {
+ <programlisting><![CDATA[ private void addPersonToEvent(Long
personId, Long eventId) {
+ Session session = HibernateUtil.getSessionFactory().getCurrentSession();
+ session.beginTransaction();
- Session session = HibernateUtil.getSessionFactory().getCurrentSession();
- session.beginTransaction();
+ Person aPerson = (Person) session
+ .createQuery("select p from Person p left join fetch p.events where
p.id = :pid")
+ .setParameter("pid", personId)
+ .uniqueResult(); // Eager fetch the collection so we can use it detached
+ Event anEvent = (Event) session.load(Event.class, eventId);
- Person aPerson = (Person) session
- .createQuery("select p from Person p left join fetch p.events where p.id
= :pid")
- .setParameter("pid", personId)
- .uniqueResult(); // Eager fetch the collection so we can use it detached
+ session.getTransaction().commit();
- Event anEvent = (Event) session.load(Event.class, eventId);
+ // End of first unit of work
- session.getTransaction().commit();
+ aPerson.getEvents().add(anEvent); // aPerson (and its collection) is detached
- // End of first unit of work
+ // Begin second unit of work
- aPerson.getEvents().add(anEvent); // aPerson (and its collection) is detached
+ Session session2 = HibernateUtil.getSessionFactory().getCurrentSession();
+ session2.beginTransaction();
+ session2.update(aPerson); // Reattachment of aPerson
- // Begin second unit of work
+ session2.getTransaction().commit();
+ }]]></programlisting>
- Session session2 = HibernateUtil.getSessionFactory().getCurrentSession();
- session2.beginTransaction();
-
- session2.update(aPerson); // Reattachment of aPerson
-
- session2.getTransaction().commit();
-}]]></programlisting>
-
<para>
- The call to <literal>update</literal> makes a detached object
persistent again by binding it to a new unit of work, so any modifications you made to it
while detached
- can be saved to the database. This includes any modifications
(additions/deletions) you
- made to a collection of that entity object.
+ The call to <literal>update</literal> makes a detached
object
+ persistent again by binding it to a new unit of work, so any
+ modifications you made to it while detached can be saved to
+ the database. This includes any modifications
+ (additions/deletions) you made to a collection of that entity
+ object.
</para>
<para>
@@ -1090,29 +1109,35 @@
returns it (you might have to modify some of the previous methods to
return that identifier):
</para>
- <programlisting><![CDATA[else if
(args[0].equals("addpersontoevent")) {
- Long eventId = mgr.createAndStoreEvent("My Event", new Date());
- Long personId = mgr.createAndStorePerson("Foo", "Bar");
- mgr.addPersonToEvent(personId, eventId);
- System.out.println("Added person " + personId + " to event " +
eventId);
-}]]></programlisting>
+ <programlisting><![CDATA[ else if
(args[0].equals("addpersontoevent")) {
+ Long eventId = mgr.createAndStoreEvent("My Event", new Date());
+ Long personId = mgr.createAndStorePerson("Foo", "Bar");
+ mgr.addPersonToEvent(personId, eventId);
+ System.out.println("Added person " + personId + " to event
" + eventId);
+ }]]></programlisting>
<para>
- This is 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 do not have their own
identity, nor are they
- shared between entities. Two persons do not reference the same
<literal>firstname</literal>
- object, even if they have the same first name. Value types cannot only be
found in
- the JDK , but
- you can also write dependent classes yourself
(<literal>Address</literal> or <literal>MonetaryAmount</literal>,
- for example). In fact, in a Hibernate application all JDK classes are
considered value types.
+ This is 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
+ <classname>java.lang.String</classname>. We call these
classes
+ <emphasis>value types</emphasis>, and their instances
+ <emphasis>depend</emphasis> on a particular entity.
Instances of
+ these types do not have their own identity, nor are they shared
+ between entities. Two persons do not reference the same
+ <literal>firstname</literal> object, even if they have the
same
+ first name. Value types cannot only be found in the JDK , but
+ you can also write dependent classes yourself
+ such as an <literal>Address</literal> or
+ <literal>MonetaryAmount</literal> class. In fact, in a
Hibernate
+ application all JDK classes are considered value types.
</para>
<para>
- You can also design a collection of value types. This is conceptually
different from a
- collection of references to other entities, but looks almost the same in
Java.
+ You can also design a collection of value types. This is
+ conceptually different from a collection of references to other
+ entities, but looks almost the same in Java.
</para>
</sect2>
@@ -1121,37 +1146,44 @@
<title>Collection of values</title>
<para>
- You can add a collection of value typed objects to the
<literal>Person</literal> entity. If you want to
- store email addresses, the type you use is
<literal>String</literal>, and the collection is
- again a <literal>Set</literal>:
+ Let's add a collection of email addresses to the
+ <literal>Person</literal> entity. This will be represented
as a
+ <interfacename>java.util.Set</interfacename> of
+ <classname>java.lang.String</classname> instances:
</para>
- <programlisting><![CDATA[private Set emailAddresses = new
HashSet();
+ <programlisting><![CDATA[ private Set emailAddresses = new
HashSet();
-public Set getEmailAddresses() {
- return emailAddresses;
-}
+ public Set getEmailAddresses() {
+ return emailAddresses;
+ }
-public void setEmailAddresses(Set emailAddresses) {
- this.emailAddresses = emailAddresses;
-}]]></programlisting>
+ public void setEmailAddresses(Set emailAddresses) {
+ this.emailAddresses = emailAddresses;
+ }]]></programlisting>
<para>
The mapping of this <literal>Set</literal> is as follows:
</para>
- <programlisting><![CDATA[<set name="emailAddresses"
table="PERSON_EMAIL_ADDR">
- <key column="PERSON_ID"/>
- <element type="string" column="EMAIL_ADDR"/>
-</set>]]></programlisting>
+ <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 is
a Hibernate mapping type/converter.
- 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.
+ The difference compared with the earlier mapping is the use of
+ the <literal>element</literal> part which tells Hibernate
that the
+ collection does not contain references to another entity, but is
+ rather a collection whose elements are values types, here specifically
+ of type <literal>string</literal>. The lowercase name tells
you
+ it is a Hibernate mapping type/converter. 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 email address values will actually
+ be stored.
</para>
<para>
@@ -1183,24 +1215,21 @@
linking persons and events. It is the same code in Java:
</para>
- <programlisting><![CDATA[private void addEmailToPerson(Long
personId, String emailAddress) {
+ <programlisting><![CDATA[ private void addEmailToPerson(Long
personId, String emailAddress) {
+ Session session = HibernateUtil.getSessionFactory().getCurrentSession();
+ session.beginTransaction();
- Session session = HibernateUtil.getSessionFactory().getCurrentSession();
- session.beginTransaction();
+ Person aPerson = (Person) session.load(Person.class, personId);
+ // adding to the emailAddress collection might trigger a lazy load of the
collection
+ aPerson.getEmailAddresses().add(emailAddress);
- Person aPerson = (Person) session.load(Person.class, personId);
+ session.getTransaction().commit();
+ }]]></programlisting>
- // The getEmailAddresses() might trigger a lazy load of the collection
- aPerson.getEmailAddresses().add(emailAddress);
-
- session.getTransaction().commit();
-}]]></programlisting>
-
<para>
- This time we did not use a <emphasis>fetch</emphasis> query
to initialize the collection.
- Hence, the call to its getter method will trigger an additional select to
initialize
- it, so we can add an element to it. Monitor the SQL log and try to
optimize this with
- an eager fetch.
+ This time we did not use a <emphasis>fetch</emphasis> query
to
+ initialize the collection. Monitor the SQL log and try to
+ optimize this with an eager fetch.
</para>
</sect2>
@@ -1209,35 +1238,44 @@
<title>Bi-directional associations</title>
<para>
- Next you will map a bi-directional association. You will make the
association between
- person and event work from both sides in Java. The database schema does
not
- change, so you will still have many-to-many multiplicity. A relational
database is more flexible
- than a network programming language, so it does not need a navigation
- direction; data can be viewed and retrieved in any possible way.
+ Next you will map a bi-directional association. You will make
+ the association between person and event work from both sides
+ in Java. The database schema does not change, so you will still
+ have many-to-many multiplicity.
</para>
+ <note>
+ <para>
+ A relational database is more flexible than a network
+ programming language, in that it does not need a navigation
+ direction; data can be viewed and retrieved in any possible
+ way.
+ </para>
+ </note>
+
<para>
- First, add a collection of participants to the
<literal>Event</literal> Event class:
+ First, add a collection of participants to the
+ <literal>Event</literal> class:
</para>
- <programlisting><![CDATA[private Set participants = new HashSet();
+ <programlisting><![CDATA[ private Set participants = new
HashSet();
-public Set getParticipants() {
- return participants;
-}
+ public Set getParticipants() {
+ return participants;
+ }
-public void setParticipants(Set participants) {
- this.participants = participants;
-}]]></programlisting>
+ public void setParticipants(Set participants) {
+ this.participants = participants;
+ }]]></programlisting>
<para>
Now map this side of the association 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="events.Person"/>
-</set>]]></programlisting>
+ <programlisting><![CDATA[ <set
name="participants" table="PERSON_EVENT" inverse="true">
+ <key column="EVENT_ID"/>
+ <many-to-many column="PERSON_ID"
class="events.Person"/>
+ </set>]]></programlisting>
<para>
These are normal <literal>set</literal> mappings in both
mapping documents.
@@ -1273,23 +1311,23 @@
correctly set both sides (for example, in
<literal>Person</literal>):
</para>
- <programlisting><![CDATA[protected Set getEvents() {
- return events;
-}
+ <programlisting><![CDATA[ protected Set getEvents() {
+ return events;
+ }
-protected void setEvents(Set events) {
- this.events = events;
-}
+ protected void setEvents(Set events) {
+ this.events = events;
+ }
-public void addToEvent(Event event) {
- this.getEvents().add(event);
- event.getParticipants().add(this);
-}
+ 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>
+ public void removeFromEvent(Event event) {
+ this.getEvents().remove(event);
+ event.getParticipants().remove(this);
+ }]]></programlisting>
<para>
The get and set methods for the collection are now protected. This allows
classes in the
@@ -1309,7 +1347,6 @@
and in many-to-many association you can select either side.
</para>
- <para>In the next section we will turn this into a small web
application.</para>
</sect2>
</sect1>
@@ -1328,50 +1365,51 @@
<title>Writing the basic servlet</title>
<para>
- Create a new class in your source directory in the
<literal>events</literal>
- package:
+ First we need create our basic processing servlet. Since our
+ servlet only handles HTTP <literal>GET</literal> requests,
we
+ will only implement the <literal>doGet()</literal> method:
</para>
- <programlisting><![CDATA[package events;
+ <programlisting><![CDATA[package org.hibernate.tutorial.web;
// Imports
public class EventManagerServlet extends HttpServlet {
- // Servlet code
-}]]></programlisting>
+ protected void doGet(
+ HttpServletRequest request,
+ HttpServletResponse response) throws ServletException, IOException {
- <para>
- The servlet handles HTTP <literal>GET</literal> requests
only. It is for this reason that the method
- we implement is <literal>doGet()</literal>:
- </para>
+ SimpleDateFormat dateFormatter = new SimpleDateFormat( "dd.MM.yyyy" );
- <programlisting><![CDATA[protected void doGet(HttpServletRequest
request,
- HttpServletResponse response)
- throws ServletException, IOException {
+ try {
+ // Begin unit of work
+ HibernateUtil.getSessionFactory().getCurrentSession().beginTransaction();
- SimpleDateFormat dateFormatter = new SimpleDateFormat("dd.MM.yyyy");
+ // Process request and render page...
- try {
- // Begin unit of work
- HibernateUtil.getSessionFactory()
- .getCurrentSession().beginTransaction();
-
- // Process request and render page...
-
- // End unit of work
- HibernateUtil.getSessionFactory()
- .getCurrentSession().getTransaction().commit();
-
- } catch (Exception ex) {
- HibernateUtil.getSessionFactory()
- .getCurrentSession().getTransaction().rollback();
- throw new ServletException(ex);
+ // End unit of work
+
HibernateUtil.getSessionFactory().getCurrentSession().getTransaction().commit();
+ }
+ catch (Exception ex) {
+
HibernateUtil.getSessionFactory().getCurrentSession().getTransaction().rollback();
+ if ( ServletException.class.isInstance( ex ) ) {
+ throw ( ServletException ) ex;
+ }
+ else {
+ throw new ServletException( ex );
+ }
+ }
}
}]]></programlisting>
<para>
+ Save this servlet as
+
<filename>src/main/java/org/hibernate/tutorial/web/EventManagerServlet.java</filename>
+ </para>
+
+ <para>
The pattern applied here is called
<emphasis>session-per-request</emphasis>.
When a request hits the servlet, a new Hibernate
<literal>Session</literal> is
opened through the first call to
<literal>getCurrentSession()</literal> on the
@@ -1412,32 +1450,33 @@
Now you can implement the processing of the request and the rendering of
the page.
</para>
-<programlisting><![CDATA[// Write HTML header
-PrintWriter out = response.getWriter();
-out.println("<html><head><title>Event
Manager</title></head><body>");
+<programlisting><![CDATA[ // Write HTML header
+ PrintWriter out = response.getWriter();
+ out.println("<html><head><title>Event
Manager</title></head><body>");
-// Handle actions
-if ( "store".equals(request.getParameter("action")) ) {
+ // Handle actions
+ if ( "store".equals(request.getParameter("action")) ) {
- String eventTitle = request.getParameter("eventTitle");
- String eventDate = request.getParameter("eventDate");
+ String eventTitle = request.getParameter("eventTitle");
+ String eventDate = request.getParameter("eventDate");
- if ( "".equals(eventTitle) || "".equals(eventDate) ) {
- out.println("<b><i>Please enter event title and
date.</i></b>");
- } else {
- createAndStoreEvent(eventTitle, dateFormatter.parse(eventDate));
- out.println("<b><i>Added event.</i></b>");
- }
-}
+ if ( "".equals(eventTitle) || "".equals(eventDate) ) {
+ out.println("<b><i>Please enter event title and
date.</i></b>");
+ }
+ else {
+ createAndStoreEvent(eventTitle, dateFormatter.parse(eventDate));
+ out.println("<b><i>Added
event.</i></b>");
+ }
+ }
-// Print page
-printEventForm(out);
-listEvents(out, dateFormatter);
+ // Print page
+ printEventForm(out);
+ listEvents(out, dateFormatter);
-// Write HTML footer
-out.println("</body></html>");
-out.flush();
-out.close();]]></programlisting>
+ // Write HTML footer
+ out.println("</body></html>");
+ out.flush();
+ out.close();]]></programlisting>
<para>
This coding style, with a mix of Java and HTML, would not scale
@@ -1448,14 +1487,14 @@
trivial and only outputs HTML:
</para>
- <programlisting><![CDATA[private void printEventForm(PrintWriter
out) {
- out.println("<h2>Add new event:</h2>");
- out.println("<form>");
- out.println("Title: <input name='eventTitle'
length='50'/><br/>");
- out.println("Date (e.g. 24.12.2009): <input name='eventDate'
length='10'/><br/>");
- out.println("<input type='submit' name='action'
value='store'/>");
- out.println("</form>");
-}]]></programlisting>
+ <programlisting><![CDATA[ private void printEventForm(PrintWriter
out) {
+ out.println("<h2>Add new event:</h2>");
+ out.println("<form>");
+ out.println("Title: <input name='eventTitle'
length='50'/><br/>");
+ out.println("Date (e.g. 24.12.2009): <input name='eventDate'
length='10'/><br/>");
+ out.println("<input type='submit' name='action'
value='store'/>");
+ out.println("</form>");
+ }]]></programlisting>
<para>
The <literal>listEvents()</literal> method uses the
Hibernate
@@ -1463,27 +1502,28 @@
a query:
</para>
- <programlisting><![CDATA[private void listEvents(PrintWriter out,
SimpleDateFormat dateFormatter) {
+ <programlisting><![CDATA[ private void listEvents(PrintWriter
out, SimpleDateFormat dateFormatter) {
- List result = HibernateUtil.getSessionFactory()
- .getCurrentSession().createCriteria(Event.class).list();
- if (result.size() > 0) {
- out.println("<h2>Events in database:</h2>");
- out.println("<table border='1'>");
- out.println("<tr>");
- out.println("<th>Event title</th>");
- out.println("<th>Event date</th>");
- out.println("</tr>");
- for (Iterator it = result.iterator(); it.hasNext();) {
- Event event = (Event) it.next();
+ List result = HibernateUtil.getSessionFactory()
+ .getCurrentSession().createCriteria(Event.class).list();
+ if (result.size() > 0) {
+ out.println("<h2>Events in database:</h2>");
+ out.println("<table border='1'>");
out.println("<tr>");
- out.println("<td>" + event.getTitle() +
"</td>");
- out.println("<td>" + dateFormatter.format(event.getDate()) +
"</td>");
+ out.println("<th>Event title</th>");
+ out.println("<th>Event date</th>");
out.println("</tr>");
+ Iterator it = result.iterator();
+ while (it.hasNext()) {
+ Event event = (Event) it.next();
+ out.println("<tr>");
+ out.println("<td>" + event.getTitle() +
"</td>");
+ out.println("<td>" +
dateFormatter.format(event.getDate()) + "</td>");
+ out.println("</tr>");
+ }
+ out.println("</table>");
}
- out.println("</table>");
- }
-}]]></programlisting>
+ }]]></programlisting>
<para>
Finally, the <literal>store</literal> action is dispatched to
the
@@ -1491,14 +1531,14 @@
the <literal>Session</literal> of the current thread:
</para>
- <programlisting><![CDATA[protected void createAndStoreEvent(String
title, Date theDate) {
- Event theEvent = new Event();
- theEvent.setTitle(title);
- theEvent.setDate(theDate);
+ <programlisting><![CDATA[ protected void
createAndStoreEvent(String title, Date theDate) {
+ Event theEvent = new Event();
+ theEvent.setTitle(title);
+ theEvent.setDate(theDate);
- HibernateUtil.getSessionFactory()
- .getCurrentSession().save(theEvent);
-}]]></programlisting>
+ HibernateUtil.getSessionFactory()
+ .getCurrentSession().save(theEvent);
+ }]]></programlisting>
<para>
The servlet is now complete. A request to the servlet will be processed
@@ -1517,26 +1557,11 @@
<title>Deploying and testing</title>
<para>
- To deploy this application you have to create a web archive, or what is
known as a WAR. Add the
- following Ant target to your <literal>build.xml</literal>:
+ To deploy this application for testing we must create a
+ Web ARchive (WAR). First we must define the WAR descriptor
+ as <filename>src/main/webapp/WEB-INF/web.xml</filename>
</para>
-<programlisting><![CDATA[<target name="war"
depends="compile">
- <war destfile="hibernate-tutorial.war" webxml="web.xml">
- <lib dir="${librarydir}">
- <exclude name="jsdk*.jar"/>
- </lib>
-
- <classes dir="${targetdir}"/>
- </war>
-</target>]]></programlisting>
-
- <para>
- This target creates a file called
<literal>hibernate-tutorial.war</literal>
- in your project directory. It packages all libraries and the
<literal>web.xml</literal>
- descriptor, which is expected in the base directory of your project:
- </para>
-
<programlisting><![CDATA[<?xml version="1.0"
encoding="UTF-8"?>
<web-app version="2.4"
xmlns="http://java.sun.com/xml/ns/j2ee"
@@ -1545,7 +1570,7 @@
<servlet>
<servlet-name>Event Manager</servlet-name>
- <servlet-class>events.EventManagerServlet</servlet-class>
+
<servlet-class>org.hibernate.tutorial.web.EventManagerServlet</servlet-class>
</servlet>
<servlet-mapping>
@@ -1555,20 +1580,19 @@
</web-app>]]></programlisting>
<para>
- Before you compile and deploy the web application, an additional library
- is required: <literal>jsdk.jar</literal>. This is the Java
servlet development kit.
- If you do not have this library already, get it from the Sun website and
copy it to
- your library directory. However, it will be only used for compilation and
excluded
- from the WAR package.
+ To build and deploy call <literal>mvn package</literal> in
your
+ project directory and copy the
<filename>hibernate-tutorial.war</filename>
+ file into your Tomcat <filename>webapps</filename>
directory.
</para>
- <para>
- To build and deploy call <literal>ant war</literal> in your
project directory
- and copy the <literal>hibernate-tutorial.war</literal> file
into your Tomcat
- <literal>webapp</literal> directory. If you do not have
Tomcat installed, download
- it and follow the installation instructions. You do not have to change
any Tomcat
- configuration to deploy this application though.
- </para>
+ <note>
+ <para>
+ If you do not have Tomcat installed, download it from
+ <ulink
url="http://tomcat.apache.org/"/> and follow
the
+ installation instructions. Our application requires
+ no changes to the standard Tomcat configuration.
+ </para>
+ </note>
<para>
Once deployed and Tomcat is running, access the application at
@@ -1587,21 +1611,10 @@
<para>
This tutorial covered the basics of writing a simple standalone Hibernate
application
- and a small web application.
+ and a small web application. More tutorials are available from the
Hibernate
+ <ulink url="http://hibernate.org">website</ulink>.
</para>
- <para>
- If you already feel confident with Hibernate, continue browsing through the
reference
- documentation table of contents for topics you find interesting. The most
popular 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>
- More tutorials are available from the Hibernate website.
- </para>
-
</sect1>
</chapter>