Added webapp to tutorial

git-svn-id: https://svn.jboss.org/repos/hibernate/trunk/Hibernate3/doc@8618 1b8cb986-b30d-0410-93ca-fae66ebed9b2
This commit is contained in:
Christian Bauer 2005-11-18 22:37:23 +00:00
parent 57c3f1f4ea
commit 601401ef7d
8 changed files with 412 additions and 638 deletions

View File

@ -39,10 +39,12 @@
description="Compile documentation for all languages and all formats."> description="Compile documentation for all languages and all formats.">
<!-- TRANSLATOR: Duplicate this line for your language --> <!-- TRANSLATOR: Duplicate this line for your language -->
<!--
<antcall target="lang.all"><param name="lang" value="en"/></antcall> <antcall target="lang.all"><param name="lang" value="en"/></antcall>
<antcall target="lang.all"><param name="lang" value="zh-cn"/></antcall> <antcall target="lang.all"><param name="lang" value="zh-cn"/></antcall>
<antcall target="lang.all"><param name="lang" value="es"/></antcall> <antcall target="lang.all"><param name="lang" value="es"/></antcall>
<antcall target="lang.all"><param name="lang" value="ko"/></antcall> <antcall target="lang.all"><param name="lang" value="ko"/></antcall>
-->
</target> </target>

View File

@ -397,7 +397,7 @@ Cat fritz = (Cat) iter.next();]]></programlisting>
</sect2> </sect2>
<sect2 id="performance-fetching-initialization"> <sect2 id="performance-fetching-initialization" revision="1">
<title>Initializing collections and proxies</title> <title>Initializing collections and proxies</title>
<para> <para>
@ -441,11 +441,8 @@ Cat fritz = (Cat) iter.next();]]></programlisting>
correctness of the exception handling of your application infrastructure. correctness of the exception handling of your application infrastructure.
It is vitally important that the <literal>Session</literal> is closed and the It is vitally important that the <literal>Session</literal> is closed and the
transaction ended before returning to the user, even when an exception occurs transaction ended before returning to the user, even when an exception occurs
during rendering of the view. The servlet filter has to be able to access the during rendering of the view. See the Hibernate Wiki for examples of this
<literal>Session</literal> for this approach. We recommend that a "Open Session in View" pattern.
<literal>ThreadLocal</literal> variable be used to hold the current
<literal>Session</literal> (see chapter 1,
<xref linkend="quickstart-playingwithcats"/>, for an example implementation).
</para> </para>
</listitem> </listitem>
<listitem> <listitem>

View File

