具体操作步骤:
新建一个WebProject,命名为hibernate,在tomcat安装目录下的conf目录下的server.xml文件中引入其context path(在</host>之前):<Context path="/hibernate" docBase="D:JavaWebhibernateWebRoot" reloadable="true"/> 添加struts依赖的jar包和mysql的驱动一共七个jar文件(commons-fileupload-1.2.1.jar、commons-logging-1.0.4.jar、freemarker-2.3.13.jar、ognl-2.6.11.jar、struts2-core-2.1.6.jar、xwork-core-2.1.2.jar、mysql-connector-java-5.1.34-bin.jar),粘贴到WEB-INF的lib目录下。
选中项目,点击菜单栏的MyEclipse(或右键MyEclipse),选择Project Capabilities->Add Hibernate Capabilities。JAR Library Installation选中Copy checked Library Jars to project folder and add to build-path,为了方便,这里Hibernate规范与风中叶老师的规范一致选择3.2,点击next,弹出Create Hibernate XML configuration file对话框继续next,在Specify Hibernate database connection details对话框中去掉Specify database connection details前面的√,点击next,去掉Create SessionFactory class?前面的√,点击Finish。这样我们的项目依赖的hibernate相关的jar文件就被纳入到项目下面了。
hibernate项目下的src目录下自动生成了一个文件:hibernate.cfg.xml,切换到source视图。 在<session-factory></session-factory>之间添加如下信息:
<?xml version='1.0' encoding='UTF-8'?> <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd"> <!-- Generated by MyEclipse Hibernate Tools. --> <hibernate-configuration> <session-factory> <property name="show_sql">true</property> <!-- 属性之间没有上下关系,放在哪里都行 --> <property name="connection.url">jdbc:mysql://localhost:3306/myhibernate</property> <property name="connection.username">root</property> <property name="connection.password">root</property> <property name="connection.driver_class">com.mysql.jdbc.Driver</property> <property name="dialect">org.hibernate.dialect.MySQLDialect</property> <mapping resource="Person.hbm.xml"/> <!-- 将主配置文件包含对象-关系映射文件,之所以映射是因为hibernate启动时只会加载主配置文件 --> </session-factory> </hibernate-configuration>
由于hibernate可以支持多种数据库,所以其dialect(方言)属性必须配置,可以在Hibernate3.2API文档中找到org.hibernate.dialect这个包下的MySqlDialect类。
接着在MySql中创建数据库Hibernate,新建表:person,字段有:id(主键,int,长度11,非空,注意:不将id设置为自动增加类型,让hibernate为我们做这些,体会hibernate的作用)、username(varchar,长度20,允许空值)、password(varchar,长度20,允许空值)、age(int,长度11,允许空值)、registerdate(datetime,长度0,允许空值)
接着对这张表创建相应的类,即POJO,新建包com.hibernate.model,在该包下建立相应的持久化类:Person.java
package com.hibernate.model; import java.sql.Date; public class Person { private Integer id; // int也没有问题,但是最好使用其包装类Integer private String username; private String password; private Integer age; private Date registerdate; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public Date getRegisterdate() { return registerdate; } public void setRegisterdate(Date registerdate) { this.registerdate = registerdate; } }
然后在src目录下对Person.java文件创建一个Hibernate映射文件Person.hbm.xml(hibernate mapping),可将其选中,右键选择Open With->MyEclipse Hibernate Mapping Editor,这样写xml的时候就带提示了。
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <class name="com.hibernate.model.Person" table="person"> <!-- 将类与表相关联,使得类中的属性和表中的字段关联起来 --> <id name="id" column="id" type="int"> <!-- 类中id属性和映射到表中的id字段,类型为int/integer皆可 --> <generator class="increment"> <!-- 主键id的生成方式为自增 --> </generator> </id> <property name="username" column="username" type="string"></property> <!-- 如果不写字段名,则默认与类中的属性名相同 --> <property name="password" column="password" type="string"></property> <property name="age" column="age" type="int"></property> <property name="registerdate" column="registerdate" type="date"></property> </class> </hibernate-mapping>
这样一个类-一张表-一个hbm已经全部创建完成。这个hbm就起到连接类和表的作用
然后,新建包com.hibernate.util,在下面新建类:HibernateUtil.java(可以不写,但是写上的话可以方便我们对后台数据库进行操作):
package com.hibernate.util; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.cfg.Configuration; public class HibernateUtil { private static SessionFactory sessionFactory; static { try { sessionFactory = new Configuration().configure().buildSessionFactory(); } catch(Exception ex) { System.out.println("构造SessionFactory异常发生:" + ex.getMessage()); } } public static Session currentSession() { Session session = sessionFactory.openSession(); return session; } public static void closeSession(Session session) { if(null != session) { session.close(); } } }
接下来,创建com.hibernate.persister包,在该包下面创建类DBPreson.java,完成增删查改操作:
package com.hibernate.persistence; import java.util.List; import org.hibernate.Query; import org.hibernate.Session; import org.hibernate.Transaction; import com.hibernate.model.Person; import com.hibernate.util.HibernateUtil; public class DBPerson { /** * 创建新的用户 */ public static void save(Person person) { Session session = HibernateUtil.currentSession(); Transaction tx = session.beginTransaction(); // 开启事务 try { session.save(person); tx.commit(); } catch(Exception ex) { System.out.println("增加用户异常发生!"); if(null != tx) { tx.rollback(); } } finally { HibernateUtil.closeSession(session); } } /** * 查询出所有用户 */ @SuppressWarnings("unchecked") public static List<Person> listAll() { Session session = HibernateUtil.currentSession(); Transaction tx = session.beginTransaction(); // 开启事务 List<Person> list = null; try { Query query = session.createQuery("from Person"); // hql语句,Hibernate query language,这里Person是类名 list = (List<Person>)query.list(); tx.commit(); } catch(Exception ex) { System.out.println("增加用户异常发生!"); if(null != tx) { tx.rollback(); } } finally { HibernateUtil.closeSession(session); } return list; } }
我们先不把DAO这块全部写完,因为现在主要关注的点是整个流程,因此我们先编写界面端,流程跑通了以后再返回来编写DAO。在WebRoot下面新建一个register.jsp:
<body> <form action="save.action"> username:<input type="text" name="username" size="20"><br> password:<input type="password" name="password" size="20"><br> age:<input type="text" name="age" size="20"><br> <input type="submit" value="submit"> </form> </body>
JSP登录页面编写完后,紧接着会点击提交,提交后流程来到struts.xml中:
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts> <package name="hibernate" extends="struts-default"> <action name="save" class="com.test.action.PersonAction" method="save"> <!-- 这里关注点在hibernate,因此struts的输入校验就省略了 --> <result name="success">/listAll.jsp</result> </action> </package> </struts>
接下来创建相应的action:PersonAction.java:
package com.test.action; import java.util.List; import javax.servlet.http.HttpServletRequest; import org.apache.struts2.ServletActionContext; import com.hibernate.model.Person; import com.hibernate.persistence.DBPerson; import com.opensymphony.xwork2.ActionSupport; public class PersonAction extends ActionSupport { private int id; // 因为增删查改共用一个action,所以这里增加不需要id,但是删除、查找、修改都需要提供一个id这样的熟悉,增加的时候没有id,只是不给它赋值而已,并不会产生什么问题 private String username; private String password; private int age; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } // 完成用户增加的操作 public String save() throws Exception { Person person = new Person(); if(username != null) username = new String(username.getBytes("ISO-8859-1"),"utf-8"); // 解决中文乱码问题 person.setUsername(username); person.setPassword(password); person.setAge(age); java.sql.Date registerDate = new java.sql.Date(new java.util.Date().getTime()); person.setRegisterdate(registerDate); DBPerson.save(person); // 将person对象存到数据库中 List<Person> list = DBPerson.listAll(); HttpServletRequest request = ServletActionContext.getRequest(); request.setAttribute("list", list); return SUCCESS; } }
接下来根据struts.xml的标识,流程应该转到listAll.jsp:
<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%> <%@ taglib uri="/struts-tags" prefix="s" %> <% String path = request.getContextPath(); String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/"; %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <base href="<%=basePath%>"> <title>My JSP 'listAll.jsp' starting page</title> <meta http-equiv="pragma" content="no-cache"> <meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="expires" content="0"> <meta http-equiv="keywords" content="keyword1,keyword2,keyword3"> <meta http-equiv="description" content="This is my page"> <!-- <link rel="stylesheet" type="text/css" href="styles.css"> --> <script type="text/javascript"> function del() { if(confirm("Are you sure?")) return true; return false; } </script> </head> <body> <table width="80%" align="center" border="1"> <tr> <th> <%--<th>:定义表格内的表头单元格。此th元素内部的文本通常会呈现为粗体。--%> username </th> <th> password </th> <th> registerdate </th> <th> age </th> <th> update </th> <th> delete </th> </tr> <s:iterator value="#request.list" id="person"> <!-- 或者嵌入Java代码使用for循环的形式 --> <tr> <td> <s:a href="getPerson.action?id=%{#person.id}"> <s:property value="username"/> <!-- username是action中的一个成员变量 --> </s:a> </td> <td> <s:property value="password"/> </td> <td> <s:property value="registerdate"/> </td> <td> <s:property value="age"/> </td> <td> <s:a href="updatePPerson.action?id=%{#person.id}"> update </s:a> </td> <td> <s:a href="deletePerson.action?id=%{#person.id}" onchange="return del();"> delete </s:a> </td> </tr> </s:iterator> </table> </body> </html>
最后,还需要在web.xml中配置struts的过滤器:
<?xml version="1.0" encoding="UTF-8"?> <web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"> <filter> <filter-name>struts2</filter-name> <filter-class> org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter </filter-class> </filter> <filter-mapping> <filter-name>struts2</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> </web-app>
访问http://localhost:8080/hibernate/register.jsp,增加用户和显示所有用户信息的功能就实现了。
值得补充的一点是:session的beginTransaction()方法底层调用了java.sql.Connection类的setAutoCommit(false)方法,即所有的SQL语句执行完之后再提交,而setAutoCommit(true)的意思是一条SQL语句执行完毕立刻进行提交,相当于一条SQL语句就是一个事务。而我们在一个事务里面可能执行多个命令(SQL语句),因此通常情况下,使用JDBC的话,通常将setAutoCommit方法的参数设置为false,因为它的默认值为true。那么什么时候进行提交呢?其实session的commit方法底层就是通过调用Connection的commit方法实现提交。
下面,进一步完善,点击用户名列出用户所有信息:
在DBPerson.java中增加方法:
public static Person getPersonById(Integer id) { Session session = HibernateUtil.currentSession(); Transaction tx = session.beginTransaction(); // 开启事务 Person person = null; try { // Object get(Class clazz, Serializable id) // Integer实现了Serializable接口(没有任何方法,一个标识性的接口)表示可以序列化到硬盘上也可以从硬盘或者网络恢复到内存里 person = (Person)session.get(Person.class, id); tx.commit(); } catch(Exception ex) { System.out.println("查看用户异常发生"); if(null != tx) tx.rollback(); } finally { HibernateUtil.closeSession(session); } return person; }
struts.xml中配置action:
<action name="getPerson" class="com.test.action.PersonAction" method="getPerson"> <result name="success">/getPerson.jsp</result> </action>
然后在PersonAction.java中写方法getPerson():
public String getPerson() throws Exception { Person person = DBPerson.getPersonById(id); HttpServletRequest request = ServletActionContext.getRequest(); request.setAttribute("person", person); return SUCCESS; }
然后是展示页面:getPerson.jsp:
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <%@ taglib uri="/struts-tags" prefix="s"%> <% String path = request.getContextPath(); String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/"; %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <base href="<%=basePath%>"> <title>My JSP 'getPerson.jsp' starting page</title> <meta http-equiv="pragma" content="no-cache"> <meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="expires" content="0"> <meta http-equiv="keywords" content="keyword1,keyword2,keyword3"> <meta http-equiv="description" content="This is my page"> <!-- <link rel="stylesheet" type="text/css" href="styles.css"> --> </head> <body> <table width="70%" align="center" border="1"> <tr> <td>username: </td> <td><s:property value="#request.person.username"> </td> </tr> <tr> <td>password: </td> <td><s:property value="#request.person.password"> </td> </tr> <tr> <td>age: </td> <td><s:property value="#request.person.age"> </td> </tr> <tr> <td>registerdate: </td> <td><s:property value="#request.person.registerdate"> </td> </tr> <tr> <td colspan="2"> <input type="button" value="back" onclick="javascript:history.back();"> </td> </tr> </table> </body> </html>
测试,其中一个实例如下:
接下来实现删除操作:
在DBPerson中增加removePerson方法:
public static void removePerson(Integer id) { Session session = HibernateUtil.currentSession(); Transaction tx = session.beginTransaction(); // 开启事务 try { // 从数据库中查询出主键为id的记录所对应的对象,删除对象,hibernate就知道要删除的对应的记录是哪个了 Person person = (Person)session.get(Person.class, id); session.delete(person); tx.commit(); } catch(Exception ex) { System.out.println("删除用户异常发生"); if(null != tx) { tx.rollback(); } } finally { HibernateUtil.closeSession(session); } }
然后配置struts.xml:
<action name="deletePerson" class="com.test.action.PersonAction" method="deletePerson"> <result name="success">/listAll.jsp</result> </action>
然后是PersonAction.java中增加deletePerson方法:
public String deletePerson() throws Exception { DBPerson.removePerson(id); List<Person> list = DBPerson.listAll(); HttpServletRequest request = ServletActionContext.getRequest(); request.setAttribute("list", list); return SUCCESS; }
重启服务器,测试通过即可。
最后,实现update操作:
在struts.xml中配置信息:
<action name="updatePPerson" class="com.test.action.PersonAction" method="getPerson"> <result name="success">/updatePerson.jsp</result> </action>
新建一个updatePerson.jsp:(引入struts标签库,页面编码UTF-8)
<body> <form action="updatePerson.action" method="post"> username:<s:textfield name="username" value="%{#request.person.username}" readonly="true"></s:textfield> <br /> password:<s:textfield name="password" value="%{#request.person.password}" ></s:textfield> <br /> age:<s:textfield name="age" value="%{#request.person.age}"></s:textfield> <br /> registerdate:<s:textfield name="registerdate" value="%{#request.person.registerdate}" readonly="true"></s:textfield> <s:hidden name="id" value="%{#request.person.id}"></s:hidden> <input type="submit" value="submit" /> </form> </body>
在DBPerson.java中增加updatePerson方法:
public static void updatePerson(Person person) { Session session = HibernateUtil.currentSession(); Transaction tx = session.beginTransaction(); // 开启事务 try { session.update(person); tx.commit(); } catch(Exception ex) { System.out.println("修改用户信息异常发生"); if(null != tx) tx.rollback(); } finally { HibernateUtil.closeSession(session); } }
在struts.xml中增加action:
<action name="updatePerson" class="com.test.action.PersonAction" method="updatePerson"> <result name="success">/listAll.jsp</result> </action>
在PersonAction.java中增加updatePerson方法:
public String updatePerson() throws Exception { Person person = DBPerson.getPersonById(id); person.setPassword(password); person.setAge(age); /* 也可以通过 Person person = new Person(); person.setId(id); person.setUsername(username); ... 的方式,但是个类里忘记定义registerdate属性了,因此采用上面先查后该的方式 */ DBPerson.updatePerson(person); List<Person> list = DBPerson.listAll(); HttpServletRequest request = ServletActionContext.getRequest(); request.setAttribute("list", list); return SUCCESS; }
至此,增删查改各个功能全部实现。但是现在这个小应用有个很严重的问题:增加用户的时候列出所有用户的时候,刷新页面,重复的用户信息会不断的插入到数据库中同时显示到页面上。
比如注册信息列出所有用户地址栏为:http://localhost:8080/hibernate/save.action?username=%E5%93%88%E5%93%88&password=123&age=12,那么重复刷新,save操作会被不断的执行,解决方法就是采用重定向的方式而不是使用请求转发。
需要更改struts.xml中result的部分,增加一个属性:redirect
<action name="save" class="com.test.action.PersonAction" method="save"> <!-- 这里关注点在hibernate,因此struts的输入校验就省略了 --> <result name="success" type="redirect">/listAll.jsp</result> </action>
删除PersonAction中如下部分(因为是重定向的方式,是另一个request,当前request已经不起作用了):
List<Person> list = DBPerson.listAll(); HttpServletRequest request = ServletActionContext.getRequest(); request.setAttribute("list", list);
需要更改listAll.jsp如下的部分(导包和访问DBPerson的listAll方法):
<%@ page language="java" import="java.util.*, com.hibernate.model.Person, com.hibernate.persistence.DBPerson" pageEncoding="utf-8"%> <%@ taglib uri="/struts-tags" prefix="s" %> <% String path = request.getContextPath(); String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/"; %> <% List<Person> list = DBPerson.listAll(); request.setAttribute("list", list); %>
这样增加用户后跳转页面链接变成了http://localhost:8080/hibernate/listAll.jsp,不会出现重复注册的现象了。