Let's turn the following discussion into a small web application...
Hibernate web应用程序使用Session
和Transaction
的方式几乎和独立应用程序是一样的。但是,有一些常见的模式(pattern)非常有用。现在我们编写一个EventManagerServlet
。这个servlet可以列出数据库中保存的所有的events,还提供一个HTML表单来增加新的events。
在你的源代码目录的events
包中创建一个新的类:
package events; // Imports public class EventManagerServlet extends HttpServlet { // Servlet code }
我们后面会用到dateFormatter
的工具, 它把Date
对象转换为字符串。只要一个formatter作为servlet的成员就可以了。
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).
我们称这里应用的模式为每次请求一个session(session-per-request)。当有请求到达这个servlet的时候,通过对SessionFactory
的第一次调用,打开一个新的Hibernate Session
。然后启动一个数据库事务-所有的数据访问都是在事务中进行,不管是读还是写(我们在应用程序中不使用auto-commit模式)。
不要为每次数据库操作都使用一个新的Hibernate Session
。将Hibernate Session
的范围设置为整个请求。要用getCurrentSession()
,这样它自动会绑定到当前Java线程。
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.
处理与渲染
// 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>"); }
listEvents()
方法使用绑定到当前线程的Hibernate Session
来执行查询:
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>"); } }
最后,store
动作会被导向到createAndStoreEvent()
方法,它也使用当前线程的Session
:
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.
要发布这个程序,你得把它打成web发布包:WAR文件。把下面的脚本加入到你的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>
这段代码在你的开发目录中创建一个hibernate-tutorial.war
的文件。它把所有的类库和web.xml
描述文件都打包进去,web.xml 文件应该位于你的开发根目录中:
<?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.
在你的开发目录中,调用ant war
来构建、打包,然后把hibernate-tutorial.war
文件拷贝到你的tomcat的webapps
目录下。假若你还没安装Tomcat,就去下载一个,按照指南来安装。对此应用的发布,你不需要修改任何Tomcat的配置。
在部署完,启动Tomcat之后,通过http://localhost:8080/hibernate-tutorial/eventmanager
进行访问你的应用,在第一次servlet 请求发生时,请在Tomcat log中确认你看到Hibernate被初始化了(HibernateUtil
的静态初始化器被调用),假若有任何异常抛出,也可以看到详细的输出。