@ -1,623 +0,0 @@
<chapter id="quickstart">
<title>Quickstart with Tomcat</title>
<sect1 id="quickstart-intro" revision="2">
<title>Getting started with Hibernate</title>
<para>
This tutorial explains a setup of Hibernate 3.0 with the Apache Tomcat
servlet container (we used version 4.1, the differences to 5.0 should be
minimal) for a web-based application. Hibernate works
well in a managed environment with all major J2EE application servers, or
even in standalone Java applications. The database system used in this
tutorial is PostgreSQL 7.4, support for other database is only a matter
of changing the Hibernate SQL dialect configuration and connection
properties.
</para>
<para>
First, we have to copy all required libraries to the Tomcat installation.
We use a separate web context (<literal>webapps/quickstart</literal>) for
this tutorial, so we've to consider both the global library search path
(<literal>TOMCAT/common/lib</literal>) and the classloader at the context level in
<literal>webapps/quickstart/WEB-INF/lib</literal> (for JAR files) and
<literal>webapps/quickstart/WEB-INF/classes</literal>. We refer to both classloader
levels as the global classpath and the context classpath.
</para>
<para>
Now, copy the libraries to the two classpaths:
</para>
<orderedlist>
<listitem>
<para>
Copy the JDBC driver for the database to the global classpath. This is
required for the DBCP connection pool software which comes bundled with Tomcat.
Hibernate uses JDBC connections to execute SQL on the database, so you
either have to provide pooled JDBC connections or configure Hibernate to
use one of the directly supported pools (C3P0, Proxool). For this tutorial,
copy the <literal>pg74jdbc3.jar</literal> library (for PostgreSQL 7.4 and JDK 1.4)
to the global classloaders path. If you'd like to use a different database, simply
copy its appropriate JDBC driver.
</para>
</listitem>
<listitem>
<para>
Never copy anything else into the global classloader path in Tomcat, or you
will get problems with various tools, including Log4j, commons-logging and
others. Always use the context classpath for each web application, that is,
copy libraries to <literal>WEB-INF/lib</literal> and your own classes and
configuration/property files to <literal>WEB-INF/classes</literal>. Both
directories are in the context level classpath by default.
</para>
</listitem>
<listitem>
<para>
Hibernate is packaged as a JAR library. The <literal>hibernate3.jar</literal>
file should be copied in the context classpath together with other classes of
the application. Hibernate requires some 3rd party libraries at runtime, these
come bundled with the Hibernate distribution in the <literal>lib/</literal>
directory; see <xref linkend="3rdpartylibs"/>. Copy the required 3rd party
libraries to the context classpath.
</para>
</listitem>
</orderedlist>
<table frame="topbot" id="3rdpartylibs">
<title>
Hibernate 3rd party libraries
</title>
<tgroup cols="2" rowsep="1" colsep="1">
<colspec colname="c1" colwidth="1*"/>
<colspec colname="c2" colwidth="2*"/>
<thead>
<row>
<entry align="center">
Library
</entry>
<entry align="center">
Description
</entry>
</row>
</thead>
<tbody>
<row>
<entry>
antlr (required)
</entry>
<entry>
Hibernate uses ANTLR to produce query parsers, this library is
also needed at runtime.
</entry>
</row>
<row>
<entry>
dom4j (required)
</entry>
<entry>
Hibernate uses dom4j to parse XML configuration and XML mapping
metadata files.
</entry>
</row>
<row>
<entry>
CGLIB, asm (required)
</entry>
<entry>
Hibernate uses the code generation library to enhance classes
at runtime (in combination with Java reflection).
</entry>
</row>
<row>
<entry>
Commons Collections, Commons Logging (required)
</entry>
<entry>
Hibernate uses various utility libraries from the Apache Jakarta
Commons project.
</entry>
</row>
<row>
<entry>
EHCache (required)
</entry>
<entry>
Hibernate can use various cache providers for the second-level
cache. EHCache is the default cache provider if not changed in
the configuration.
</entry>
</row>
<row>
<entry>
Log4j (optional)
</entry>
<entry>
Hibernate uses the Commons Logging API, which in turn can use
Log4j as the underlying logging mechanism. If the Log4j library is
available in the context library directory, Commons Logging will use
Log4j and the <literal>log4j.properties</literal> configuration in the
context classpath. An example properties file for Log4j is bundled
with the Hibernate distribution. So, copy log4j.jar and the configuration
file (from <literal>src/</literal>) to your context classpath if
you want to see whats going on behind the scenes.
</entry>
</row>
<row>
<entry>
Required or not?
</entry>
<entry>
Have a look at the file <literal>lib/README.txt</literal> in the
Hibernate distribution. This is an up-to-date list of 3rd party
libraries distributed with Hibernate. You will find all required
and optional libraries listed there (note that "buildtime required"
here means for Hibernate's build, not your application).
</entry>
</row>
</tbody>
</tgroup>
</table>
<para>
We now set up the database connection pooling and sharing in both Tomcat and
Hibernate. This means Tomcat will provide pooled JDBC connections (using its
builtin DBCP pooling feature), Hibernate requests theses connections through
JNDI. Alternatively, you can let Hibernate manage the connection pool. Tomcat
binds its connection pool to JNDI; we add a resource declaration
to Tomcats main configuration file, <literal>TOMCAT/conf/server.xml</literal>:
</para>
<programlisting><![CDATA[<Context path="/quickstart" docBase="quickstart">
<Resource name="jdbc/quickstart" scope="Shareable" type="javax.sql.DataSource"/>
<ResourceParams name="jdbc/quickstart">
<parameter>
<name>factory</name>
<value>org.apache.commons.dbcp.BasicDataSourceFactory</value>
</parameter>
<!-- DBCP database connection settings -->
<parameter>
<name>url</name>
<value>jdbc:postgresql://localhost/quickstart</value>
</parameter>
<parameter>
<name>driverClassName</name><value>org.postgresql.Driver</value>
</parameter>
<parameter>
<name>username</name>
<value>quickstart</value>
</parameter>
<parameter>
<name>password</name>
<value>secret</value>
</parameter>
<!-- DBCP connection pooling options -->
<parameter>
<name>maxWait</name>
<value>3000</value>
</parameter>
<parameter>
<name>maxIdle</name>
<value>100</value>
</parameter>
<parameter>
<name>maxActive</name>
<value>10</value>
</parameter>
</ResourceParams>
</Context>]]></programlisting>
<para>
The context we configure in this example is named <literal>quickstart</literal>,
its base is the <literal>TOMCAT/webapp/quickstart</literal> directory. To access
any servlets, call the path <literal>http://localhost:8080/quickstart</literal>
in your browser (of course, adding the name of the servlet as mapped in your
<literal>web.xml</literal>). You may also go ahead and create a simple servlet
now that has an empty <literal>process()</literal> method.
</para>
<para>
Tomcat provides connections now through JNDI at
<literal>java:comp/env/jdbc/quickstart</literal>. If you have trouble getting the
connection pool running, refer to the Tomcat documentation. If you get JDBC driver
exception messages, try to setup JDBC connection pool without Hibernate first.
Tomcat &amp; JDBC tutorials are available on the Web.
</para>
<para>
Your next step is to configure Hibernate. Hibernate has to know how it should obtain
JDBC connections. We use Hibernate's XML-based configuration. The other approach, using
a properties file, is almost equivalent but misses a few features the XML syntax allows.
The XML configuration file is placed in the context classpath (<literal>WEB-INF/classes</literal>),
as <literal>hibernate.cfg.xml</literal>:
</para>
<programlisting><![CDATA[<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="connection.datasource">java:comp/env/jdbc/quickstart</property>
<property name="show_sql">false</property>
<property name="dialect">org.hibernate.dialect.PostgreSQLDialect</property>
<!-- Mapping files -->
<mapping resource="Cat.hbm.xml"/>
</session-factory>
</hibernate-configuration>]]></programlisting>
<para>
We turn logging of SQL commands off and tell Hibernate what database SQL
dialect is used and where to get the JDBC connections (by declaring the JNDI
address of the Tomcat bound pool). The dialect is a required setting,
databases differ in their interpretation of the SQL "standard". Hibernate will take
care of the differences and comes bundled with dialects for all major
commercial and open source databases.
</para>
<para>
A <literal>SessionFactory</literal> is Hibernate's concept of a single
datastore, multiple databases can be used by creating multiple XML
configuration files and creating multiple <literal>Configuration</literal>
and <literal>SessionFactory</literal> objects in your application.
</para>
<para>
The last element of the <literal>hibernate.cfg.xml</literal> declares
<literal>Cat.hbm.xml</literal> as the name of a Hibernate XML mapping
file for the persistent class <literal>Cat</literal>. This file contains
the metadata for the mapping of the POJO class <literal>Cat</literal> to
a datbase table (or tables). We'll come back to that file soon. Let's write
the POJO class first and then declare the mapping metadata for it.
</para>
</sect1>
<sect1 id="quickstart-persistentclass" revision="1">
<title>First persistent class</title>
<para>
Hibernate works best with the Plain Old Java Objects (POJOs, sometimes
called Plain Ordinary Java Objects) programming model for persistent classes.
A POJO is much like a JavaBean, with properties of the class accessible via getter
and setter methods, shielding the internal representation from the publicly
visible interface (Hibernate can also access fields directly, if needed):
</para>
<programlisting><![CDATA[package org.hibernate.examples.quickstart;
public class Cat {
private String id;
private String name;
private char sex;
private float weight;
public Cat() {
}
public String getId() {
return id;
}
private void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public char getSex() {
return sex;
}
public void setSex(char sex) {
this.sex = sex;
}
public float getWeight() {
return weight;
}
public void setWeight(float weight) {
this.weight = weight;
}
}]]></programlisting>
<para>
Hibernate is not restricted in its usage of property types, all Java JDK
types and primitives (like <literal>String</literal>, <literal>char</literal>
and <literal>Date</literal>) can be mapped, including classes from the Java
collections framework. You can map them as values, collections of values, or
associations to other entities. The <literal>id</literal> is a special property
that represents the database identifer (primary key) of that class, it is
highly recommended for entities like a <literal>Cat</literal>. Hibernate can
use identifiers only internally, but we would lose some of the flexibility in our
application architecture.
</para>
<para>
No special interface has to be implemented for persistent classes nor do you have
to subclass from a special root persistent class. Hibernate also doesn't require
any build time processing, such as byte-code manipulation, it relies solely on
Java reflection and runtime class enhancement (through CGLIB). So, without any
dependency of the POJO class on Hibernate, we can map it to a database table.
</para>
</sect1>
<sect1 id="quickstart-mapping" revision="2">
<title>Mapping the cat</title>
<para>
The <literal>Cat.hbm.xml</literal> mapping file contains the metadata
required for the object/relational mapping. The metadata includes declaration
of persistent classes and the mapping of properties (to columns and
foreign key relationships to other entities) to database tables.
</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>
<class name="org.hibernate.examples.quickstart.Cat" table="CAT">
<!-- A 32 hex character is our surrogate key. It's automatically
generated by Hibernate with the UUID pattern. -->
<id name="id" type="string" unsaved-value="null" >
<column name="CAT_ID" sql-type="char(32)" not-null="true"/>
<generator class="uuid"/>
</id>
<!-- A cat has to have a name, but it shouldn' be too long. -->
<property name="name">
<column name="NAME" length="16" not-null="true"/>
</property>
<property name="sex"/>
<property name="weight"/>
</class>
</hibernate-mapping>]]></programlisting>
<para>
Every persistent class should have an identifer attribute (actually, only
classes representing entities, not dependent value-typed classes, which
are mapped as components of an entity). This property is used to distinguish
persistent objects: Two cats are equal if
<literal>catA.getId().equals(catB.getId())</literal> is true, this concept is
called <emphasis>database identity</emphasis>. Hibernate comes bundled with
various identifer generators for different scenarios (including native generators
for database sequences, hi/lo identifier tables, and application assigned
identifiers). We use the UUID generator (only recommended for testing, as integer
surrogate keys generated by the database should be prefered) and also specify the
column <literal>CAT_ID</literal> of the table <literal>CAT</literal> for the
Hibernate generated identifier value (as a primary key of the table).
</para>
<para>
All other properties of <literal>Cat</literal> are mapped to the same table. In
the case of the <literal>name</literal> property, we mapped it with an explicit
database column declaration. This is especially useful when the database
schema is automatically generated (as SQL DDL statements) from the mapping
declaration with Hibernate's <emphasis>SchemaExport</emphasis> tool. All other
properties are mapped using Hibernate's default settings, which is what you
need most of the time. The table <literal>CAT</literal> in the database looks
like this:
</para>
<programlisting><![CDATA[ Column | Type | Modifiers
--------+-----------------------+-----------
cat_id | character(32) | not null
name | character varying(16) | not null
sex | character(1) |
weight | real |
Indexes: cat_pkey primary key btree (cat_id)]]></programlisting>
<para>
You should now create this table in your database manually, and later read
<xref linkend="toolsetguide"/> if you want to automate this step with the
<literal>hbm2ddl</literal> tool. This tool can create a full SQL DDL, including
table definition, custom column type constraints, unique constraints and indexes.
</para>
</sect1>
<sect1 id="quickstart-playingwithcats" revision="3">
<title>Playing with cats</title>
<para>
We're now ready to start Hibernate's <literal>Session</literal>. It is the
<emphasis>persistence manager</emphasis>, we use it to store and retrieve
<literal>Cat</literal>s to and from the database. But first, we've to get a
<literal>Session</literal> (Hibernate's unit-of-work) from the
<literal>SessionFactory</literal>:
</para>
<programlisting><![CDATA[SessionFactory sessionFactory =
new Configuration().configure().buildSessionFactory();]]></programlisting>
<para>
The call to <literal>configure()</literal> loads the <literal>hibernate.cfg.xml</literal>
configuration file and initializes the <literal>Configuration</literal> instance.
You can set other properties (and even change the mapping metadata) by
accessing the <literal>Configuration</literal> <emphasis>before</emphasis>
you build the <literal>SessionFactory</literal> (it is immutable). Where
do we create the <literal>SessionFactory</literal> and how can we access
it in our application?
</para>
<para>
A <literal>SessionFactory</literal> is usually only build once,
e.g. at startup with a <emphasis>load-on-startup</emphasis> servlet.
This also means you should not keep it in an instance variable in your
servlets, but in some other location. Furthermore, we need some kind of
<emphasis>Singleton</emphasis>, so we can access the
<literal>SessionFactory</literal> easily in application code. The approach
shown next solves both problems: startup configuration and easy access to a
<literal>SessionFactory</literal>.
</para>
<para>
We implement a <literal>HibernateUtil</literal> helper class:
</para>
<programlisting><![CDATA[import org.hibernate.*;
import org.hibernate.cfg.*;
public class HibernateUtil {
private static Log log = LogFactory.getLog(HibernateUtil.class);
private static final SessionFactory sessionFactory;
static {
try {
// Create the SessionFactory
sessionFactory = new Configuration().configure().buildSessionFactory();
} catch (Throwable ex) {
// Make sure you log the exception, as it might be swallowed
log.error("Initial SessionFactory creation failed.", ex);
throw new ExceptionInInitializerError(ex);
}
}
public static final ThreadLocal session = new ThreadLocal();
public static Session getCurrentSession() {
Session s = (Session) session.get();
// Open a new Session, if this Thread has none yet
if (s == null) {
s = sessionFactory.openSession();
session.set(s);
}
return s;
}
public static void closeSession() {
Session s = (Session) session.get();
if (s != null)
s.close();
session.set(null);
}
}]]></programlisting>
<para>
This class does not only take care of the <literal>SessionFactory</literal>
with its static initializer, but also has a <literal>ThreadLocal</literal>
variable which holds the <literal>Session</literal> for the current thread.
Make sure you understand the Java concept of a thread-local variable before you
try to use this helper. A more complex and powerful <literal>HibernateUtil</literal>
class can be found in <literal>CaveatEmptor</literal>, http://caveatemptor.hibernate.org/
</para>
<para>
A <literal>SessionFactory</literal> is threadsafe, many threads can access
it concurrently and request <literal>Session</literal>s. A <literal>Session</literal>
is a non-threadsafe object that represents a single unit-of-work with the database.
<literal>Session</literal>s are opened from a <literal>SessionFactory</literal> and
are closed when all work is completed. An example in your servlet's
<literal>process()</literal> method might look like this (sans exception handling):
</para>
<programlisting><![CDATA[Session session = HibernateUtil.getCurrentSession();
Transaction tx = session.beginTransaction();
Cat princess = new Cat();
princess.setName("Princess");
princess.setSex('F');
princess.setWeight(7.4f);
session.save(princess);
tx.commit();
HibernateUtil.closeSession();]]></programlisting>
<para>
In a <literal>Session</literal>, every database operation occurs inside a
transaction that isolates the database operations (even read-only operations).
We use Hibernates <literal>Transaction</literal> API to abstract from the underlying
transaction strategy (in our case, JDBC transactions). This allows our code
to be deployed with container-managed transactions (using JTA) without any changes.
</para>
<para>
Note that you may call <literal>HibernateUtil.getCurrentSession();</literal>
as many times as you like, you will always get the current <literal>Session</literal>
of this thread. You have to make sure the <literal>Session</literal> is closed
after your unit-of-work completes, either in your servlet code or in a servlet filter
before the HTTP response is send. The nice side effect of the second option is easy
lazy initialization: the <literal>Session</literal> is still open when the view is
rendered, so Hibernate can load unitialized objects while you navigate the current
object graph.
</para>
<para>
Hibernate has various methods that can be used to retrieve objects from the
database. The most flexible way is using the Hibernate Query Language (HQL),
which is an easy to learn and powerful object-oriented extension to SQL:
</para>
<programlisting><![CDATA[Transaction tx = session.beginTransaction();
Query query = session.createQuery("select c from Cat as c where c.sex = :sex");
query.setCharacter("sex", 'F');
for (Iterator it = query.iterate(); it.hasNext();) {
Cat cat = (Cat) it.next();
out.println("Female Cat: " + cat.getName() );
}
tx.commit();]]></programlisting>
<para>
Hibernate also offers an object-oriented <emphasis>query by criteria</emphasis> API
that can be used to formulate type-safe queries. Hibernate of course uses
<literal>PreparedStatement</literal>s and parameter binding for all SQL communication
with the database. You may also use Hibernate's direct SQL query feature or
get a plain JDBC connection from a <literal>Session</literal> in rare cases.
</para>
</sect1>
<sect1 id="quickstart-summary" revision="1">
<title>Finally</title>
<para>
We only scratched the surface of Hibernate in this small tutorial. Please note that
we don't include any servlet specific code in our examples. You have to create a
servlet yourself and insert the Hibernate code as you see fit.
</para>
<para>
Keep in mind that Hibernate, as a data access layer, is tightly integrated into
your application. Usually, all other layers depent on the persistence mechanism.
Make sure you understand the implications of this design.
</para>
<para>
For a more complex application example, see http://caveatemptor.hibernate.org/ and
have a look at other tutorials linked on http://www.hibernate.org/Documentation
</para>
</sect1>
</chapter>

View File

@ -1234,20 +1234,286 @@ public void removeFromEvent(Event event) {
need one side as <literal>inverse</literal>. In a one-to-many association it has to be the many-side, 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. in many-to-many association you can pick either side, there is no difference.
</para> </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> </sect2>
<para>
Let's turn this into a small web application.
</para>
</sect1> </sect1>
<sect1 id="tutorial-summary"> <sect1 id="tutorial-webapp">
<title>Part 3 - The EventManager web application</title>
<para>
A Hibernate web application uses <literal>Session</literal> and <literal>Transaction</literal>
almost like a standalone application. However, some common patterns are useful. We now write
an <literal>EventManagerServlet</literal>. This servlet can list all events stored in the
database, and it provides an HTML form to enter new events.
</para>
<sect2 id="tutorial-webapp-servlet">
<title>Writing the basic servlet</title>
<para>
Create a new class in your source directory, in the <literal>events</literal>
package:
</para>
<programlisting><![CDATA[package events;
// Imports
public class EventManagerServlet extends HttpServlet {
private final SimpleDateFormat dateFormatter =
new SimpleDateFormat("dd.MM.yyyy");
// Servlet code
}]]></programlisting>
<para>
The <literal>dateFormatter</literal> is a tool we'll need later to convert
<literal>Date</literal> objects from and to strings. It makes sense to only
have one formatter as a member of the servlet.
</para>
<para>
The servlet handles HTTP <literal>GET</literal> requests only, hence, the method
we implement is <literal>doGet()</literal>:
</para>
<programlisting><![CDATA[protected void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
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);
}}]]></programlisting>
<para>
The pattern we are applying 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
<literal>SessionFactory</literal>. Then a database transaction is started&mdash;all
data access as to occur inside a transaction, no matter if data is read or written
(we don't use the auto-commit mode in applications).
</para>
<para>
Next, the possible actions of the request are processed and the response HTML
is rendered. We'll get to that part soon.
</para>
<para>
Finally, the unit of work ends when processing and rendering is complete. If any
problem occured during processing or rendering, an exception will be thrown
and the database transaction rolled back. This completes the
<literal>session-per-request</literal> pattern. Instead of the transaction
demarcation code in every servlet you could also write a servlet filter.
See the Hibernate website and Wiki for more information about this pattern,
called <emphasis>Open Session in View</emphasis>&mdash;you'll need it as soon
as you consider rendering your view in JSP, not in a servlet.
</para>
</sect2>
<sect2 id="tutorial-webapp-processing">
<title>Processing and rendering</title>
<para>
Let's implement the processing of the request and rendering of the page.
</para>
<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")) ) {
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>");
}
}
// Print page
printEventForm(out);
listEvents(out);
// Write HTML footer
out.println("</body></html>");
out.flush();
out.close();]]></programlisting>
<para>
Granted, this coding style with a mix of Java and HTMl would not scale
in a bigger application&mdash;keep in mind that we are only illustrating
basic Hibernate concepts in this tutorial. The code prints an HTML
header and a footer. Inside this page, an HTML form for event entry and
a list of all events in the database are printed. The first method is
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>
<para>
The <literal>listEvents()</literal> method uses the Hibernate
<literal>Session</literal> bound to the current thread to execute
a query:
</para>
<programlisting><![CDATA[private void listEvents(PrintWriter out) {
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();
out.println("<tr>");
out.println("<td>" + event.getTitle() + "</td>");
out.println("<td>" + dateFormatter.format(event.getDate()) + "</td>");
out.println("</tr>");
}
out.println("</table>");
}
}]]></programlisting>
<para>
Finally, the <literal>store</literal> action is dispatched to the
<literal>createAndStoreEvent()</literal> method, which also uses
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);
HibernateUtil.getSessionFactory()
.getCurrentSession().save(theEvent);
}]]></programlisting>
<para>
That's it, the servlet is complete. A request to the servlet will be processed
in a single <literal>Session</literal> and <literal>Transaction</literal>. As
earlier in the standalone application, Hibernate can automatically bind these
ojects to the current thread of execution. This gives you the freedom to layer
your code and access the <literal>SessionFactory</literal> in any way you like.
Usually you'd use a more sophisticated design and move the data access code
into data access objects (the DAO pattern). See the Hibernate Wiki for more
examples.
</para>
</sect2>
<sect2 id="tutorial-webapp-deploy">
<title>Deploying and testing</title>
<para>
To deploy this application you have to create a web archive, a WAR. Add the
following Ant target to your <literal>build.xml</literal>:
</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"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<servlet>
<servlet-name>Event Manager</servlet-name>
<servlet-class>events.EventManagerServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Event Manager</servlet-name>
<url-pattern>/eventmanager</url-pattern>
</servlet-mapping>
</web-app>]]></programlisting>
<para>
Before you compile and deploy the web application, note that an additional library
is required: <literal>jsdk.jar</literal>. This is the Java servlet development kit,
if you don't have this library already, get it from the Sun website and copy it to
your library directory. However, it will be only used for compliation and excluded
from the WAR package.
</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 don't have Tomcat installed, download
it and follow the installation instructions. You don't have to change any Tomcat
configuration to deploy this application though.
</para>
<para>
Once deployed and Tomcat is running, access the application at
<literal>http://localhost:8080/hibernate-tutorial/eventmanager</literal>. Make
sure you watch the Tomcat log to see Hibernate initialize when the first
request hits your servlet (the static initializer in <literal>HibernateUtil</literal>
is called) and to get the detailed output if any exceptions occurs.
</para>
</sect2>
</sect1>
<sect1 id="tutorial-summary" revision="1">
<title>Summary</title> <title>Summary</title>
<para> <para>
This tutorial covered the basics of writing a simple standalone Hibernate application. This tutorial covered the basics of writing a simple standalone Hibernate application
and a small web application.
</para> </para>
<para> <para>
@ -1264,4 +1530,4 @@ public void removeFromEvent(Event event) {
</sect1> </sect1>
</chapter> </chapter>

View File

@ -35,5 +35,15 @@
<arg value="${action}"/> <arg value="${action}"/>
</java> </java>
</target> </target>
<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>
</project> </project>

View File

@ -1,3 +1,7 @@
Put all required Hibernate libraries here to run the tutorial. Put all required Hibernate libraries here to run the tutorial.
See lib/README.txt in the Hibernate distribution and the tutorial See lib/README.txt in the Hibernate distribution and the tutorial
chapter in the reference documentation. chapter in the reference documentation.
You might need other third-party libraries to compile the full
tutorial source code. For example, you will need jsdk.jar if you
want to compile the servlets of the example web application.

View File

@ -0,0 +1,102 @@
package events;
import util.HibernateUtil;
import javax.servlet.http.*;
import javax.servlet.ServletException;
import java.io.*;
import java.util.*;
import java.text.SimpleDateFormat;
public class EventManagerServlet extends HttpServlet {
private final SimpleDateFormat dateFormatter =
new SimpleDateFormat("dd.MM.yyyy");
protected void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
try {
// Begin unit of work
HibernateUtil.getSessionFactory()
.getCurrentSession().beginTransaction();
// 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")) ) {
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>");
}
}
// Print page
printEventForm(out);
listEvents(out);
// Write HTML footer
out.println("</body></html>");
out.flush();
out.close();
// End unit of work
HibernateUtil.getSessionFactory()
.getCurrentSession().getTransaction().commit();
} catch (Exception ex) {
HibernateUtil.getSessionFactory()
.getCurrentSession().getTransaction().rollback();
throw new ServletException(ex);
}
}
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>");
}
private void listEvents(PrintWriter out) {
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();
out.println("<tr>");
out.println("<td>" + event.getTitle() + "</td>");
out.println("<td>" + dateFormatter.format(event.getDate()) + "</td>");
out.println("</tr>");
}
out.println("</table>");
}
}
protected void createAndStoreEvent(String title, Date theDate) {
Event theEvent = new Event();
theEvent.setTitle(title);
theEvent.setDate(theDate);
HibernateUtil.getSessionFactory()
.getCurrentSession().save(theEvent);
}
}

View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4"
xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<servlet>
<servlet-name>Event Manager</servlet-name>
<servlet-class>events.EventManagerServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Event Manager</servlet-name>
<url-pattern>/eventmanager</url-pattern>
</servlet-mapping>
</web-app>