Let's turn the following discussion into a small web application...
A Hibernate web application uses Session
and Transaction
almost like a standalone application. However, some common patterns are useful. We now write
an EventManagerServlet
. This servlet can list all events stored in the
database, and it provides an HTML form to enter new events.
Create a new class in your source directory, in the events
package:
package events; // Imports public class EventManagerServlet extends HttpServlet { // Servlet code }
The servlet handles HTTP GET
requests only, hence, the method
we implement is doGet()
:
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { SimpleDateFormat dateFormatter = new SimpleDateFormat("dd.MM.yyyy"); 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); } }
The pattern we are applying here is called session-per-request.
When a request hits the servlet, a new Hibernate Session
is
opened through the first call to getCurrentSession()
on the
SessionFactory
. 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).
Do not use a new Hibernate Session
for
every database operation. Use one Hibernate Session
that is
scoped to the whole request. Use getCurrentSession()
, so that
it is automatically bound to the current Java thread.
Next, the possible actions of the request are processed and the response HTML is rendered. We'll get to that part soon.
Finally, the unit of work ends when processing and rendering is complete. If any
problem occurred during processing or rendering, an exception will be thrown
and the database transaction rolled back. This completes the
session-per-request
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 Open Session in View-you'll need it as soon
as you consider rendering your view in JSP, not in a servlet.
Let's implement the processing of the request and rendering of the page.
// 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, dateFormatter); // Write HTML footer out.println("</body></html>"); out.flush(); out.close();
Granted, this coding style with a mix of Java and HTML would not scale in a more complex 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:
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>"); }
The listEvents()
method uses the Hibernate
Session
bound to the current thread to execute
a query:
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(); out.println("<tr>"); out.println("<td>" + event.getTitle() + "</td>"); out.println("<td>" + dateFormatter.format(event.getDate()) + "</td>"); out.println("</tr>"); } out.println("</table>"); } }
Finally, the store
action is dispatched to the
createAndStoreEvent()
method, which also uses
the Session
of the current thread:
protected void createAndStoreEvent(String title, Date theDate) { Event theEvent = new Event(); theEvent.setTitle(title); theEvent.setDate(theDate); HibernateUtil.getSessionFactory() .getCurrentSession().save(theEvent); }
That's it, the servlet is complete. A request to the servlet will be processed
in a single Session
and Transaction
. As
earlier in the standalone application, Hibernate can automatically bind these
objects to the current thread of execution. This gives you the freedom to layer
your code and access the SessionFactory
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.
To deploy this application you have to create a web archive, a WAR. Add the
following Ant target to your build.xml
:
<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>
This target creates a file called hibernate-tutorial.war
in your project directory. It packages all libraries and the web.xml
descriptor, which is expected in the base directory of your project:
<?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>
Before you compile and deploy the web application, note that an additional library
is required: jsdk.jar
. 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 compilation and excluded
from the WAR package.
To build and deploy call ant war
in your project directory
and copy the hibernate-tutorial.war
file into your Tomcat
webapp
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.
Once deployed and Tomcat is running, access the application at
http://localhost:8080/hibernate-tutorial/eventmanager
. Make
sure you watch the Tomcat log to see Hibernate initialize when the first
request hits your servlet (the static initializer in HibernateUtil
is called) and to get the detailed output if any exceptions occurs.