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:
parent
57c3f1f4ea
commit
601401ef7d
|
@ -39,10 +39,12 @@
|
|||
description="Compile documentation for all languages and all formats.">
|
||||
|
||||
<!-- 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="zh-cn"/></antcall>
|
||||
<antcall target="lang.all"><param name="lang" value="es"/></antcall>
|
||||
<antcall target="lang.all"><param name="lang" value="ko"/></antcall>
|
||||
-->
|
||||
|
||||
</target>
|
||||
|
||||
|
|
|
@ -397,7 +397,7 @@ Cat fritz = (Cat) iter.next();]]></programlisting>
|
|||
|
||||
</sect2>
|
||||
|
||||
<sect2 id="performance-fetching-initialization">
|
||||
<sect2 id="performance-fetching-initialization" revision="1">
|
||||
<title>Initializing collections and proxies</title>
|
||||
|
||||
<para>
|
||||
|
@ -441,11 +441,8 @@ Cat fritz = (Cat) iter.next();]]></programlisting>
|
|||
correctness of the exception handling of your application infrastructure.
|
||||
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
|
||||
during rendering of the view. The servlet filter has to be able to access the
|
||||
<literal>Session</literal> for this approach. We recommend that a
|
||||
<literal>ThreadLocal</literal> variable be used to hold the current
|
||||
<literal>Session</literal> (see chapter 1,
|
||||
<xref linkend="quickstart-playingwithcats"/>, for an example implementation).
|
||||
during rendering of the view. See the Hibernate Wiki for examples of this
|
||||
"Open Session in View" pattern.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
|
|
|
@ -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 & 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>
|
|
@ -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,
|
||||
in many-to-many association you can pick either side, there is no difference.
|
||||
</para>
|
||||
<!--
|
||||
<para>
|
||||
In the next section we integrate Hibernate with Tomcat and WebWork - the <literal>EventManager</literal>
|
||||
doesn't scale anymore with our growing application.
|
||||
</para>
|
||||
-->
|
||||
|
||||
</sect2>
|
||||
|
||||
<para>
|
||||
Let's turn this into a small web application.
|
||||
</para>
|
||||
|
||||
</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—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>—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—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>
|
||||
|
||||
<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>
|
||||
|
@ -1264,4 +1530,4 @@ public void removeFromEvent(Event event) {
|
|||
|
||||
</sect1>
|
||||
|
||||
</chapter>
|
||||
</chapter>
|
||||
|
|
|
@ -35,5 +35,15 @@
|
|||
<arg value="${action}"/>
|
||||
</java>
|
||||
</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>
|
|
@ -1,3 +1,7 @@
|
|||
Put all required Hibernate libraries here to run the tutorial.
|
||||
See lib/README.txt in the Hibernate distribution and the tutorial
|
||||
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.
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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>
|
Loading…
Reference in New Issue