Let's turn the following discussion into a small web application...
Une application web Hibernate utilise la Session
et Transaction
comme une application standalone. Cependant, quelques patterns sont utiles. Nous allons coder une EventManagerServlet
. Cette servlet peut lister tous les évènements stockés dans la base de données, et fournir une formulaire HTML pour saisir
d'autres évènements.
Créons une nouvelle classe dans notre répertoire source, dans le package events
:
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).
UNTRANSLATED 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.
Ensuite, les actions possibles de la requêtes sont exécutées et la réponse HTML est rendue. Nous en parlerons plus tard.
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.
Implémentons l'exécution de la requête et le rendu de la 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>"); }
La méthode listEvents()
utilise la Session
Hibernate liée au thread courant pour exécuter la requête:
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>"); } }
FEnfin, l'action store
renvoie à la méthode createAndStoreEvent()
, qui utilise aussi la Session
du thread courant:
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.
Pour déployer cette application, vous devez créer une archive Web, un War. Ajoutez la cible Ant suivante dans votre 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>
Cette cible créé un fichier nommé hibernate-tutorial.war
dans le répertoire de votre projet. Elle package les bibliothèques et le descripteur web.xml
qui est attendu dans le répertoire racine de votre projet:
<?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.
Pour construire et déployer, appelez ant war
dans votre projet et copier le fichier hibernate-tutorial.war
dans le répertoire webapp
de tomcat Si vous n'avez pas installé Tomcat, téléchargez le et suivez la notice d'installation. Vous n'avez pas à modifier
la configuration Tomcat pour déployer cette application.
Une fois l'application déployée et Tomcat lancé, accédez à l'application via http://localhost:8080/hibernate-tutorial/eventmanager
. Assurez vous de consulter les traces tomcat pour observer l'initialisation d'Hibernate à la première requête touchant votre
servlet (l'initialisation statique dans HibernateUtil
est invoquée) et pour vérifier qu'aucune exception ne survienne.