为资费列表页面增加分页功能。
Hibernate中的分页查询是通过Query对象设置参数来统一实现的,而使用Spring整合Hibernate后,session由Spring统一负责维护,并且没有直接暴露给开发者。如果想在这样的程序中获取到session,从而创建Query对象实现分页的话,需要使用HibernateTemplate中的executeFind方法实现查询,代码结构如下:
getHibernateTemplate().executeFind(new HibernateCallback() {
@Override
public Object doInHibernate(Session session)
throws HibernateException, SQLException {
// 在这里使用session
return null;
}
});
上面方法中,需要传入一个接口类型,我们往往传入一个匿名的对象,实现了该接口。这个接口Spring会自动调用并以它的返回值作为查询结果返回,在接口的实现中,我们可以使用session来自定义查询。
实现此案例需要按照如下步骤进行。
步骤一:在DAO中增加分页方法
在ICostDao接口中,增加分页方法,代码如下:
package com.tarena.dao; import java.util.List; import com.tarena.entity.Cost; public interface ICostDao { List<Cost> findAll(); #cold_bold /** #cold_bold * 查询某页资费数据 #cold_bold * @param page 页码 #cold_bold * @param pageSize 页容量 #cold_bold * @return #cold_bold */ #cold_bold List<Cost> findByPage(int page, int pageSize); #cold_bold #cold_bold /** #cold_bold * 查询总页数 #cold_bold * @param pageSize #cold_bold * @return #cold_bold */ #cold_bold int findTotalPage(int pageSize); }
在CostDaoImpl中,增加分页的实现方法,代码如下:
package com.tarena.dao; import java.sql.SQLException; import java.util.List; import javax.annotation.Resource; import org.hibernate.HibernateException; import org.hibernate.Query; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.springframework.orm.hibernate3.HibernateCallback; import org.springframework.orm.hibernate3.support.HibernateDaoSupport; import org.springframework.stereotype.Repository; import com.tarena.entity.Cost; @Repository public class CostDaoImpl extends HibernateDaoSupport implements ICostDao { @Resource public void setSF(SessionFactory sf) { super.setSessionFactory(sf); } @Override public List<Cost> findAll() { String hql = "from Cost"; return getHibernateTemplate().find(hql); } #cold_bold @Override #cold_bold public List<Cost> findByPage(final int page, final int pageSize) { #cold_bold /* #cold_bold * executeFind方法需要传入接口对象,该接口对象的doInHibernate方法 #cold_bold * 会在执行查询时自动被Spring调用,并且doInHibernate方法的返回值 #cold_bold * 将作为最终的结果返回。 #cold_bold * */ #cold_bold return getHibernateTemplate().executeFind(new HibernateCallback() { #cold_bold #cold_bold /* #cold_bold * doInHibernate方法类似于回调函数,是在executeFind方法的内部被调用的。 #cold_bold * 该方法中可以直接使用session,这个session由Spring负责管理,不需要我们 #cold_bold * 创建和关闭。 #cold_bold */ #cold_bold @Override #cold_bold public Object doInHibernate(Session session) #cold_bold throws HibernateException, SQLException { #cold_bold String hql = "from Cost"; #cold_bold Query query = session.createQuery(hql); #cold_bold /* #cold_bold * 设置分页参数,注意在内层函数中调用外层函数的参数, #cold_bold * 要求外层函数的参数必须是final的,因此需要将 #cold_bold * page、pageSize设置为final。 #cold_bold * */ #cold_bold query.setFirstResult((page-1)*pageSize); #cold_bold query.setMaxResults(pageSize); #cold_bold return query.list(); #cold_bold } #cold_bold }); #cold_bold } #cold_bold #cold_bold @Override #cold_bold public int findTotalPage(int pageSize) { #cold_bold String hql = "select count(*) from Cost"; #cold_bold List<Object> list = getHibernateTemplate().find(hql); #cold_bold // 查询出总行数 #cold_bold int rows = Integer.valueOf(list.get(0).toString()); #cold_bold // 根据总行数计算总页数 #cold_bold if (rows % pageSize == 0) #cold_bold return rows / pageSize; #cold_bold else #cold_bold return rows / pageSize + 1; #cold_bold } }
步骤二:修改Action,处理分页查询请求
修改Action,增加分页输入条件page、pageSize,增加输出条件totalPage,并根据分页条件查询数据以及计算出总页数,代码如下:
package com.tarena.action; import java.util.List; import javax.annotation.Resource; import org.springframework.stereotype.Controller; import com.tarena.dao.ICostDao; import com.tarena.entity.Cost; @Controller @Scope("prototype") public class FindCostAction { @Resource private ICostDao costDao; #cold_bold // input #cold_bold private int page = 1; #cold_bold private int pageSize; #cold_bold // output private List<Cost> costs; #cold_bold private int totalPage; public String load() { #cold_bold costs = costDao.findByPage(page, pageSize); #cold_bold totalPage = costDao.findTotalPage(pageSize); return "success"; } #cold_bold public int getPage() { #cold_bold return page; #cold_bold } #cold_bold #cold_bold public void setPage(int page) { #cold_bold this.page = page; #cold_bold } #cold_bold #cold_bold public int getPageSize() { #cold_bold return pageSize; #cold_bold } #cold_bold #cold_bold public void setPageSize(int pageSize) { #cold_bold this.pageSize = pageSize; #cold_bold } #cold_bold #cold_bold public int getTotalPage() { #cold_bold return totalPage; #cold_bold } #cold_bold #cold_bold public void setTotalPage(int totalPage) { #cold_bold this.totalPage = totalPage; #cold_bold } public List<Cost> getCosts() { return costs; } public void setCosts(List<Cost> costs) { this.costs = costs; } }
步骤三:注入页容量
修改struts.xml,在查询action的配置中注入页容量pageSize,代码如下:
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.1.7//EN" "http://struts.apache.org/dtds/struts-2.1.7.dtd"> <struts> <package name="cost" namespace="/cost" extends="json-default"> <action name="findCost" class="findCostAction" method="load"> #cold_bold <!-- 注入页容量 --> #cold_bold <param name="pageSize">3</param> <result name="success"> /WEB-INF/cost/find_cost.jsp </result> </action> </package> </struts>
步骤四:处理页面分页逻辑
修改find_cost.jsp,增加分页的业务逻辑,代码如下:
<%@page pageEncoding="utf-8" isELIgnored="false"%> <%@taglib uri="/struts-tags" prefix="s"%> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>达内-NetCTOSS</title> <link type="text/css" rel="stylesheet" media="all" href="../styles/global.css" /> <link type="text/css" rel="stylesheet" media="all" href="../styles/global_color.css" /> <script language="javascript" type="text/javascript"> //排序按钮的点击事件 function sort(btnObj) { if (btnObj.className == "sort_desc") btnObj.className = "sort_asc"; else btnObj.className = "sort_desc"; } //启用 function startFee() { var r = window.confirm("确定要启用此资费吗?资费启用后将不能修改和删除。"); document.getElementById("operate_result_info").style.display = "block"; } //删除 function deleteFee() { var r = window.confirm("确定要删除此资费吗?"); document.getElementById("operate_result_info").style.display = "block"; } </script> </head> <body> <!--Logo区域开始--> <div id="header"> <img src="../images/logo.png" alt="logo" class="left"/> <a href="#">[退出]</a> </div> <!--Logo区域结束--> <!--导航区域开始--> <div id="navi"> <ul id="menu"> <li><a href="../index.html" class="index_off"></a></li> <li><a href="../role/role_list.html" class="role_off"></a></li> <li><a href="../admin/admin_list.html" class="admin_off"></a></li> <li><a href="../fee/fee_list.html" class="fee_on"></a></li> <li><a href="../account/account_list.html" class="account_off"></a></li> <li><a href="../service/service_list.html" class="service_off"></a></li> <li><a href="../bill/bill_list.html" class="bill_off"></a></li> <li><a href="../report/report_list.html" class="report_off"></a></li> <li><a href="../user/user_info.html" class="information_off"></a></li> <li><a href="../user/user_modi_pwd.html" class="password_off"></a></li> </ul> </div> <!--导航区域结束--> <!--主要区域开始--> <div id="main"> <form action="" method=""> <!--排序--> <div class="search_add"> <div> <!--<input type="button" value="月租" class="sort_asc" onclick="sort(this);" />--> <input type="button" value="基费" class="sort_asc" onclick="sort(this);" /> <input type="button" value="时长" class="sort_asc" onclick="sort(this);" /> </div> <input type="button" value="增加" class="btn_add" onclick="location.href='fee_add.html';" /> </div> <!--启用操作的操作提示--> <div id="operate_result_info" class="operate_success"> <img src="../images/close.png" onclick="this.parentNode.style.display='none';" /> 删除成功! </div> <!--数据区域:用表格展示数据--> <div id="data"> <table id="datalist"> <tr> <th>资费ID</th> <th class="width100">资费名称</th> <th>基本时长</th> <th>基本费用</th> <th>单位费用</th> <th>创建时间</th> <th>开通时间</th> <th class="width50">状态</th> <th class="width200"></th> </tr> <!-- 使用Struts2标签遍历集合,使用OGNL表达式输出内容。 --> <s:iterator value="costs"> <tr> <td><s:property value="id"/></td> <td><a href="fee_detail.html"><s:property value="name"/></a></td> <td><s:property value="baseDuration"/></td> <td><s:property value="baseCost"/></td> <td><s:property value="unitCost"/></td> <td><s:property value="createTime"/></td> <td><s:property value="startTime"/></td> <td> <s:if test="status==0">开通</s:if> <s:else>暂停</s:else> </td> <td> <input type="button" value="启用" class="btn_start" onclick="startFee();" /> <input type="button" value="修改" class="btn_modify" onclick="location.href='fee_modi.html';" /> <input type="button" value="删除" class="btn_delete" onclick="deleteFee();" /> </td> </tr> </s:iterator> </table> <p>业务说明:<br /> 1、创建资费时,状态为暂停,记载创建时间;<br /> 2、暂停状态下,可修改,可删除;<br /> 3、开通后,记载开通时间,且开通后不能修改、不能再停用、也不能删除;<br /> 4、业务账号修改资费时,在下月底统一触发,修改其关联的资费ID(此触发动作由程序处理) </p> </div> <!--分页--> <div id="pages"> <s:if test="page==1"> <a href="#">上一页</a> </s:if> <s:else> <a href="findCost?page=<s:property value='page-1'/>">上一页</a> </s:else> <s:iterator begin="1" end="totalPage" var="p"> <s:if test="#p==page"> <a href="findCost?page=<s:property value="#p"/>" class="current_page"><s:property value="#p"/></a> </s:if> <s:else> <a href="findCost?page=<s:property value="#p"/>"><s:property value="#p"/></a> </s:else> </s:iterator> <s:if test="page==totalPage"> <a href="#">下一页</a> </s:if> <s:else> <a href="findCost?page=<s:property value='page+1'/>">下一页</a> </s:else> </div> </form> </div> <!--主要区域结束--> <div id="footer"> <p>[源自北美的技术,最优秀的师资,最真实的企业环境,最适用的实战项目]</p> <p>版权所有(C)加拿大达内IT培训集团公司 </p> </div> </body> </html>
步骤五:测试
重新部署项目并重启tomcat,访问资费列表功能,效果如下图:
图-1
本案例的完整代码如下所示:
ICostDao完整代码如下:
package com.tarena.dao; import java.util.List; import com.tarena.entity.Cost; public interface ICostDao { List<Cost> findAll(); /** * 查询某页资费数据 * @param page 页码 * @param pageSize 页容量 * @return */ List<Cost> findByPage(int page, int pageSize); /** * 查询总页数 * @param pageSize * @return */ int findTotalPage(int pageSize); }
CostDaoImpl完整代码如下:
package com.tarena.dao; import java.sql.SQLException; import java.util.List; import javax.annotation.Resource; import org.hibernate.HibernateException; import org.hibernate.Query; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.springframework.orm.hibernate3.HibernateCallback; import org.springframework.orm.hibernate3.support.HibernateDaoSupport; import org.springframework.stereotype.Repository; import com.tarena.entity.Cost; @Repository public class CostDaoImpl extends HibernateDaoSupport implements ICostDao { @Resource public void setSF(SessionFactory sf) { super.setSessionFactory(sf); } @Override public List<Cost> findAll() { String hql = "from Cost"; return getHibernateTemplate().find(hql); } @Override public List<Cost> findByPage(final int page, final int pageSize) { /* * executeFind方法需要传入接口对象,该接口对象的doInHibernate方法 * 会在执行查询时自动被Spring调用,并且doInHibernate方法的返回值 * 将作为最终的结果返回。 * */ return getHibernateTemplate().executeFind(new HibernateCallback() { /* * doInHibernate方法类似于回调函数,是在executeFind方法的内部被调用的。 * 该方法中可以直接使用session,这个session由Spring负责管理,不需要我们 * 创建和关闭。 */ @Override public Object doInHibernate(Session session) throws HibernateException, SQLException { String hql = "from Cost"; Query query = session.createQuery(hql); /* * 设置分页参数,注意在内层函数中调用外层函数的参数, * 要求外层函数的参数必须是final的,因此需要将 * page、pageSize设置为final。 * */ query.setFirstResult((page-1)*pageSize); query.setMaxResults(pageSize); return query.list(); } }); } @Override public int findTotalPage(int pageSize) { String hql = "select count(*) from Cost"; List<Object> list = getHibernateTemplate().find(hql); // 查询出总行数 int rows = Integer.valueOf(list.get(0).toString()); // 根据总行数计算总页数 if (rows % pageSize == 0) return rows / pageSize; else return rows / pageSize + 1; } }
FindCostAction完整代码如下:
package com.tarena.action; import java.util.List; import javax.annotation.Resource; import org.springframework.stereotype.Controller; import com.tarena.dao.ICostDao; import com.tarena.entity.Cost; @Controller @Scope("prototype") public class FindCostAction { @Resource private ICostDao costDao; // input private int page = 1; private int pageSize; // output private List<Cost> costs; private int totalPage; public String load() { costs = costDao.findByPage(page, pageSize); totalPage = costDao.findTotalPage(pageSize); return "success"; } public int getPage() { return page; } public void setPage(int page) { this.page = page; } public int getPageSize() { return pageSize; } public void setPageSize(int pageSize) { this.pageSize = pageSize; } public int getTotalPage() { return totalPage; } public void setTotalPage(int totalPage) { this.totalPage = totalPage; } public List<Cost> getCosts() { return costs; } public void setCosts(List<Cost> costs) { this.costs = costs; } }
struts.xml完整代码如下:
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.1.7//EN" "http://struts.apache.org/dtds/struts-2.1.7.dtd"> <struts> <package name="cost" namespace="/cost" extends="json-default"> <action name="findCost" class="findCostAction" method="load"> <!-- 注入页容量 --> <param name="pageSize">3</param> <result name="success"> /WEB-INF/cost/find_cost.jsp </result> </action> </package> </struts>
find_cost.jsp完整代码如下:
<%@page pageEncoding="utf-8" isELIgnored="false"%> <%@taglib uri="/struts-tags" prefix="s"%> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>达内-NetCTOSS</title> <link type="text/css" rel="stylesheet" media="all" href="../styles/global.css" /> <link type="text/css" rel="stylesheet" media="all" href="../styles/global_color.css" /> <script language="javascript" type="text/javascript"> //排序按钮的点击事件 function sort(btnObj) { if (btnObj.className == "sort_desc") btnObj.className = "sort_asc"; else btnObj.className = "sort_desc"; } //启用 function startFee() { var r = window.confirm("确定要启用此资费吗?资费启用后将不能修改和删除。"); document.getElementById("operate_result_info").style.display = "block"; } //删除 function deleteFee() { var r = window.confirm("确定要删除此资费吗?"); document.getElementById("operate_result_info").style.display = "block"; } </script> </head> <body> <!--Logo区域开始--> <div id="header"> <img src="../images/logo.png" alt="logo" class="left"/> <a href="#">[退出]</a> </div> <!--Logo区域结束--> <!--导航区域开始--> <div id="navi"> <ul id="menu"> <li><a href="../index.html" class="index_off"></a></li> <li><a href="../role/role_list.html" class="role_off"></a></li> <li><a href="../admin/admin_list.html" class="admin_off"></a></li> <li><a href="../fee/fee_list.html" class="fee_on"></a></li> <li><a href="../account/account_list.html" class="account_off"></a></li> <li><a href="../service/service_list.html" class="service_off"></a></li> <li><a href="../bill/bill_list.html" class="bill_off"></a></li> <li><a href="../report/report_list.html" class="report_off"></a></li> <li><a href="../user/user_info.html" class="information_off"></a></li> <li><a href="../user/user_modi_pwd.html" class="password_off"></a></li> </ul> </div> <!--导航区域结束--> <!--主要区域开始--> <div id="main"> <form action="" method=""> <!--排序--> <div class="search_add"> <div> <!--<input type="button" value="月租" class="sort_asc" onclick="sort(this);" />--> <input type="button" value="基费" class="sort_asc" onclick="sort(this);" /> <input type="button" value="时长" class="sort_asc" onclick="sort(this);" /> </div> <input type="button" value="增加" class="btn_add" onclick="location.href='fee_add.html';" /> </div> <!--启用操作的操作提示--> <div id="operate_result_info" class="operate_success"> <img src="../images/close.png" onclick="this.parentNode.style.display='none';" /> 删除成功! </div> <!--数据区域:用表格展示数据--> <div id="data"> <table id="datalist"> <tr> <th>资费ID</th> <th class="width100">资费名称</th> <th>基本时长</th> <th>基本费用</th> <th>单位费用</th> <th>创建时间</th> <th>开通时间</th> <th class="width50">状态</th> <th class="width200"></th> </tr> <!-- 使用Struts2标签遍历集合,使用OGNL表达式输出内容。 --> <s:iterator value="costs"> <tr> <td><s:property value="id"/></td> <td><a href="fee_detail.html"><s:property value="name"/></a></td> <td><s:property value="baseDuration"/></td> <td><s:property value="baseCost"/></td> <td><s:property value="unitCost"/></td> <td><s:property value="createTime"/></td> <td><s:property value="startTime"/></td> <td> <s:if test="status==0">开通</s:if> <s:else>暂停</s:else> </td> <td> <input type="button" value="启用" class="btn_start" onclick="startFee();" /> <input type="button" value="修改" class="btn_modify" onclick="location.href='fee_modi.html';" /> <input type="button" value="删除" class="btn_delete" onclick="deleteFee();" /> </td> </tr> </s:iterator> </table> <p>业务说明:<br /> 1、创建资费时,状态为暂停,记载创建时间;<br /> 2、暂停状态下,可修改,可删除;<br /> 3、开通后,记载开通时间,且开通后不能修改、不能再停用、也不能删除;<br /> 4、业务账号修改资费时,在下月底统一触发,修改其关联的资费ID(此触发动作由程序处理) </p> </div> <!--分页--> <div id="pages"> <s:if test="page==1"> <a href="#">上一页</a> </s:if> <s:else> <a href="findCost?page=<s:property value='page-1'/>">上一页</a> </s:else> <s:iterator begin="1" end="totalPage" var="p"> <s:if test="#p==page"> <a href="findCost?page=<s:property value="#p"/>" class="current_page"><s:property value="#p"/></a> </s:if> <s:else> <a href="findCost?page=<s:property value="#p"/>"><s:property value="#p"/></a> </s:else> </s:iterator> <s:if test="page==totalPage"> <a href="#">下一页</a> </s:if> <s:else> <a href="findCost?page=<s:property value='page+1'/>">下一页</a> </s:else> </div> </form> </div> <!--主要区域结束--> <div id="footer"> <p>[源自北美的技术,最优秀的师资,最真实的企业环境,最适用的实战项目]</p> <p>版权所有(C)加拿大达内IT培训集团公司 </p> </div> </body> </html>
开发资费修改功能,要求可以打开修改页面,并显示出要修改的数据,其中对于修改数据的查询要求使用延迟加载的方法。
由于DAO中使用延迟加载方法查询出修改数据,而该数据在修改页面中才被使用,即在使用数据时session已经关闭。因此,要想解决这个问题,需要使用Open session in view技术。在SSH中使用Open session in view技术很简单,只需要在web.xml中配置一个filter即可,并且这个filter已经由Spring预置好了,名为org.springframework.orm.hibernate3.support.OpenSessionInViewFilter。
实现此案例需要按照如下步骤进行。
步骤一:在DAO中增加根据ID查询的方法
在ICostDao接口中,增加根据ID查询资费数据的方法,代码如下:
package com.tarena.dao; import java.util.List; import com.tarena.entity.Cost; public interface ICostDao { List<Cost> findAll(); /** * 查询某页资费数据 * @param page 页码 * @param pageSize 页容量 * @return */ List<Cost> findByPage(int page, int pageSize); /** * 查询总页数 * @param pageSize * @return */ int findTotalPage(int pageSize); #cold_bold /** #cold_bold * 根据ID查询资费数据 #cold_bold * @param id #cold_bold * @return #cold_bold */ #cold_bold Cost findById(int id); }
在CostDaoImpl中实现这个方法,代码如下:
package com.tarena.dao; import java.sql.SQLException; import java.util.List; import javax.annotation.Resource; import org.hibernate.HibernateException; import org.hibernate.Query; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.springframework.orm.hibernate3.HibernateCallback; import org.springframework.orm.hibernate3.support.HibernateDaoSupport; import org.springframework.stereotype.Repository; import com.tarena.entity.Cost; @Repository public class CostDaoImpl extends HibernateDaoSupport implements ICostDao { @Resource public void setSF(SessionFactory sf) { super.setSessionFactory(sf); } @Override public List<Cost> findAll() { String hql = "from Cost"; return getHibernateTemplate().find(hql); } @Override public List<Cost> findByPage(final int page, final int pageSize) { /* * executeFind方法需要传入接口对象,该接口对象的doInHibernate方法 * 会在执行查询时自动被Spring调用,并且doInHibernate方法的返回值 * 将作为最终的结果返回。 * */ return getHibernateTemplate().executeFind(new HibernateCallback() { /* * doInHibernate方法类似于回调函数,是在executeFind方法的内部被调用的。 * 该方法中可以直接使用session,这个session由Spring负责管理,不需要我们 * 创建和关闭。 */ @Override public Object doInHibernate(Session session) throws HibernateException, SQLException { String hql = "from Cost"; Query query = session.createQuery(hql); /* * 设置分页参数,注意在内层函数中调用外层函数的参数, * 要求外层函数的参数必须是final的,因此需要将 * page、pageSize设置为final。 * */ query.setFirstResult((page-1)*pageSize); query.setMaxResults(pageSize); return query.list(); } }); } @Override public int findTotalPage(int pageSize) { String hql = "select count(*) from Cost"; List<Object> list = getHibernateTemplate().find(hql); // 查询出总行数 int rows = Integer.valueOf(list.get(0).toString()); // 根据总行数计算总页数 if (rows % pageSize == 0) return rows / pageSize; else return rows / pageSize + 1; } #cold_bold @Override #cold_bold public Cost findById(int id) { #cold_bold // 使用延迟加载的方法实现 #cold_bold return (Cost) getHibernateTemplate().load(Cost.class, id); #cold_bold } }
步骤二: 创建打开修改页面的Action
在com.tarena.action包下,创建打开修改页面的Action,实现根据ID查询资费数据的逻辑,代码如下:
package com.tarena.action; import javax.annotation.Resource; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Controller; import com.tarena.dao.ICostDao; import com.tarena.entity.Cost; /** * 加载修改数据 */ @Controller @Scope("prototype") public class ToUpdateCostAction { @Resource private ICostDao costDao; // input private int id; // output private Cost cost; public String load() { cost = costDao.findById(id); return "success"; } public int getId() { return id; } public void setId(int id) { this.id = id; } public Cost getCost() { return cost; } public void setCost(Cost cost) { this.cost = cost; } }
步骤三:配置Action
在struts.xml中配置这个Action,代码如下:
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.1.7//EN" "http://struts.apache.org/dtds/struts-2.1.7.dtd"> <struts> <package name="cost" namespace="/cost" extends="json-default"> <action name="findCost" class="findCostAction" method="load"> <!-- 注入页容量 --> <param name="pageSize">3</param> <result name="success"> /WEB-INF/cost/find_cost.jsp </result> </action> #cold_bold <!-- 打开修改页面 --> #cold_bold <action name="toUpdateCost" #cold_bold class="toUpdateCostAction" method="load"> #cold_bold <result name="success"> #cold_bold /WEB-INF/cost/update_cost.jsp #cold_bold </result> #cold_bold </action> </package> </struts>
步骤四:创建修改页面
在WEB-INF/cost文件夹下,创建修改页面update_cost.jsp,并通过Struts2的UI标签回显要修改的数据,代码如下:
<%@page pageEncoding="utf-8"%> <%@taglib uri="/struts-tags" prefix="s"%> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>达内-NetCTOSS</title> <link type="text/css" rel="stylesheet" media="all" href="../styles/global.css" /> <link type="text/css" rel="stylesheet" media="all" href="../styles/global_color.css" /> <script language="javascript" type="text/javascript"> //保存结果的提示 function showResult() { showResultDiv(true); window.setTimeout("showResultDiv(false);", 3000); } function showResultDiv(flag) { var divResult = document.getElementById("save_result_info"); if (flag) divResult.style.display = "block"; else divResult.style.display = "none"; } //切换资费类型 function feeTypeChange(type) { var inputArray = document.getElementById("main").getElementsByTagName("input"); if (type == 1) { inputArray[5].readOnly = true; inputArray[5].value = ""; inputArray[5].className += " readonly"; inputArray[6].readOnly = false; inputArray[6].className = "width100"; inputArray[7].readOnly = true; inputArray[7].className += " readonly"; inputArray[7].value = ""; } else if (type == 2) { inputArray[5].readOnly = false; inputArray[5].className = "width100"; inputArray[6].readOnly = false; inputArray[6].className = "width100"; inputArray[7].readOnly = false; inputArray[7].className = "width100"; } else if (type == 3) { inputArray[5].readOnly = true; inputArray[5].value = ""; inputArray[5].className += " readonly"; inputArray[6].readOnly = true; inputArray[6].value = ""; inputArray[6].className += " readonly"; inputArray[7].readOnly = false; inputArray[7].className = "width100"; } } </script> </head> <body> <!--Logo区域开始--> <div id="header"> <img src="../images/logo.png" alt="logo" class="left"/> <a href="#">[退出]</a> </div> <!--Logo区域结束--> <!--导航区域开始--> <div id="navi"> <ul id="menu"> <li><a href="../index.html" class="index_off"></a></li> <li><a href="../role/role_list.html" class="role_off"></a></li> <li><a href="../admin/admin_list.html" class="admin_off"></a></li> <li><a href="../fee/fee_list.html" class="fee_on"></a></li> <li><a href="../account/account_list.html" class="account_off"></a></li> <li><a href="../service/service_list.html" class="service_off"></a></li> <li><a href="../bill/bill_list.html" class="bill_off"></a></li> <li><a href="../report/report_list.html" class="report_off"></a></li> <li><a href="../user/user_info.html" class="information_off"></a></li> <li><a href="../user/user_modi_pwd.html" class="password_off"></a></li> </ul> </div> <!--导航区域结束--> <!--主要区域开始--> <div id="main"> <div id="save_result_info" class="save_success">保存成功!</div> <form action="" method="" class="main_form"> <div class="text_info clearfix"><span>资费ID:</span></div> <div class="input_info"> <s:textfield name="cost.id" cssClass="readonly" readonly="true"/> </div> <div class="text_info clearfix"><span>资费名称:</span></div> <div class="input_info"> <s:textfield name="cost.name" cssClass="width300"/> <span class="required">*</span> <div class="validate_msg_short">50长度的字母、数字、汉字和下划线的组合</div> </div> <div class="text_info clearfix"><span>资费类型:</span></div> <div class="input_info fee_type"> <s:radio name="cost.costType" list="#{'1':'包月','2':'套餐','3':'计时' }" onclick="feeTypeChange(this.value);"/> </div> <div class="text_info clearfix"><span>基本时长:</span></div> <div class="input_info"> <s:textfield name="cost.baseDuration" cssClass="width100"/> <span class="info">小时</span> <span class="required">*</span> <div class="validate_msg_long">1-600之间的整数</div> </div> <div class="text_info clearfix"><span>基本费用:</span></div> <div class="input_info"> <s:textfield name="cost.baseCost" cssClass="width100"/> <span class="info">元</span> <span class="required">*</span> <div class="validate_msg_long">0-99999.99之间的数值</div> </div> <div class="text_info clearfix"><span>单位费用:</span></div> <div class="input_info"> <s:textfield name="cost.unitCost" cssClass="width100"/> <span class="info">元/小时</span> <span class="required">*</span> <div class="validate_msg_long">0-99999.99之间的数值</div> </div> <div class="text_info clearfix"><span>资费说明:</span></div> <div class="input_info_high"> <s:textarea name="cost.descr" cssClass="width300 height70"/> <div class="validate_msg_short">100长度的字母、数字、汉字和下划线的组合</div> </div> <div class="button_info clearfix"> <input type="button" value="保存" class="btn_save" onclick="showResult();" /> <input type="button" value="取消" class="btn_save" /> </div> </form> </div> <!--主要区域结束--> <div id="footer"> <span>[源自北美的技术,最优秀的师资,最真实的企业环境,最适用的实战项目]</span> <br /> <span>版权所有(C)加拿大达内IT培训集团公司 </span> </div> </body> </html>
步骤五:处理修改按钮
在find_cost.jsp中,将修改按钮的访问路径改为修改action,代码如下:
<%@page pageEncoding="utf-8" isELIgnored="false"%> <%@taglib uri="/struts-tags" prefix="s"%> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>达内-NetCTOSS</title> <link type="text/css" rel="stylesheet" media="all" href="../styles/global.css" /> <link type="text/css" rel="stylesheet" media="all" href="../styles/global_color.css" /> <script language="javascript" type="text/javascript"> //排序按钮的点击事件 function sort(btnObj) { if (btnObj.className == "sort_desc") btnObj.className = "sort_asc"; else btnObj.className = "sort_desc"; } //启用 function startFee() { var r = window.confirm("确定要启用此资费吗?资费启用后将不能修改和删除。"); document.getElementById("operate_result_info").style.display = "block"; } //删除 function deleteFee() { var r = window.confirm("确定要删除此资费吗?"); document.getElementById("operate_result_info").style.display = "block"; } </script> </head> <body> <!--Logo区域开始--> <div id="header"> <img src="../images/logo.png" alt="logo" class="left"/> <a href="#">[退出]</a> </div> <!--Logo区域结束--> <!--导航区域开始--> <div id="navi"> <ul id="menu"> <li><a href="../index.html" class="index_off"></a></li> <li><a href="../role/role_list.html" class="role_off"></a></li> <li><a href="../admin/admin_list.html" class="admin_off"></a></li> <li><a href="../fee/fee_list.html" class="fee_on"></a></li> <li><a href="../account/account_list.html" class="account_off"></a></li> <li><a href="../service/service_list.html" class="service_off"></a></li> <li><a href="../bill/bill_list.html" class="bill_off"></a></li> <li><a href="../report/report_list.html" class="report_off"></a></li> <li><a href="../user/user_info.html" class="information_off"></a></li> <li><a href="../user/user_modi_pwd.html" class="password_off"></a></li> </ul> </div> <!--导航区域结束--> <!--主要区域开始--> <div id="main"> <form action="" method=""> <!--排序--> <div class="search_add"> <div> <!--<input type="button" value="月租" class="sort_asc" onclick="sort(this);" />--> <input type="button" value="基费" class="sort_asc" onclick="sort(this);" /> <input type="button" value="时长" class="sort_asc" onclick="sort(this);" /> </div> <input type="button" value="增加" class="btn_add" onclick="location.href='fee_add.html';" /> </div> <!--启用操作的操作提示--> <div id="operate_result_info" class="operate_success"> <img src="../images/close.png" onclick="this.parentNode.style.display='none';" /> 删除成功! </div> <!--数据区域:用表格展示数据--> <div id="data"> <table id="datalist"> <tr> <th>资费ID</th> <th class="width100">资费名称</th> <th>基本时长</th> <th>基本费用</th> <th>单位费用</th> <th>创建时间</th> <th>开通时间</th> <th class="width50">状态</th> <th class="width200"></th> </tr> <!-- 使用Struts2标签遍历集合,使用OGNL表达式输出内容。 --> <s:iterator value="costs"> <tr> <td><s:property value="id"/></td> <td><a href="fee_detail.html"><s:property value="name"/></a></td> <td><s:property value="baseDuration"/></td> <td><s:property value="baseCost"/></td> <td><s:property value="unitCost"/></td> <td><s:property value="createTime"/></td> <td><s:property value="startTime"/></td> <td> <s:if test="status==0">开通</s:if> <s:else>暂停</s:else> </td> <td> <input type="button" value="启用" class="btn_start" onclick="startFee();" /> #cold_bold <input type="button" value="修改" class="btn_modify" onclick="location.href='toUpdateCost?id=<s:property value="id"/>';" /> <input type="button" value="删除" class="btn_delete" onclick="deleteFee();" /> </td> </tr> </s:iterator> </table> <p>业务说明:<br /> 1、创建资费时,状态为暂停,记载创建时间;<br /> 2、暂停状态下,可修改,可删除;<br /> 3、开通后,记载开通时间,且开通后不能修改、不能再停用、也不能删除;<br /> 4、业务账号修改资费时,在下月底统一触发,修改其关联的资费ID(此触发动作由程序处理) </p> </div> <!--分页--> <div id="pages"> <s:if test="page==1"> <a href="#">上一页</a> </s:if> <s:else> <a href="findCost?page=<s:property value='page-1'/>">上一页</a> </s:else> <s:iterator begin="1" end="totalPage" var="p"> <s:if test="#p==page"> <a href="findCost?page=<s:property value="#p"/>" class="current_page"><s:property value="#p"/></a> </s:if> <s:else> <a href="findCost?page=<s:property value="#p"/>"><s:property value="#p"/></a> </s:else> </s:iterator> <s:if test="page==totalPage"> <a href="#">下一页</a> </s:if> <s:else> <a href="findCost?page=<s:property value='page+1'/>">下一页</a> </s:else> </div> </form> </div> <!--主要区域结束--> <div id="footer"> <p>[源自北美的技术,最优秀的师资,最真实的企业环境,最适用的实战项目]</p> <p>版权所有(C)加拿大达内IT培训集团公司 </p> </div> </body> </html>
步骤六:测试
重新部署项目并启动tomcat,访问资费列表页面,并点击任意一行数据的修改按钮,显示出的修改页面效果如下图,可见除了ID字段外,其他字段都显示为空,这是由于采用了延迟加载查询数据,却没有保证session不提前关闭导致的。
图-2
步骤七:完善
在web.xml中配置一个filter,用于保证session在视图层加载数据时是开启的,代码如下:
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5"> <display-name></display-name> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> <!-- 配置listener,使tomcat启动时自动加载Spring --> <listener> <listener-class> org.springframework.web.context.ContextLoaderListener </listener-class> </listener> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:applicationContext.xml</param-value> </context-param> #cold_bold <!-- #cold_bold 配置过滤器,以保证在视图层session是开启的。 #cold_bold 该过滤器必须在Struts2前端控制器之前配置才有效。 #cold_bold --> #cold_bold <filter> #cold_bold <filter-name>OpenSessionInView</filter-name> #cold_bold <filter-class> #cold_bold org.springframework.orm.hibernate3.support.OpenSessionInViewFilter #cold_bold </filter-class> #cold_bold </filter> #cold_bold <filter-mapping> #cold_bold <filter-name>OpenSessionInView</filter-name> #cold_bold <url-pattern>/*</url-pattern> #cold_bold </filter-mapping> <!-- 配置前端控制器 --> <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>
步骤八:测试
重新部署项目并启动tomcat,再次访问资费修改页面,效果如下图:
图-3
本案例的完整代码如下所示:
ICostDao完整代码如下:
package com.tarena.dao; import java.util.List; import com.tarena.entity.Cost; public interface ICostDao { List<Cost> findAll(); /** * 查询某页资费数据 * @param page 页码 * @param pageSize 页容量 * @return */ List<Cost> findByPage(int page, int pageSize); /** * 查询总页数 * @param pageSize * @return */ int findTotalPage(int pageSize); /** * 根据ID查询资费数据 * @param id * @return */ Cost findById(int id); }
CostDaoImpl完整代码如下:
package com.tarena.dao; import java.sql.SQLException; import java.util.List; import javax.annotation.Resource; import org.hibernate.HibernateException; import org.hibernate.Query; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.springframework.orm.hibernate3.HibernateCallback; import org.springframework.orm.hibernate3.support.HibernateDaoSupport; import org.springframework.stereotype.Repository; import com.tarena.entity.Cost; @Repository public class CostDaoImpl extends HibernateDaoSupport implements ICostDao { @Resource public void setSF(SessionFactory sf) { super.setSessionFactory(sf); } @Override public List<Cost> findAll() { String hql = "from Cost"; return getHibernateTemplate().find(hql); } @Override public List<Cost> findByPage(final int page, final int pageSize) { /* * executeFind方法需要传入接口对象,该接口对象的doInHibernate方法 * 会在执行查询时自动被Spring调用,并且doInHibernate方法的返回值 * 将作为最终的结果返回。 * */ return getHibernateTemplate().executeFind(new HibernateCallback() { /* * doInHibernate方法类似于回调函数,是在executeFind方法的内部被调用的。 * 该方法中可以直接使用session,这个session由Spring负责管理,不需要我们 * 创建和关闭。 */ @Override public Object doInHibernate(Session session) throws HibernateException, SQLException { String hql = "from Cost"; Query query = session.createQuery(hql); /* * 设置分页参数,注意在内层函数中调用外层函数的参数, * 要求外层函数的参数必须是final的,因此需要将 * page、pageSize设置为final。 * */ query.setFirstResult((page-1)*pageSize); query.setMaxResults(pageSize); return query.list(); } }); } @Override public int findTotalPage(int pageSize) { String hql = "select count(*) from Cost"; List<Object> list = getHibernateTemplate().find(hql); // 查询出总行数 int rows = Integer.valueOf(list.get(0).toString()); // 根据总行数计算总页数 if (rows % pageSize == 0) return rows / pageSize; else return rows / pageSize + 1; } @Override public Cost findById(int id) { // 使用延迟加载的方法实现 return (Cost) getHibernateTemplate().load(Cost.class, id); } }
ToUpdateCostAction完整代码如下:
package com.tarena.action; import javax.annotation.Resource; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Controller; import com.tarena.dao.ICostDao; import com.tarena.entity.Cost; /** * 加载修改数据 */ @Controller @Scope("prototype") public class ToUpdateCostAction { @Resource private ICostDao costDao; // input private int id; // output private Cost cost; public String load() { cost = costDao.findById(id); return "success"; } public int getId() { return id; } public void setId(int id) { this.id = id; } public Cost getCost() { return cost; } public void setCost(Cost cost) { this.cost = cost; } }
struts.xml完整代码如下:
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.1.7//EN" "http://struts.apache.org/dtds/struts-2.1.7.dtd"> <struts> <package name="cost" namespace="/cost" extends="json-default"> <action name="findCost" class="findCostAction" method="load"> <!-- 注入页容量 --> <param name="pageSize">3</param> <result name="success"> /WEB-INF/cost/find_cost.jsp </result> </action> <!-- 打开修改页面 --> <action name="toUpdateCost" class="toUpdateCostAction" method="load"> <result name="success"> /WEB-INF/cost/update_cost.jsp </result> </action> </package> </struts>
update_cost.jsp完整代码如下:
<%@page pageEncoding="utf-8"%> <%@taglib uri="/struts-tags" prefix="s"%> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>达内-NetCTOSS</title> <link type="text/css" rel="stylesheet" media="all" href="../styles/global.css" /> <link type="text/css" rel="stylesheet" media="all" href="../styles/global_color.css" /> <script language="javascript" type="text/javascript"> //保存结果的提示 function showResult() { showResultDiv(true); window.setTimeout("showResultDiv(false);", 3000); } function showResultDiv(flag) { var divResult = document.getElementById("save_result_info"); if (flag) divResult.style.display = "block"; else divResult.style.display = "none"; } //切换资费类型 function feeTypeChange(type) { var inputArray = document.getElementById("main").getElementsByTagName("input"); if (type == 1) { inputArray[5].readOnly = true; inputArray[5].value = ""; inputArray[5].className += " readonly"; inputArray[6].readOnly = false; inputArray[6].className = "width100"; inputArray[7].readOnly = true; inputArray[7].className += " readonly"; inputArray[7].value = ""; } else if (type == 2) { inputArray[5].readOnly = false; inputArray[5].className = "width100"; inputArray[6].readOnly = false; inputArray[6].className = "width100"; inputArray[7].readOnly = false; inputArray[7].className = "width100"; } else if (type == 3) { inputArray[5].readOnly = true; inputArray[5].value = ""; inputArray[5].className += " readonly"; inputArray[6].readOnly = true; inputArray[6].value = ""; inputArray[6].className += " readonly"; inputArray[7].readOnly = false; inputArray[7].className = "width100"; } } </script> </head> <body> <!--Logo区域开始--> <div id="header"> <img src="../images/logo.png" alt="logo" class="left"/> <a href="#">[退出]</a> </div> <!--Logo区域结束--> <!--导航区域开始--> <div id="navi"> <ul id="menu"> <li><a href="../index.html" class="index_off"></a></li> <li><a href="../role/role_list.html" class="role_off"></a></li> <li><a href="../admin/admin_list.html" class="admin_off"></a></li> <li><a href="../fee/fee_list.html" class="fee_on"></a></li> <li><a href="../account/account_list.html" class="account_off"></a></li> <li><a href="../service/service_list.html" class="service_off"></a></li> <li><a href="../bill/bill_list.html" class="bill_off"></a></li> <li><a href="../report/report_list.html" class="report_off"></a></li> <li><a href="../user/user_info.html" class="information_off"></a></li> <li><a href="../user/user_modi_pwd.html" class="password_off"></a></li> </ul> </div> <!--导航区域结束--> <!--主要区域开始--> <div id="main"> <div id="save_result_info" class="save_success">保存成功!</div> <form action="" method="" class="main_form"> <div class="text_info clearfix"><span>资费ID:</span></div> <div class="input_info"> <s:textfield name="cost.id" cssClass="readonly" readonly="true"/> </div> <div class="text_info clearfix"><span>资费名称:</span></div> <div class="input_info"> <s:textfield name="cost.name" cssClass="width300"/> <span class="required">*</span> <div class="validate_msg_short">50长度的字母、数字、汉字和下划线的组合</div> </div> <div class="text_info clearfix"><span>资费类型:</span></div> <div class="input_info fee_type"> <s:radio name="cost.costType" list="#{'1':'包月','2':'套餐','3':'计时' }" onclick="feeTypeChange(this.value);"/> </div> <div class="text_info clearfix"><span>基本时长:</span></div> <div class="input_info"> <s:textfield name="cost.baseDuration" cssClass="width100"/> <span class="info">小时</span> <span class="required">*</span> <div class="validate_msg_long">1-600之间的整数</div> </div> <div class="text_info clearfix"><span>基本费用:</span></div> <div class="input_info"> <s:textfield name="cost.baseCost" cssClass="width100"/> <span class="info">元</span> <span class="required">*</span> <div class="validate_msg_long">0-99999.99之间的数值</div> </div> <div class="text_info clearfix"><span>单位费用:</span></div> <div class="input_info"> <s:textfield name="cost.unitCost" cssClass="width100"/> <span class="info">元/小时</span> <span class="required">*</span> <div class="validate_msg_long">0-99999.99之间的数值</div> </div> <div class="text_info clearfix"><span>资费说明:</span></div> <div class="input_info_high"> <s:textarea name="cost.descr" cssClass="width300 height70"/> <div class="validate_msg_short">100长度的字母、数字、汉字和下划线的组合</div> </div> <div class="button_info clearfix"> <input type="button" value="保存" class="btn_save" onclick="showResult();" /> <input type="button" value="取消" class="btn_save" /> </div> </form> </div> <!--主要区域结束--> <div id="footer"> <span>[源自北美的技术,最优秀的师资,最真实的企业环境,最适用的实战项目]</span> <br /> <span>版权所有(C)加拿大达内IT培训集团公司 </span> </div> </body> </html>
web.xml完整代码如下:
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5"> <display-name></display-name> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> <!-- 配置listener,使tomcat启动时自动加载Spring --> <listener> <listener-class> org.springframework.web.context.ContextLoaderListener </listener-class> </listener> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:applicationContext.xml</param-value> </context-param> <!-- 配置过滤器,以保证在视图层session是开启的。 该过滤器必须在Struts2前端控制器之前配置才有效。 --> <filter> <filter-name>OpenSessionInView</filter-name> <filter-class> org.springframework.orm.hibernate3.support.OpenSessionInViewFilter </filter-class> </filter> <filter-mapping> <filter-name>OpenSessionInView</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- 配置前端控制器 --> <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>
完成资费修改保存功能,并在项目中解决使用Hibernate修改带来的问题。
使用Hibernate修改时,它会根据映射关系文件,自动拼出一个update语句,然后执行修改,其中映射关系文件中往往配置表中全部的字段,而很多时候,页面上不需要修改表中全部的字段,只需要修改其中的一部分,因此页面上的字段少于表中字段,在提交保存时,缺少的字段就成了空值,那么再按照完整的update语句来执行更新,就会把这些不需要更新的字段更新为空。
这个问题如果使用JDBC或者MyBatis是不会存在的,因为SQL是自己写的。而Hibernate自动生成SQL,就出现了这个问题。
本案例中,我们采用动态更新的方式来解决这个问题,即在映射关系文件中通过dynamic-update=”true”来声明更新方式为动态更新,届时Hibernate在自动生成update语句时会判断属性值是否发生改变,若改变则将属性拼入SQL,否则忽略掉这个属性。
这种方式要求传给Hibernate的对象必须是持久态的,而通过页面传入的对象是Struts2自动初始化的,是临时态的。我们可以通过ID查询出持久态对象,然后通过Spring中的BeanUtils工具类,将临时态对象的属性值复制给持久态对象,然后用这个持久态对象进行更新即可。
实现此案例需要按照如下步骤进行。
步骤一:在DAO中增加修改保存方法
在ICostDao中增加修改保存方法,代码如下:
package com.tarena.dao; import java.util.List; import com.tarena.entity.Cost; public interface ICostDao { List<Cost> findAll(); /** * 查询某页资费数据 * @param page 页码 * @param pageSize 页容量 * @return */ List<Cost> findByPage(int page, int pageSize); /** * 查询总页数 * @param pageSize * @return */ int findTotalPage(int pageSize); /** * 根据ID查询资费数据 * @param id * @return */ Cost findById(int id); #cold_bold /** #cold_bold * 修改资费数据 #cold_bold * @param cost #cold_bold */ #cold_bold void update(Cost cost); }
在CostDaoImpl中实现这个方法,代码如下:
package com.tarena.dao; import java.sql.SQLException; import java.util.List; import javax.annotation.Resource; import org.hibernate.HibernateException; import org.hibernate.Query; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.springframework.orm.hibernate3.HibernateCallback; import org.springframework.orm.hibernate3.support.HibernateDaoSupport; import org.springframework.stereotype.Repository; import com.tarena.entity.Cost; @Repository public class CostDaoImpl extends HibernateDaoSupport implements ICostDao { @Resource public void setSF(SessionFactory sf) { super.setSessionFactory(sf); } @Override public List<Cost> findAll() { String hql = "from Cost"; return getHibernateTemplate().find(hql); } @Override public List<Cost> findByPage(final int page, final int pageSize) { /* * executeFind方法需要传入接口对象,该接口对象的doInHibernate方法 * 会在执行查询时自动被Spring调用,并且doInHibernate方法的返回值 * 将作为最终的结果返回。 * */ return getHibernateTemplate().executeFind(new HibernateCallback() { /* * doInHibernate方法类似于回调函数,是在executeFind方法的内部被调用的。 * 该方法中可以直接使用session,这个session由Spring负责管理,不需要我们 * 创建和关闭。 */ @Override public Object doInHibernate(Session session) throws HibernateException, SQLException { String hql = "from Cost"; Query query = session.createQuery(hql); /* * 设置分页参数,注意在内层函数中调用外层函数的参数, * 要求外层函数的参数必须是final的,因此需要将 * page、pageSize设置为final。 * */ query.setFirstResult((page-1)*pageSize); query.setMaxResults(pageSize); return query.list(); } }); } @Override public int findTotalPage(int pageSize) { String hql = "select count(*) from Cost"; List<Object> list = getHibernateTemplate().find(hql); // 查询出总行数 int rows = Integer.valueOf(list.get(0).toString()); // 根据总行数计算总页数 if (rows % pageSize == 0) return rows / pageSize; else return rows / pageSize + 1; } @Override public Cost findById(int id) { // 使用延迟加载的方法实现 return (Cost) getHibernateTemplate().load(Cost.class, id); } #cold_bold @Override #cold_bold public void update(Cost cost) { #cold_bold if (cost == null) #cold_bold return; #cold_bold getHibernateTemplate().update(cost); #cold_bold } }
步骤二:创建修改保存Action
在com.tarena.action包下,创建修改保存Action类,代码如下:
package com.tarena.action; import javax.annotation.Resource; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Controller; import com.tarena.dao.ICostDao; import com.tarena.entity.Cost; /** * 修改保存 */ @Controller @Scope("prototype") public class UpdateCostAction { @Resource private ICostDao costDao; // input private Cost cost; public String update() { costDao.update(cost); return "success"; } public Cost getCost() { return cost; } public void setCost(Cost cost) { this.cost = cost; } }
步骤三:配置Action
在struts.xml中配置这个Action,代码如下:
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.1.7//EN" "http://struts.apache.org/dtds/struts-2.1.7.dtd"> <struts> <package name="cost" namespace="/cost" extends="json-default"> <action name="findCost" class="findCostAction" method="load"> <!-- 注入页容量 --> <param name="pageSize">3</param> <result name="success"> /WEB-INF/cost/find_cost.jsp </result> </action> <!-- 打开修改页面 --> <action name="toUpdateCost" class="toUpdateCostAction" method="load"> <result name="success"> /WEB-INF/cost/update_cost.jsp </result> </action> #cold_bold <!-- 修改保存 --> #cold_bold <action name="updateCost" #cold_bold class="updateCostAction" method="update"> #cold_bold <result name="success" type="redirectAction"> #cold_bold findCost #cold_bold </result> #cold_bold </action> </package> </struts>
步骤四:配置修改表单及按钮
修改update_cost.jsp,配置好表单以及提交按钮,代码如下:
<%@page pageEncoding="utf-8"%> <%@taglib uri="/struts-tags" prefix="s"%> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>达内-NetCTOSS</title> <link type="text/css" rel="stylesheet" media="all" href="../styles/global.css" /> <link type="text/css" rel="stylesheet" media="all" href="../styles/global_color.css" /> <script language="javascript" type="text/javascript"> //保存结果的提示 function showResult() { showResultDiv(true); window.setTimeout("showResultDiv(false);", 3000); } function showResultDiv(flag) { var divResult = document.getElementById("save_result_info"); if (flag) divResult.style.display = "block"; else divResult.style.display = "none"; } //切换资费类型 function feeTypeChange(type) { var inputArray = document.getElementById("main").getElementsByTagName("input"); if (type == 1) { inputArray[5].readOnly = true; inputArray[5].value = ""; inputArray[5].className += " readonly"; inputArray[6].readOnly = false; inputArray[6].className = "width100"; inputArray[7].readOnly = true; inputArray[7].className += " readonly"; inputArray[7].value = ""; } else if (type == 2) { inputArray[5].readOnly = false; inputArray[5].className = "width100"; inputArray[6].readOnly = false; inputArray[6].className = "width100"; inputArray[7].readOnly = false; inputArray[7].className = "width100"; } else if (type == 3) { inputArray[5].readOnly = true; inputArray[5].value = ""; inputArray[5].className += " readonly"; inputArray[6].readOnly = true; inputArray[6].value = ""; inputArray[6].className += " readonly"; inputArray[7].readOnly = false; inputArray[7].className = "width100"; } } </script> </head> <body> <!--Logo区域开始--> <div id="header"> <img src="../images/logo.png" alt="logo" class="left"/> <a href="#">[退出]</a> </div> <!--Logo区域结束--> <!--导航区域开始--> <div id="navi"> <ul id="menu"> <li><a href="../index.html" class="index_off"></a></li> <li><a href="../role/role_list.html" class="role_off"></a></li> <li><a href="../admin/admin_list.html" class="admin_off"></a></li> <li><a href="../fee/fee_list.html" class="fee_on"></a></li> <li><a href="../account/account_list.html" class="account_off"></a></li> <li><a href="../service/service_list.html" class="service_off"></a></li> <li><a href="../bill/bill_list.html" class="bill_off"></a></li> <li><a href="../report/report_list.html" class="report_off"></a></li> <li><a href="../user/user_info.html" class="information_off"></a></li> <li><a href="../user/user_modi_pwd.html" class="password_off"></a></li> </ul> </div> <!--导航区域结束--> <!--主要区域开始--> <div id="main"> <div id="save_result_info" class="save_success">保存成功!</div> #cold_bold <form action="updateCost" method="post" class="main_form"> <div class="text_info clearfix"><span>资费ID:</span></div> <div class="input_info"> <s:textfield name="cost.id" cssClass="readonly" readonly="true"/> </div> <div class="text_info clearfix"><span>资费名称:</span></div> <div class="input_info"> <s:textfield name="cost.name" cssClass="width300"/> <span class="required">*</span> <div class="validate_msg_short">50长度的字母、数字、汉字和下划线的组合</div> </div> <div class="text_info clearfix"><span>资费类型:</span></div> <div class="input_info fee_type"> <s:radio name="cost.costType" list="#{'1':'包月','2':'套餐','3':'计时' }" onclick="feeTypeChange(this.value);"/> </div> <div class="text_info clearfix"><span>基本时长:</span></div> <div class="input_info"> <s:textfield name="cost.baseDuration" cssClass="width100"/> <span class="info">小时</span> <span class="required">*</span> <div class="validate_msg_long">1-600之间的整数</div> </div> <div class="text_info clearfix"><span>基本费用:</span></div> <div class="input_info"> <s:textfield name="cost.baseCost" cssClass="width100"/> <span class="info">元</span> <span class="required">*</span> <div class="validate_msg_long">0-99999.99之间的数值</div> </div> <div class="text_info clearfix"><span>单位费用:</span></div> <div class="input_info"> <s:textfield name="cost.unitCost" cssClass="width100"/> <span class="info">元/小时</span> <span class="required">*</span> <div class="validate_msg_long">0-99999.99之间的数值</div> </div> <div class="text_info clearfix"><span>资费说明:</span></div> <div class="input_info_high"> <s:textarea name="cost.descr" cssClass="width300 height70"/> <div class="validate_msg_short">100长度的字母、数字、汉字和下划线的组合</div> </div> <div class="button_info clearfix"> #cold_bold <input type="submit" value="保存" class="btn_save" /> <input type="button" value="取消" class="btn_save" /> </div> </form> </div> <!--主要区域结束--> <div id="footer"> <span>[源自北美的技术,最优秀的师资,最真实的企业环境,最适用的实战项目]</span> <br /> <span>版权所有(C)加拿大达内IT培训集团公司 </span> </div> </body> </html>
步骤五:测试
修改之前,资费表中数据如下图所示:
图-4
重新部署项目并启动tomcat,再次访问修改功能,对id=3的数据进行一些修改,保存后,资费表中的数据如下图,可见此次更新将该条记录的status、creatime、startime字段清空了
图-5
步骤六:完善
在映射关系文件Cost.hbm.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> #cold_bold <!-- #cold_bold 通过dynamic-update将当前对象设置为动态更新, #cold_bold 对象中没有发生变化的属性将不会拼入update语句。 #cold_bold --> #cold_bold <class name="com.tarena.entity.Cost" #cold_bold table="cost" dynamic-update="true"> <id name="id" type="integer" column="id"> <!-- 用来指明主键的生成方式 --> <generator class="sequence"> <param name="sequence">cost_seq</param> </generator> </id> <property name="name" type="string" column="name" /> <property name="baseDuration" type="integer" column="base_duration" /> <property name="baseCost" type="double" column="base_cost" /> <property name="unitCost" type="double" column="unit_cost" /> <property name="status" type="string" column="status" /> <property name="descr" type="string" column="descr" /> <property name="createTime" type="date" column="creatime" /> <property name="startTime" type="date" column="startime" /> <property name="costType" type="string" column="cost_type" /> </class> </hibernate-mapping>
在UpdateCostAction中,调用DAO更新方法之前,根据传入的对象,构造一个持久态的更新对象,然后利用这个构造出来的对象进行更新,代码如下:
package com.tarena.action; import javax.annotation.Resource; import org.springframework.beans.BeanUtils; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Controller; import com.tarena.dao.ICostDao; import com.tarena.entity.Cost; /** * 修改保存 */ @Controller @Scope("prototype") public class UpdateCostAction { @Resource private ICostDao costDao; // input private Cost cost; public String update() { #cold_bold // 查询出要修改的原始对象,该对象为持久态 #cold_bold Cost c = costDao.findById(cost.getId()); #cold_bold // 将传入对象的属性复制到原始对象上,忽视不需修改的列 #cold_bold BeanUtils.copyProperties(cost, c, #cold_bold new String[] { "status", "createTime", "startTime" }); #cold_bold /* #cold_bold * 对持久态对象更新,由于映射关系文件中设置了动态更新, #cold_bold * 因此持久对象中值为null的列将不被更新。 #cold_bold * */ #cold_bold costDao.update(c); return "success"; } public Cost getCost() { return cost; } public void setCost(Cost cost) { this.cost = cost; } }
步骤七:测试
重新部署项目并启动tomcat,修改id=4的数据,保存后表中数据如下图。可见这次status、creatime、startime没有被更新为空值。
图-6
本案例的完整代码如下所示:
ICostDao完整代码如下:
package com.tarena.dao; import java.util.List; import com.tarena.entity.Cost; public interface ICostDao { List<Cost> findAll(); /** * 查询某页资费数据 * @param page 页码 * @param pageSize 页容量 * @return */ List<Cost> findByPage(int page, int pageSize); /** * 查询总页数 * @param pageSize * @return */ int findTotalPage(int pageSize); /** * 根据ID查询资费数据 * @param id * @return */ Cost findById(int id); /** * 修改资费数据 * @param cost */ void update(Cost cost); }
CostDaoImpl完整代码如下:
package com.tarena.dao; import java.sql.SQLException; import java.util.List; import javax.annotation.Resource; import org.hibernate.HibernateException; import org.hibernate.Query; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.springframework.orm.hibernate3.HibernateCallback; import org.springframework.orm.hibernate3.support.HibernateDaoSupport; import org.springframework.stereotype.Repository; import com.tarena.entity.Cost; @Repository public class CostDaoImpl extends HibernateDaoSupport implements ICostDao { @Resource public void setSF(SessionFactory sf) { super.setSessionFactory(sf); } @Override public List<Cost> findAll() { String hql = "from Cost"; return getHibernateTemplate().find(hql); } @Override public List<Cost> findByPage(final int page, final int pageSize) { /* * executeFind方法需要传入接口对象,该接口对象的doInHibernate方法 * 会在执行查询时自动被Spring调用,并且doInHibernate方法的返回值 * 将作为最终的结果返回。 * */ return getHibernateTemplate().executeFind(new HibernateCallback() { /* * doInHibernate方法类似于回调函数,是在executeFind方法的内部被调用的。 * 该方法中可以直接使用session,这个session由Spring负责管理,不需要我们 * 创建和关闭。 */ @Override public Object doInHibernate(Session session) throws HibernateException, SQLException { String hql = "from Cost"; Query query = session.createQuery(hql); /* * 设置分页参数,注意在内层函数中调用外层函数的参数, * 要求外层函数的参数必须是final的,因此需要将 * page、pageSize设置为final。 * */ query.setFirstResult((page-1)*pageSize); query.setMaxResults(pageSize); return query.list(); } }); } @Override public int findTotalPage(int pageSize) { String hql = "select count(*) from Cost"; List<Object> list = getHibernateTemplate().find(hql); // 查询出总行数 int rows = Integer.valueOf(list.get(0).toString()); // 根据总行数计算总页数 if (rows % pageSize == 0) return rows / pageSize; else return rows / pageSize + 1; } @Override public Cost findById(int id) { // 使用延迟加载的方法实现 return (Cost) getHibernateTemplate().load(Cost.class, id); } @Override public void update(Cost cost) { if (cost == null) return; getHibernateTemplate().update(cost); } }
UpdateCostAction完整代码如下:
package com.tarena.action; import javax.annotation.Resource; import org.springframework.beans.BeanUtils; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Controller; import com.tarena.dao.ICostDao; import com.tarena.entity.Cost; /** * 修改保存 */ @Controller @Scope("prototype") public class UpdateCostAction { @Resource private ICostDao costDao; // input private Cost cost; public String update() { // 查询出要修改的原始对象,该对象为持久态 Cost c = costDao.findById(cost.getId()); // 将传入对象的属性复制到原始对象上,忽视不需修改的列 BeanUtils.copyProperties(cost, c, new String[] { "status", "createTime", "startTime" }); /* * 对持久态对象更新,由于映射关系文件中设置了动态更新, * 因此持久对象中值为null的列将不被更新。 * */ costDao.update(c); return "success"; } public Cost getCost() { return cost; } public void setCost(Cost cost) { this.cost = cost; } }
struts.xml完整代码如下:
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.1.7//EN" "http://struts.apache.org/dtds/struts-2.1.7.dtd"> <struts> <package name="cost" namespace="/cost" extends="json-default"> <action name="findCost" class="findCostAction" method="load"> <!-- 注入页容量 --> <param name="pageSize">3</param> <result name="success"> /WEB-INF/cost/find_cost.jsp </result> </action> <!-- 打开修改页面 --> <action name="toUpdateCost" class="toUpdateCostAction" method="load"> <result name="success"> /WEB-INF/cost/update_cost.jsp </result> </action> <!-- 修改保存 --> <action name="updateCost" class="updateCostAction" method="update"> <result name="success" type="redirectAction"> findCost </result> </action> </package> </struts>
update_cost.jsp完整代码如下:
<%@page pageEncoding="utf-8"%> <%@taglib uri="/struts-tags" prefix="s"%> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>达内-NetCTOSS</title> <link type="text/css" rel="stylesheet" media="all" href="../styles/global.css" /> <link type="text/css" rel="stylesheet" media="all" href="../styles/global_color.css" /> <script language="javascript" type="text/javascript"> //保存结果的提示 function showResult() { showResultDiv(true); window.setTimeout("showResultDiv(false);", 3000); } function showResultDiv(flag) { var divResult = document.getElementById("save_result_info"); if (flag) divResult.style.display = "block"; else divResult.style.display = "none"; } //切换资费类型 function feeTypeChange(type) { var inputArray = document.getElementById("main").getElementsByTagName("input"); if (type == 1) { inputArray[5].readOnly = true; inputArray[5].value = ""; inputArray[5].className += " readonly"; inputArray[6].readOnly = false; inputArray[6].className = "width100"; inputArray[7].readOnly = true; inputArray[7].className += " readonly"; inputArray[7].value = ""; } else if (type == 2) { inputArray[5].readOnly = false; inputArray[5].className = "width100"; inputArray[6].readOnly = false; inputArray[6].className = "width100"; inputArray[7].readOnly = false; inputArray[7].className = "width100"; } else if (type == 3) { inputArray[5].readOnly = true; inputArray[5].value = ""; inputArray[5].className += " readonly"; inputArray[6].readOnly = true; inputArray[6].value = ""; inputArray[6].className += " readonly"; inputArray[7].readOnly = false; inputArray[7].className = "width100"; } } </script> </head> <body> <!--Logo区域开始--> <div id="header"> <img src="../images/logo.png" alt="logo" class="left"/> <a href="#">[退出]</a> </div> <!--Logo区域结束--> <!--导航区域开始--> <div id="navi"> <ul id="menu"> <li><a href="../index.html" class="index_off"></a></li> <li><a href="../role/role_list.html" class="role_off"></a></li> <li><a href="../admin/admin_list.html" class="admin_off"></a></li> <li><a href="../fee/fee_list.html" class="fee_on"></a></li> <li><a href="../account/account_list.html" class="account_off"></a></li> <li><a href="../service/service_list.html" class="service_off"></a></li> <li><a href="../bill/bill_list.html" class="bill_off"></a></li> <li><a href="../report/report_list.html" class="report_off"></a></li> <li><a href="../user/user_info.html" class="information_off"></a></li> <li><a href="../user/user_modi_pwd.html" class="password_off"></a></li> </ul> </div> <!--导航区域结束--> <!--主要区域开始--> <div id="main"> <div id="save_result_info" class="save_success">保存成功!</div> <form action="updateCost" method="post" class="main_form"> <div class="text_info clearfix"><span>资费ID:</span></div> <div class="input_info"> <s:textfield name="cost.id" cssClass="readonly" readonly="true"/> </div> <div class="text_info clearfix"><span>资费名称:</span></div> <div class="input_info"> <s:textfield name="cost.name" cssClass="width300"/> <span class="required">*</span> <div class="validate_msg_short">50长度的字母、数字、汉字和下划线的组合</div> </div> <div class="text_info clearfix"><span>资费类型:</span></div> <div class="input_info fee_type"> <s:radio name="cost.costType" list="#{'1':'包月','2':'套餐','3':'计时' }" onclick="feeTypeChange(this.value);"/> </div> <div class="text_info clearfix"><span>基本时长:</span></div> <div class="input_info"> <s:textfield name="cost.baseDuration" cssClass="width100"/> <span class="info">小时</span> <span class="required">*</span> <div class="validate_msg_long">1-600之间的整数</div> </div> <div class="text_info clearfix"><span>基本费用:</span></div> <div class="input_info"> <s:textfield name="cost.baseCost" cssClass="width100"/> <span class="info">元</span> <span class="required">*</span> <div class="validate_msg_long">0-99999.99之间的数值</div> </div> <div class="text_info clearfix"><span>单位费用:</span></div> <div class="input_info"> <s:textfield name="cost.unitCost" cssClass="width100"/> <span class="info">元/小时</span> <span class="required">*</span> <div class="validate_msg_long">0-99999.99之间的数值</div> </div> <div class="text_info clearfix"><span>资费说明:</span></div> <div class="input_info_high"> <s:textarea name="cost.descr" cssClass="width300 height70"/> <div class="validate_msg_short">100长度的字母、数字、汉字和下划线的组合</div> </div> <div class="button_info clearfix"> <input type="submit" value="保存" class="btn_save" /> <input type="button" value="取消" class="btn_save" /> </div> </form> </div> <!--主要区域结束--> <div id="footer"> <span>[源自北美的技术,最优秀的师资,最真实的企业环境,最适用的实战项目]</span> <br /> <span>版权所有(C)加拿大达内IT培训集团公司 </span> </div> </body> </html>
Cost.hbm.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> <!-- 通过dynamic-update将当前对象设置为动态更新, 对象中没有发生变化的属性将不会拼入update语句。 --> <class name="com.tarena.entity.Cost" table="cost" dynamic-update="true"> <id name="id" type="integer" column="id"> <!-- 用来指明主键的生成方式 --> <generator class="sequence"> <param name="sequence">cost_seq</param> </generator> </id> <property name="name" type="string" column="name" /> <property name="baseDuration" type="integer" column="base_duration" /> <property name="baseCost" type="double" column="base_cost" /> <property name="unitCost" type="double" column="unit_cost" /> <property name="status" type="string" column="status" /> <property name="descr" type="string" column="descr" /> <property name="createTime" type="date" column="creatime" /> <property name="startTime" type="date" column="startime" /> <property name="costType" type="string" column="cost_type" /> </class> </hibernate-mapping>
完成资费新增功能。
资费新增和资费修改十分相似,不同点在于新增页面不需要默认显示数据,因此该功能可以参考资费修改完成。
实现此案例需要按照如下步骤进行。
步骤一:配置资费新增Action
由于资费新增Action没有任何业务逻辑,因此不需要创建Action类,只需要直接在struts.xml配置即可。运行时Struts2会自动实例化AcionSupport类并调用默认的execute业务方法,该业务方法中返回了字符串”success”,根据该返回值配置好对应的新增页面即可,代码如下:
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.1.7//EN" "http://struts.apache.org/dtds/struts-2.1.7.dtd"> <struts> <package name="cost" namespace="/cost" extends="json-default"> <action name="findCost" class="findCostAction" method="load"> <!-- 注入页容量 --> <param name="pageSize">3</param> <result name="success"> /WEB-INF/cost/find_cost.jsp </result> </action> <!-- 打开修改页面 --> <action name="toUpdateCost" class="toUpdateCostAction" method="load"> <result name="success"> /WEB-INF/cost/update_cost.jsp </result> </action> <!-- 修改保存 --> <action name="updateCost" class="updateCostAction" method="update"> <result name="success" type="redirectAction"> findCost </result> </action> #cold_bold <!-- 打开新增页面 --> #cold_bold <action name="toAddCost"> #cold_bold <result name="success"> #cold_bold /WEB-INF/cost/add_cost.jsp #cold_bold </result> #cold_bold </action> </package> </struts>
步骤二:创建新增页面
在WEB-INF/cost文件夹下,创建新增资费页面add_cost.jsp,代码如下:
<%@page pageEncoding="utf-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>达内-NetCTOSS</title> <link type="text/css" rel="stylesheet" media="all" href="../styles/global.css" /> <link type="text/css" rel="stylesheet" media="all" href="../styles/global_color.css" /> <script language="javascript" type="text/javascript"> //保存结果的提示 function showResult() { showResultDiv(true); window.setTimeout("showResultDiv(false);", 3000); } function showResultDiv(flag) { var divResult = document.getElementById("save_result_info"); if (flag) divResult.style.display = "block"; else divResult.style.display = "none"; } //切换资费类型 function feeTypeChange(type) { var inputArray = document.getElementById("main").getElementsByTagName("input"); if (type == 1) { inputArray[4].readOnly = true; inputArray[4].value = ""; inputArray[4].className += " readonly"; inputArray[5].readOnly = false; inputArray[5].className = "width100"; inputArray[6].readOnly = true; inputArray[6].className += " readonly"; inputArray[6].value = ""; } else if (type == 2) { inputArray[4].readOnly = false; inputArray[4].className = "width100"; inputArray[5].readOnly = false; inputArray[5].className = "width100"; inputArray[6].readOnly = false; inputArray[6].className = "width100"; } else if (type == 3) { inputArray[4].readOnly = true; inputArray[4].value = ""; inputArray[4].className += " readonly"; inputArray[5].readOnly = true; inputArray[5].value = ""; inputArray[5].className += " readonly"; inputArray[6].readOnly = false; inputArray[6].className = "width100"; } } </script> </head> <body> <!--Logo区域开始--> <div id="header"> <img src="../images/logo.png" alt="logo" class="left"/> <a href="#">[退出]</a> </div> <!--Logo区域结束--> <!--导航区域开始--> <!--导航区域结束--> <!--主要区域开始--> <div id="main"> <div id="save_result_info" class="save_fail">保存失败,资费名称重复!</div> <form action="" method="" class="main_form"> <div class="text_info clearfix"><span>资费名称:</span></div> <div class="input_info"> <input type="text" class="width300" value=""/> <span class="required">*</span> <div class="validate_msg_short">50长度的字母、数字、汉字和下划线的组合</div> </div> <div class="text_info clearfix"><span>资费类型:</span></div> <div class="input_info fee_type"> <input type="radio" name="radFeeType" id="monthly" onclick="feeTypeChange(1);" /> <label for="monthly">包月</label> <input type="radio" name="radFeeType" checked="checked" id="package" onclick="feeTypeChange(2);" /> <label for="package">套餐</label> <input type="radio" name="radFeeType" id="timeBased" onclick="feeTypeChange(3);" /> <label for="timeBased">计时</label> </div> <div class="text_info clearfix"><span>基本时长:</span></div> <div class="input_info"> <input type="text" value="" class="width100" /> <span class="info">小时</span> <span class="required">*</span> <div class="validate_msg_long">1-600之间的整数</div> </div> <div class="text_info clearfix"><span>基本费用:</span></div> <div class="input_info"> <input type="text" value="" class="width100" /> <span class="info">元</span> <span class="required">*</span> <div class="validate_msg_long error_msg">0-99999.99之间的数值</div> </div> <div class="text_info clearfix"><span>单位费用:</span></div> <div class="input_info"> <input type="text" value="" class="width100" /> <span class="info">元/小时</span> <span class="required">*</span> <div class="validate_msg_long error_msg">0-99999.99之间的数值</div> </div> <div class="text_info clearfix"><span>资费说明:</span></div> <div class="input_info_high"> <textarea class="width300 height70"></textarea> <div class="validate_msg_short error_msg">100长度的字母、数字、汉字和下划线的组合</div> </div> <div class="button_info clearfix"> <input type="button" value="保存" class="btn_save" onclick="showResult();" /> <input type="button" value="取消" class="btn_save" /> </div> </form> </div> <!--主要区域结束--> <div id="footer"> <span>[源自北美的技术,最优秀的师资,最真实的企业环境,最适用的实战项目]</span> <br /> <span>版权所有(C)加拿大达内IT培训集团公司 </span> </div> </body> </html>
步骤三:处理新增按钮
在资费列表页面find_cost.jsp上,修改新增按钮,将其URL指定为资费新增Action,代码如下:
<%@page pageEncoding="utf-8" isELIgnored="false"%> <%@taglib uri="/struts-tags" prefix="s"%> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>达内-NetCTOSS</title> <link type="text/css" rel="stylesheet" media="all" href="../styles/global.css" /> <link type="text/css" rel="stylesheet" media="all" href="../styles/global_color.css" /> <script language="javascript" type="text/javascript"> //排序按钮的点击事件 function sort(btnObj) { if (btnObj.className == "sort_desc") btnObj.className = "sort_asc"; else btnObj.className = "sort_desc"; } //启用 function startFee() { var r = window.confirm("确定要启用此资费吗?资费启用后将不能修改和删除。"); document.getElementById("operate_result_info").style.display = "block"; } //删除 function deleteFee() { var r = window.confirm("确定要删除此资费吗?"); document.getElementById("operate_result_info").style.display = "block"; } </script> </head> <body> <!--Logo区域开始--> <div id="header"> <img src="../images/logo.png" alt="logo" class="left"/> <a href="#">[退出]</a> </div> <!--Logo区域结束--> <!--导航区域开始--> <div id="navi"> <ul id="menu"> <li><a href="../index.html" class="index_off"></a></li> <li><a href="../role/role_list.html" class="role_off"></a></li> <li><a href="../admin/admin_list.html" class="admin_off"></a></li> <li><a href="../fee/fee_list.html" class="fee_on"></a></li> <li><a href="../account/account_list.html" class="account_off"></a></li> <li><a href="../service/service_list.html" class="service_off"></a></li> <li><a href="../bill/bill_list.html" class="bill_off"></a></li> <li><a href="../report/report_list.html" class="report_off"></a></li> <li><a href="../user/user_info.html" class="information_off"></a></li> <li><a href="../user/user_modi_pwd.html" class="password_off"></a></li> </ul> </div> <!--导航区域结束--> <!--主要区域开始--> <div id="main"> <form action="" method=""> <!--排序--> <div class="search_add"> <div> <!--<input type="button" value="月租" class="sort_asc" onclick="sort(this);" />--> <input type="button" value="基费" class="sort_asc" onclick="sort(this);" /> <input type="button" value="时长" class="sort_asc" onclick="sort(this);" /> </div> #cold_bold <input type="button" value="增加" class="btn_add" onclick="location.href='toAddCost';" /> </div> <!--启用操作的操作提示--> <div id="operate_result_info" class="operate_success"> <img src="../images/close.png" onclick="this.parentNode.style.display='none';" /> 删除成功! </div> <!--数据区域:用表格展示数据--> <div id="data"> <table id="datalist"> <tr> <th>资费ID</th> <th class="width100">资费名称</th> <th>基本时长</th> <th>基本费用</th> <th>单位费用</th> <th>创建时间</th> <th>开通时间</th> <th class="width50">状态</th> <th class="width200"></th> </tr> <!-- 使用Struts2标签遍历集合,使用OGNL表达式输出内容。 --> <s:iterator value="costs"> <tr> <td><s:property value="id"/></td> <td><a href="fee_detail.html"><s:property value="name"/></a></td> <td><s:property value="baseDuration"/></td> <td><s:property value="baseCost"/></td> <td><s:property value="unitCost"/></td> <td><s:property value="createTime"/></td> <td><s:property value="startTime"/></td> <td> <s:if test="status==0">开通</s:if> <s:else>暂停</s:else> </td> <td> <input type="button" value="启用" class="btn_start" onclick="startFee();" /> <input type="button" value="修改" class="btn_modify" onclick="location.href='toUpdateCost?id=<s:property value="id"/>';" /> <input type="button" value="删除" class="btn_delete" onclick="deleteFee();" /> </td> </tr> </s:iterator> </table> <p>业务说明:<br /> 1、创建资费时,状态为暂停,记载创建时间;<br /> 2、暂停状态下,可修改,可删除;<br /> 3、开通后,记载开通时间,且开通后不能修改、不能再停用、也不能删除;<br /> 4、业务账号修改资费时,在下月底统一触发,修改其关联的资费ID(此触发动作由程序处理) </p> </div> <!--分页--> <div id="pages"> <s:if test="page==1"> <a href="#">上一页</a> </s:if> <s:else> <a href="findCost?page=<s:property value='page-1'/>">上一页</a> </s:else> <s:iterator begin="1" end="totalPage" var="p"> <s:if test="#p==page"> <a href="findCost?page=<s:property value="#p"/>" class="current_page"><s:property value="#p"/></a> </s:if> <s:else> <a href="findCost?page=<s:property value="#p"/>"><s:property value="#p"/></a> </s:else> </s:iterator> <s:if test="page==totalPage"> <a href="#">下一页</a> </s:if> <s:else> <a href="findCost?page=<s:property value='page+1'/>">下一页</a> </s:else> </div> </form> </div> <!--主要区域结束--> <div id="footer"> <p>[源自北美的技术,最优秀的师资,最真实的企业环境,最适用的实战项目]</p> <p>版权所有(C)加拿大达内IT培训集团公司 </p> </div> </body> </html>
步骤四:阶段性测试
重新部署项目并启动tomcat,访问资费列表页面,点击新增按钮,跳转后的页面如下图所示:
图-7
步骤五:DAO中增加新增保存方法
在ICostDao中增加新增保存的方法,代码如下:
package com.tarena.dao; import java.util.List; import com.tarena.entity.Cost; public interface ICostDao { List<Cost> findAll(); /** * 查询某页资费数据 * @param page 页码 * @param pageSize 页容量 * @return */ List<Cost> findByPage(int page, int pageSize); /** * 查询总页数 * @param pageSize * @return */ int findTotalPage(int pageSize); /** * 根据ID查询资费数据 * @param id * @return */ Cost findById(int id); /** * 修改资费数据 * @param cost */ void update(Cost cost); #cold_bold /** #cold_bold * 新增资费数据 #cold_bold * @param cost #cold_bold */ #cold_bold void save(Cost cost); }
在CostDaoImpl中实现这个方法,代码如下:
package com.tarena.dao; import java.sql.SQLException; import java.util.List; import javax.annotation.Resource; import org.hibernate.HibernateException; import org.hibernate.Query; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.springframework.orm.hibernate3.HibernateCallback; import org.springframework.orm.hibernate3.support.HibernateDaoSupport; import org.springframework.stereotype.Repository; import com.tarena.entity.Cost; @Repository public class CostDaoImpl extends HibernateDaoSupport implements ICostDao { @Resource public void setSF(SessionFactory sf) { super.setSessionFactory(sf); } @Override public List<Cost> findAll() { String hql = "from Cost"; return getHibernateTemplate().find(hql); } @Override public List<Cost> findByPage(final int page, final int pageSize) { /* * executeFind方法需要传入接口对象,该接口对象的doInHibernate方法 * 会在执行查询时自动被Spring调用,并且doInHibernate方法的返回值 * 将作为最终的结果返回。 * */ return getHibernateTemplate().executeFind(new HibernateCallback() { /* * doInHibernate方法类似于回调函数,是在executeFind方法的内部被调用的。 * 该方法中可以直接使用session,这个session由Spring负责管理,不需要我们 * 创建和关闭。 */ @Override public Object doInHibernate(Session session) throws HibernateException, SQLException { String hql = "from Cost"; Query query = session.createQuery(hql); /* * 设置分页参数,注意在内层函数中调用外层函数的参数, * 要求外层函数的参数必须是final的,因此需要将 * page、pageSize设置为final。 * */ query.setFirstResult((page-1)*pageSize); query.setMaxResults(pageSize); return query.list(); } }); } @Override public int findTotalPage(int pageSize) { String hql = "select count(*) from Cost"; List<Object> list = getHibernateTemplate().find(hql); // 查询出总行数 int rows = Integer.valueOf(list.get(0).toString()); // 根据总行数计算总页数 if (rows % pageSize == 0) return rows / pageSize; else return rows / pageSize + 1; } @Override public Cost findById(int id) { // 使用延迟加载的方法实现 return (Cost) getHibernateTemplate().load(Cost.class, id); } @Override public void update(Cost cost) { if (cost == null) return; getHibernateTemplate().update(cost); } #cold_bold @Override #cold_bold public void save(Cost cost) { #cold_bold if(cost == null) #cold_bold return; #cold_bold getHibernateTemplate().save(cost); #cold_bold } }
步骤六:创建新增保存Action
在com.tarena.action包下,创建新增保存Action,代码如下:
package com.tarena.action; import java.sql.Date; import javax.annotation.Resource; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Controller; import com.tarena.dao.ICostDao; import com.tarena.entity.Cost; /** * 新增保存资费数据 */ @Controller @Scope("prototype") public class AddCostAction { @Resource private ICostDao costDao; // input private Cost cost; public String add() { // 初始化一些默认值 initDefaultValue(cost); costDao.save(cost); return "success"; } /** * 初始化默认值 */ private void initDefaultValue(Cost cost) { // 状态默认为暂停态 cost.setStatus("1"); // 创建时间默认为当前系统时间 cost.setCreateTime( new Date(System.currentTimeMillis())); } public Cost getCost() { return cost; } public void setCost(Cost cost) { this.cost = cost; } }
步骤七:配置新增保存Action
在struts.xml 中配置这个Action,代码如下:
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.1.7//EN" "http://struts.apache.org/dtds/struts-2.1.7.dtd"> <struts> <package name="cost" namespace="/cost" extends="json-default"> <action name="findCost" class="findCostAction" method="load"> <!-- 注入页容量 --> <param name="pageSize">3</param> <result name="success"> /WEB-INF/cost/find_cost.jsp </result> </action> <!-- 打开修改页面 --> <action name="toUpdateCost" class="toUpdateCostAction" method="load"> <result name="success"> /WEB-INF/cost/update_cost.jsp </result> </action> <!-- 修改保存 --> <action name="updateCost" class="updateCostAction" method="update"> <result name="success" type="redirectAction"> findCost </result> </action> <!-- 打开新增页面 --> <action name="toAddCost"> <result name="success"> /WEB-INF/cost/add_cost.jsp </result> </action> #cold_bold <!-- 新增保存 --> #cold_bold <action name="addCost" #cold_bold class="addCostAction" method="add"> #cold_bold <result name="success" type="redirectAction"> #cold_bold findCost #cold_bold </result> #cold_bold </action> </package> </struts>
步骤八:修改新增页面的表单
修改add_cost.jsp,配置好表单、框体名称以及保存按钮,代码如下:
<%@page pageEncoding="utf-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>达内-NetCTOSS</title> <link type="text/css" rel="stylesheet" media="all" href="../styles/global.css" /> <link type="text/css" rel="stylesheet" media="all" href="../styles/global_color.css" /> <script language="javascript" type="text/javascript"> //保存结果的提示 function showResult() { showResultDiv(true); window.setTimeout("showResultDiv(false);", 3000); } function showResultDiv(flag) { var divResult = document.getElementById("save_result_info"); if (flag) divResult.style.display = "block"; else divResult.style.display = "none"; } //切换资费类型 function feeTypeChange(type) { var inputArray = document.getElementById("main").getElementsByTagName("input"); if (type == 1) { inputArray[4].readOnly = true; inputArray[4].value = ""; inputArray[4].className += " readonly"; inputArray[5].readOnly = false; inputArray[5].className = "width100"; inputArray[6].readOnly = true; inputArray[6].className += " readonly"; inputArray[6].value = ""; } else if (type == 2) { inputArray[4].readOnly = false; inputArray[4].className = "width100"; inputArray[5].readOnly = false; inputArray[5].className = "width100"; inputArray[6].readOnly = false; inputArray[6].className = "width100"; } else if (type == 3) { inputArray[4].readOnly = true; inputArray[4].value = ""; inputArray[4].className += " readonly"; inputArray[5].readOnly = true; inputArray[5].value = ""; inputArray[5].className += " readonly"; inputArray[6].readOnly = false; inputArray[6].className = "width100"; } } </script> </head> <body> <!--Logo区域开始--> <div id="header"> <img src="../images/logo.png" alt="logo" class="left"/> <a href="#">[退出]</a> </div> <!--Logo区域结束--> <!--导航区域开始--> <!--导航区域结束--> <!--主要区域开始--> <div id="main"> <div id="save_result_info" class="save_fail">保存失败,资费名称重复!</div> #cold_bold <form action="addCost" method="post" class="main_form"> <div class="text_info clearfix"><span>资费名称:</span></div> <div class="input_info"> #cold_bold <input type="text" class="width300" name="cost.name"/> <span class="required">*</span> <div class="validate_msg_short">50长度的字母、数字、汉字和下划线的组合</div> </div> <div class="text_info clearfix"><span>资费类型:</span></div> <div class="input_info fee_type"> #cold_bold <input type="radio" name="cost.costType" value="1" id="monthly" onclick="feeTypeChange(1);" /> #cold_bold <label for="monthly">包月</label> #cold_bold <input type="radio" name="cost.costType" value="2" checked="checked" id="package" onclick="feeTypeChange(2);" /> #cold_bold <label for="package">套餐</label> #cold_bold <input type="radio" name="cost.costType" value="3" id="timeBased" onclick="feeTypeChange(3);" /> #cold_bold <label for="timeBased">计时</label> </div> <div class="text_info clearfix"><span>基本时长:</span></div> <div class="input_info"> #cold_bold <input type="text" name="cost.baseDuration" class="width100" /> <span class="info">小时</span> <span class="required">*</span> <div class="validate_msg_long">1-600之间的整数</div> </div> <div class="text_info clearfix"><span>基本费用:</span></div> <div class="input_info"> #cold_bold <input type="text" name="cost.baseCost" class="width100" /> <span class="info">元</span> <span class="required">*</span> <div class="validate_msg_long error_msg">0-99999.99之间的数值</div> </div> <div class="text_info clearfix"><span>单位费用:</span></div> <div class="input_info"> #cold_bold <input type="text" name="cost.unitCost" class="width100" /> <span class="info">元/小时</span> <span class="required">*</span> <div class="validate_msg_long error_msg">0-99999.99之间的数值</div> </div> <div class="text_info clearfix"><span>资费说明:</span></div> <div class="input_info_high"> #cold_bold <textarea class="width300 height70" name="cost.descr"></textarea> <div class="validate_msg_short error_msg">100长度的字母、数字、汉字和下划线的组合</div> </div> <div class="button_info clearfix"> #cold_bold <input type="submit" value="保存" class="btn_save" /> <input type="button" value="取消" class="btn_save" /> </div> </form> </div> <!--主要区域结束--> <div id="footer"> <span>[源自北美的技术,最优秀的师资,最真实的企业环境,最适用的实战项目]</span> <br /> <span>版权所有(C)加拿大达内IT培训集团公司 </span> </div> </body> </html>
步骤九:测试
重新部署项目并启动tomcat,访问新增页面,输入一些内容点击保存,成功后重定向到列表页面,可以看到列表页面上多了一条新增的数据(id=360),效果如下图:
图-8
本案例的完整代码如下所示:
ICostDao完整代码如下:
package com.tarena.dao; import java.util.List; import com.tarena.entity.Cost; public interface ICostDao { List<Cost> findAll(); /** * 查询某页资费数据 * @param page 页码 * @param pageSize 页容量 * @return */ List<Cost> findByPage(int page, int pageSize); /** * 查询总页数 * @param pageSize * @return */ int findTotalPage(int pageSize); /** * 根据ID查询资费数据 * @param id * @return */ Cost findById(int id); /** * 修改资费数据 * @param cost */ void update(Cost cost); /** * 新增资费数据 * @param cost */ void save(Cost cost); }
CostDaoImpl完整代码如下:
package com.tarena.dao; import java.sql.SQLException; import java.util.List; import javax.annotation.Resource; import org.hibernate.HibernateException; import org.hibernate.Query; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.springframework.orm.hibernate3.HibernateCallback; import org.springframework.orm.hibernate3.support.HibernateDaoSupport; import org.springframework.stereotype.Repository; import com.tarena.entity.Cost; @Repository public class CostDaoImpl extends HibernateDaoSupport implements ICostDao { @Resource public void setSF(SessionFactory sf) { super.setSessionFactory(sf); } @Override public List<Cost> findAll() { String hql = "from Cost"; return getHibernateTemplate().find(hql); } @Override public List<Cost> findByPage(final int page, final int pageSize) { /* * executeFind方法需要传入接口对象,该接口对象的doInHibernate方法 * 会在执行查询时自动被Spring调用,并且doInHibernate方法的返回值 * 将作为最终的结果返回。 * */ return getHibernateTemplate().executeFind(new HibernateCallback() { /* * doInHibernate方法类似于回调函数,是在executeFind方法的内部被调用的。 * 该方法中可以直接使用session,这个session由Spring负责管理,不需要我们 * 创建和关闭。 */ @Override public Object doInHibernate(Session session) throws HibernateException, SQLException { String hql = "from Cost"; Query query = session.createQuery(hql); /* * 设置分页参数,注意在内层函数中调用外层函数的参数, * 要求外层函数的参数必须是final的,因此需要将 * page、pageSize设置为final。 * */ query.setFirstResult((page-1)*pageSize); query.setMaxResults(pageSize); return query.list(); } }); } @Override public int findTotalPage(int pageSize) { String hql = "select count(*) from Cost"; List<Object> list = getHibernateTemplate().find(hql); // 查询出总行数 int rows = Integer.valueOf(list.get(0).toString()); // 根据总行数计算总页数 if (rows % pageSize == 0) return rows / pageSize; else return rows / pageSize + 1; } @Override public Cost findById(int id) { // 使用延迟加载的方法实现 return (Cost) getHibernateTemplate().load(Cost.class, id); } @Override public void update(Cost cost) { if (cost == null) return; getHibernateTemplate().update(cost); } @Override public void save(Cost cost) { if(cost == null) return; getHibernateTemplate().save(cost); } }
AddCostAction完整代码如下:
package com.tarena.action; import java.sql.Date; import javax.annotation.Resource; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Controller; import com.tarena.dao.ICostDao; import com.tarena.entity.Cost; /** * 新增保存资费数据 */ @Controller @Scope("prototype") public class AddCostAction { @Resource private ICostDao costDao; // input private Cost cost; public String add() { // 初始化一些默认值 initDefaultValue(cost); costDao.save(cost); return "success"; } /** * 初始化默认值 */ private void initDefaultValue(Cost cost) { // 状态默认为暂停态 cost.setStatus("1"); // 创建时间默认为当前系统时间 cost.setCreateTime( new Date(System.currentTimeMillis())); } public Cost getCost() { return cost; } public void setCost(Cost cost) { this.cost = cost; } }
struts.xml完整代码如下:
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.1.7//EN" "http://struts.apache.org/dtds/struts-2.1.7.dtd"> <struts> <package name="cost" namespace="/cost" extends="json-default"> <action name="findCost" class="findCostAction" method="load"> <!-- 注入页容量 --> <param name="pageSize">3</param> <result name="success"> /WEB-INF/cost/find_cost.jsp </result> </action> <!-- 打开修改页面 --> <action name="toUpdateCost" class="toUpdateCostAction" method="load"> <result name="success"> /WEB-INF/cost/update_cost.jsp </result> </action> <!-- 修改保存 --> <action name="updateCost" class="updateCostAction" method="update"> <result name="success" type="redirectAction"> findCost </result> </action> <!-- 打开新增页面 --> <action name="toAddCost"> <result name="success"> /WEB-INF/cost/add_cost.jsp </result> </action> <!-- 新增保存 --> <action name="addCost" class="addCostAction" method="add"> <result name="success" type="redirectAction"> findCost </result> </action> </package> </struts>
add_cost.jsp完整代码如下:
<%@page pageEncoding="utf-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>达内-NetCTOSS</title> <link type="text/css" rel="stylesheet" media="all" href="../styles/global.css" /> <link type="text/css" rel="stylesheet" media="all" href="../styles/global_color.css" /> <script language="javascript" type="text/javascript"> //保存结果的提示 function showResult() { showResultDiv(true); window.setTimeout("showResultDiv(false);", 3000); } function showResultDiv(flag) { var divResult = document.getElementById("save_result_info"); if (flag) divResult.style.display = "block"; else divResult.style.display = "none"; } //切换资费类型 function feeTypeChange(type) { var inputArray = document.getElementById("main").getElementsByTagName("input"); if (type == 1) { inputArray[4].readOnly = true; inputArray[4].value = ""; inputArray[4].className += " readonly"; inputArray[5].readOnly = false; inputArray[5].className = "width100"; inputArray[6].readOnly = true; inputArray[6].className += " readonly"; inputArray[6].value = ""; } else if (type == 2) { inputArray[4].readOnly = false; inputArray[4].className = "width100"; inputArray[5].readOnly = false; inputArray[5].className = "width100"; inputArray[6].readOnly = false; inputArray[6].className = "width100"; } else if (type == 3) { inputArray[4].readOnly = true; inputArray[4].value = ""; inputArray[4].className += " readonly"; inputArray[5].readOnly = true; inputArray[5].value = ""; inputArray[5].className += " readonly"; inputArray[6].readOnly = false; inputArray[6].className = "width100"; } } </script> </head> <body> <!--Logo区域开始--> <div id="header"> <img src="../images/logo.png" alt="logo" class="left"/> <a href="#">[退出]</a> </div> <!--Logo区域结束--> <!--导航区域开始--> <!--导航区域结束--> <!--主要区域开始--> <div id="main"> <div id="save_result_info" class="save_fail">保存失败,资费名称重复!</div> <form action="addCost" method="post" class="main_form"> <div class="text_info clearfix"><span>资费名称:</span></div> <div class="input_info"> <input type="text" class="width300" name="cost.name"/> <span class="required">*</span> <div class="validate_msg_short">50长度的字母、数字、汉字和下划线的组合</div> </div> <div class="text_info clearfix"><span>资费类型:</span></div> <div class="input_info fee_type"> <input type="radio" name="cost.costType" value="1" id="monthly" onclick="feeTypeChange(1);" /> <label for="monthly">包月</label> <input type="radio" name="cost.costType" value="2" checked="checked" id="package" onclick="feeTypeChange(2);" /> <label for="package">套餐</label> <input type="radio" name="cost.costType" value="3" id="timeBased" onclick="feeTypeChange(3);" /> <label for="timeBased">计时</label> </div> <div class="text_info clearfix"><span>基本时长:</span></div> <div class="input_info"> <input type="text" name="cost.baseDuration" class="width100" /> <span class="info">小时</span> <span class="required">*</span> <div class="validate_msg_long">1-600之间的整数</div> </div> <div class="text_info clearfix"><span>基本费用:</span></div> <div class="input_info"> <input type="text" name="cost.baseCost" class="width100" /> <span class="info">元</span> <span class="required">*</span> <div class="validate_msg_long error_msg">0-99999.99之间的数值</div> </div> <div class="text_info clearfix"><span>单位费用:</span></div> <div class="input_info"> <input type="text" name="cost.unitCost" class="width100" /> <span class="info">元/小时</span> <span class="required">*</span> <div class="validate_msg_long error_msg">0-99999.99之间的数值</div> </div> <div class="text_info clearfix"><span>资费说明:</span></div> <div class="input_info_high"> <textarea class="width300 height70" name="cost.descr"></textarea> <div class="validate_msg_short error_msg">100长度的字母、数字、汉字和下划线的组合</div> </div> <div class="button_info clearfix"> <input type="submit" value="保存" class="btn_save" /> <input type="button" value="取消" class="btn_save" /> </div> </form> </div> <!--主要区域结束--> <div id="footer"> <span>[源自北美的技术,最优秀的师资,最真实的企业环境,最适用的实战项目]</span> <br /> <span>版权所有(C)加拿大达内IT培训集团公司 </span> </div> </body> </html>
完成资费删除功能。
资费删除功能比较简单,在页面上选中一条资费,将ID传入Action,然后调用DAO使用Hibernate删除这条数据即可。
实现此案例需要按照如下步骤进行。
步骤一:DAO中增加删除方法
在ICostDao中增加资费删除方法,代码如下:
package com.tarena.dao; import java.util.List; import com.tarena.entity.Cost; public interface ICostDao { List<Cost> findAll(); /** * 查询某页资费数据 * @param page 页码 * @param pageSize 页容量 * @return */ List<Cost> findByPage(int page, int pageSize); /** * 查询总页数 * @param pageSize * @return */ int findTotalPage(int pageSize); /** * 根据ID查询资费数据 * @param id * @return */ Cost findById(int id); /** * 修改资费数据 * @param cost */ void update(Cost cost); /** * 新增资费数据 * @param cost */ void save(Cost cost); #cold_bold /** #cold_bold * 删除资费数据 #cold_bold * @param id #cold_bold */ #cold_bold void delete(int id); }
在CostDaoImpl中实现这个方法,代码如下:
package com.tarena.dao; import java.sql.SQLException; import java.util.List; import javax.annotation.Resource; import org.hibernate.HibernateException; import org.hibernate.Query; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.springframework.orm.hibernate3.HibernateCallback; import org.springframework.orm.hibernate3.support.HibernateDaoSupport; import org.springframework.stereotype.Repository; import com.tarena.entity.Cost; @Repository public class CostDaoImpl extends HibernateDaoSupport implements ICostDao { @Resource public void setSF(SessionFactory sf) { super.setSessionFactory(sf); } @Override public List<Cost> findAll() { String hql = "from Cost"; return getHibernateTemplate().find(hql); } @Override public List<Cost> findByPage(final int page, final int pageSize) { /* * executeFind方法需要传入接口对象,该接口对象的doInHibernate方法 * 会在执行查询时自动被Spring调用,并且doInHibernate方法的返回值 * 将作为最终的结果返回。 * */ return getHibernateTemplate().executeFind(new HibernateCallback() { /* * doInHibernate方法类似于回调函数,是在executeFind方法的内部被调用的。 * 该方法中可以直接使用session,这个session由Spring负责管理,不需要我们 * 创建和关闭。 */ @Override public Object doInHibernate(Session session) throws HibernateException, SQLException { String hql = "from Cost"; Query query = session.createQuery(hql); /* * 设置分页参数,注意在内层函数中调用外层函数的参数, * 要求外层函数的参数必须是final的,因此需要将 * page、pageSize设置为final。 * */ query.setFirstResult((page-1)*pageSize); query.setMaxResults(pageSize); return query.list(); } }); } @Override public int findTotalPage(int pageSize) { String hql = "select count(*) from Cost"; List<Object> list = getHibernateTemplate().find(hql); // 查询出总行数 int rows = Integer.valueOf(list.get(0).toString()); // 根据总行数计算总页数 if (rows % pageSize == 0) return rows / pageSize; else return rows / pageSize + 1; } @Override public Cost findById(int id) { // 使用延迟加载的方法实现 return (Cost) getHibernateTemplate().load(Cost.class, id); } @Override public void update(Cost cost) { if (cost == null) return; getHibernateTemplate().update(cost); } @Override public void save(Cost cost) { if(cost == null) return; getHibernateTemplate().save(cost); } #cold_bold @Override #cold_bold public void delete(int id) { #cold_bold Cost cost = new Cost(); #cold_bold cost.setId(id); #cold_bold getHibernateTemplate().delete(cost); #cold_bold } }
步骤二:创建资费删除Action
在com.tarena.action包下,创建资费删除Action,代码如下:
package com.tarena.action; import javax.annotation.Resource; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Controller; import com.tarena.dao.ICostDao; /** * 删除资费数据 */ @Controller @Scope("prototype") public class DeleteCostAction { @Resource private ICostDao costDao; // input private int id; public String delete() { costDao.delete(id); return "success"; } public int getId() { return id; } public void setId(int id) { this.id = id; } }
步骤三:配置资费删除Action
在struts.xml中,配置资费删除Action,代码如下:
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.1.7//EN" "http://struts.apache.org/dtds/struts-2.1.7.dtd"> <struts> <package name="cost" namespace="/cost" extends="json-default"> <action name="findCost" class="findCostAction" method="load"> <!-- 注入页容量 --> <param name="pageSize">3</param> <result name="success"> /WEB-INF/cost/find_cost.jsp </result> </action> <!-- 打开修改页面 --> <action name="toUpdateCost" class="toUpdateCostAction" method="load"> <result name="success"> /WEB-INF/cost/update_cost.jsp </result> </action> <!-- 修改保存 --> <action name="updateCost" class="updateCostAction" method="update"> <result name="success" type="redirectAction"> findCost </result> </action> <!-- 打开新增页面 --> <action name="toAddCost"> <result name="success"> /WEB-INF/cost/add_cost.jsp </result> </action> <!-- 新增保存 --> <action name="addCost" class="addCostAction" method="add"> <result name="success" type="redirectAction"> findCost </result> </action> #cold_bold <!-- 删除 --> #cold_bold <action name="deleteCost" #cold_bold class="deleteCostAction" method="delete"> #cold_bold <result name="success" type="redirectAction"> #cold_bold findCost #cold_bold </result> #cold_bold </action> </package> </struts>
步骤四:处理删除按钮
在find_cost.jsp中,将删除按钮的URL指向资费删除Action,代码如下:
<%@page pageEncoding="utf-8" isELIgnored="false"%> <%@taglib uri="/struts-tags" prefix="s"%> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>达内-NetCTOSS</title> <link type="text/css" rel="stylesheet" media="all" href="../styles/global.css" /> <link type="text/css" rel="stylesheet" media="all" href="../styles/global_color.css" /> <script language="javascript" type="text/javascript"> //排序按钮的点击事件 function sort(btnObj) { if (btnObj.className == "sort_desc") btnObj.className = "sort_asc"; else btnObj.className = "sort_desc"; } //启用 function startFee() { var r = window.confirm("确定要启用此资费吗?资费启用后将不能修改和删除。"); document.getElementById("operate_result_info").style.display = "block"; } #cold_bold //删除 #cold_bold function deleteFee(id) { #cold_bold var r = window.confirm("确定要删除此资费吗?"); #cold_bold if(r) { #cold_bold window.location.href = "deleteCost?id="+id; #cold_bold } #cold_bold } </script> </head> <body> <!--Logo区域开始--> <div id="header"> <img src="../images/logo.png" alt="logo" class="left"/> <a href="#">[退出]</a> </div> <!--Logo区域结束--> <!--导航区域开始--> <div id="navi"> <ul id="menu"> <li><a href="../index.html" class="index_off"></a></li> <li><a href="../role/role_list.html" class="role_off"></a></li> <li><a href="../admin/admin_list.html" class="admin_off"></a></li> <li><a href="../fee/fee_list.html" class="fee_on"></a></li> <li><a href="../account/account_list.html" class="account_off"></a></li> <li><a href="../service/service_list.html" class="service_off"></a></li> <li><a href="../bill/bill_list.html" class="bill_off"></a></li> <li><a href="../report/report_list.html" class="report_off"></a></li> <li><a href="../user/user_info.html" class="information_off"></a></li> <li><a href="../user/user_modi_pwd.html" class="password_off"></a></li> </ul> </div> <!--导航区域结束--> <!--主要区域开始--> <div id="main"> <form action="" method=""> <!--排序--> <div class="search_add"> <div> <!--<input type="button" value="月租" class="sort_asc" onclick="sort(this);" />--> <input type="button" value="基费" class="sort_asc" onclick="sort(this);" /> <input type="button" value="时长" class="sort_asc" onclick="sort(this);" /> </div> <input type="button" value="增加" class="btn_add" onclick="location.href='toAddCost';" /> </div> <!--启用操作的操作提示--> <div id="operate_result_info" class="operate_success"> <img src="../images/close.png" onclick="this.parentNode.style.display='none';" /> 删除成功! </div> <!--数据区域:用表格展示数据--> <div id="data"> <table id="datalist"> <tr> <th>资费ID</th> <th class="width100">资费名称</th> <th>基本时长</th> <th>基本费用</th> <th>单位费用</th> <th>创建时间</th> <th>开通时间</th> <th class="width50">状态</th> <th class="width200"></th> </tr> <!-- 使用Struts2标签遍历集合,使用OGNL表达式输出内容。 --> <s:iterator value="costs"> <tr> <td><s:property value="id"/></td> <td><a href="fee_detail.html"><s:property value="name"/></a></td> <td><s:property value="baseDuration"/></td> <td><s:property value="baseCost"/></td> <td><s:property value="unitCost"/></td> <td><s:property value="createTime"/></td> <td><s:property value="startTime"/></td> <td> <s:if test="status==0">开通</s:if> <s:else>暂停</s:else> </td> <td> <input type="button" value="启用" class="btn_start" onclick="startFee();" /> <input type="button" value="修改" class="btn_modify" onclick="location.href='toUpdateCost?id=<s:property value="id"/>';" /> #cold_bold <input type="button" value="删除" class="btn_delete" onclick="deleteFee(<s:property value="id"/>);" /> </td> </tr> </s:iterator> </table> <p>业务说明:<br /> 1、创建资费时,状态为暂停,记载创建时间;<br /> 2、暂停状态下,可修改,可删除;<br /> 3、开通后,记载开通时间,且开通后不能修改、不能再停用、也不能删除;<br /> 4、业务账号修改资费时,在下月底统一触发,修改其关联的资费ID(此触发动作由程序处理) </p> </div> <!--分页--> <div id="pages"> <s:if test="page==1"> <a href="#">上一页</a> </s:if> <s:else> <a href="findCost?page=<s:property value='page-1'/>">上一页</a> </s:else> <s:iterator begin="1" end="totalPage" var="p"> <s:if test="#p==page"> <a href="findCost?page=<s:property value="#p"/>" class="current_page"><s:property value="#p"/></a> </s:if> <s:else> <a href="findCost?page=<s:property value="#p"/>"><s:property value="#p"/></a> </s:else> </s:iterator> <s:if test="page==totalPage"> <a href="#">下一页</a> </s:if> <s:else> <a href="findCost?page=<s:property value='page+1'/>">下一页</a> </s:else> </div> </form> </div> <!--主要区域结束--> <div id="footer"> <p>[源自北美的技术,最优秀的师资,最真实的企业环境,最适用的实战项目]</p> <p>版权所有(C)加拿大达内IT培训集团公司 </p> </div> </body> </html>
步骤五:测试
重新部署项目并启动tomcat,访问资费列表页面,选择一条数据并删除,然后看列表页面上是否删除了这条数据。
本案例的完整代码如下所示:
ICostDao完整代码如下:
package com.tarena.dao; import java.util.List; import com.tarena.entity.Cost; public interface ICostDao { List<Cost> findAll(); /** * 查询某页资费数据 * @param page 页码 * @param pageSize 页容量 * @return */ List<Cost> findByPage(int page, int pageSize); /** * 查询总页数 * @param pageSize * @return */ int findTotalPage(int pageSize); /** * 根据ID查询资费数据 * @param id * @return */ Cost findById(int id); /** * 修改资费数据 * @param cost */ void update(Cost cost); /** * 新增资费数据 * @param cost */ void save(Cost cost); /** * 删除资费数据 * @param id */ void delete(int id); }
CostDaoImpl完整代码如下:
package com.tarena.dao; import java.sql.SQLException; import java.util.List; import javax.annotation.Resource; import org.hibernate.HibernateException; import org.hibernate.Query; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.springframework.orm.hibernate3.HibernateCallback; import org.springframework.orm.hibernate3.support.HibernateDaoSupport; import org.springframework.stereotype.Repository; import com.tarena.entity.Cost; @Repository public class CostDaoImpl extends HibernateDaoSupport implements ICostDao { @Resource public void setSF(SessionFactory sf) { super.setSessionFactory(sf); } @Override public List<Cost> findAll() { String hql = "from Cost"; return getHibernateTemplate().find(hql); } @Override public List<Cost> findByPage(final int page, final int pageSize) { /* * executeFind方法需要传入接口对象,该接口对象的doInHibernate方法 * 会在执行查询时自动被Spring调用,并且doInHibernate方法的返回值 * 将作为最终的结果返回。 * */ return getHibernateTemplate().executeFind(new HibernateCallback() { /* * doInHibernate方法类似于回调函数,是在executeFind方法的内部被调用的。 * 该方法中可以直接使用session,这个session由Spring负责管理,不需要我们 * 创建和关闭。 */ @Override public Object doInHibernate(Session session) throws HibernateException, SQLException { String hql = "from Cost"; Query query = session.createQuery(hql); /* * 设置分页参数,注意在内层函数中调用外层函数的参数, * 要求外层函数的参数必须是final的,因此需要将 * page、pageSize设置为final。 * */ query.setFirstResult((page-1)*pageSize); query.setMaxResults(pageSize); return query.list(); } }); } @Override public int findTotalPage(int pageSize) { String hql = "select count(*) from Cost"; List<Object> list = getHibernateTemplate().find(hql); // 查询出总行数 int rows = Integer.valueOf(list.get(0).toString()); // 根据总行数计算总页数 if (rows % pageSize == 0) return rows / pageSize; else return rows / pageSize + 1; } @Override public Cost findById(int id) { // 使用延迟加载的方法实现 return (Cost) getHibernateTemplate().load(Cost.class, id); } @Override public void update(Cost cost) { if (cost == null) return; getHibernateTemplate().update(cost); } @Override public void save(Cost cost) { if(cost == null) return; getHibernateTemplate().save(cost); } @Override public void delete(int id) { Cost cost = new Cost(); cost.setId(id); getHibernateTemplate().delete(cost); } }
DeleteCostAction完整代码如下:
package com.tarena.action; import javax.annotation.Resource; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Controller; import com.tarena.dao.ICostDao; /** * 删除资费数据 */ @Controller @Scope("prototype") public class DeleteCostAction { @Resource private ICostDao costDao; // input private int id; public String delete() { costDao.delete(id); return "success"; } public int getId() { return id; } public void setId(int id) { this.id = id; } }
struts.xml完整代码如下:
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.1.7//EN" "http://struts.apache.org/dtds/struts-2.1.7.dtd"> <struts> <package name="cost" namespace="/cost" extends="json-default"> <action name="findCost" class="findCostAction" method="load"> <!-- 注入页容量 --> <param name="pageSize">3</param> <result name="success"> /WEB-INF/cost/find_cost.jsp </result> </action> <!-- 打开修改页面 --> <action name="toUpdateCost" class="toUpdateCostAction" method="load"> <result name="success"> /WEB-INF/cost/update_cost.jsp </result> </action> <!-- 修改保存 --> <action name="updateCost" class="updateCostAction" method="update"> <result name="success" type="redirectAction"> findCost </result> </action> <!-- 打开新增页面 --> <action name="toAddCost"> <result name="success"> /WEB-INF/cost/add_cost.jsp </result> </action> <!-- 新增保存 --> <action name="addCost" class="addCostAction" method="add"> <result name="success" type="redirectAction"> findCost </result> </action> <!-- 删除 --> <action name="deleteCost" class="deleteCostAction" method="delete"> <result name="success" type="redirectAction"> findCost </result> </action> </package> </struts>
find_cost.jsp完整代码如下:
<%@page pageEncoding="utf-8" isELIgnored="false"%> <%@taglib uri="/struts-tags" prefix="s"%> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>达内-NetCTOSS</title> <link type="text/css" rel="stylesheet" media="all" href="../styles/global.css" /> <link type="text/css" rel="stylesheet" media="all" href="../styles/global_color.css" /> <script language="javascript" type="text/javascript"> //排序按钮的点击事件 function sort(btnObj) { if (btnObj.className == "sort_desc") btnObj.className = "sort_asc"; else btnObj.className = "sort_desc"; } //启用 function startFee() { var r = window.confirm("确定要启用此资费吗?资费启用后将不能修改和删除。"); document.getElementById("operate_result_info").style.display = "block"; } //删除 function deleteFee(id) { var r = window.confirm("确定要删除此资费吗?"); if(r) { window.location.href = "deleteCost?id="+id; } } </script> </head> <body> <!--Logo区域开始--> <div id="header"> <img src="../images/logo.png" alt="logo" class="left"/> <a href="#">[退出]</a> </div> <!--Logo区域结束--> <!--导航区域开始--> <div id="navi"> <ul id="menu"> <li><a href="../index.html" class="index_off"></a></li> <li><a href="../role/role_list.html" class="role_off"></a></li> <li><a href="../admin/admin_list.html" class="admin_off"></a></li> <li><a href="../fee/fee_list.html" class="fee_on"></a></li> <li><a href="../account/account_list.html" class="account_off"></a></li> <li><a href="../service/service_list.html" class="service_off"></a></li> <li><a href="../bill/bill_list.html" class="bill_off"></a></li> <li><a href="../report/report_list.html" class="report_off"></a></li> <li><a href="../user/user_info.html" class="information_off"></a></li> <li><a href="../user/user_modi_pwd.html" class="password_off"></a></li> </ul> </div> <!--导航区域结束--> <!--主要区域开始--> <div id="main"> <form action="" method=""> <!--排序--> <div class="search_add"> <div> <!--<input type="button" value="月租" class="sort_asc" onclick="sort(this);" />--> <input type="button" value="基费" class="sort_asc" onclick="sort(this);" /> <input type="button" value="时长" class="sort_asc" onclick="sort(this);" /> </div> <input type="button" value="增加" class="btn_add" onclick="location.href='toAddCost';" /> </div> <!--启用操作的操作提示--> <div id="operate_result_info" class="operate_success"> <img src="../images/close.png" onclick="this.parentNode.style.display='none';" /> 删除成功! </div> <!--数据区域:用表格展示数据--> <div id="data"> <table id="datalist"> <tr> <th>资费ID</th> <th class="width100">资费名称</th> <th>基本时长</th> <th>基本费用</th> <th>单位费用</th> <th>创建时间</th> <th>开通时间</th> <th class="width50">状态</th> <th class="width200"></th> </tr> <!-- 使用Struts2标签遍历集合,使用OGNL表达式输出内容。 --> <s:iterator value="costs"> <tr> <td><s:property value="id"/></td> <td><a href="fee_detail.html"><s:property value="name"/></a></td> <td><s:property value="baseDuration"/></td> <td><s:property value="baseCost"/></td> <td><s:property value="unitCost"/></td> <td><s:property value="createTime"/></td> <td><s:property value="startTime"/></td> <td> <s:if test="status==0">开通</s:if> <s:else>暂停</s:else> </td> <td> <input type="button" value="启用" class="btn_start" onclick="startFee();" /> <input type="button" value="修改" class="btn_modify" onclick="location.href='toUpdateCost?id=<s:property value="id"/>';" /> <input type="button" value="删除" class="btn_delete" onclick="deleteFee(<s:property value="id"/>);" /> </td> </tr> </s:iterator> </table> <p>业务说明:<br /> 1、创建资费时,状态为暂停,记载创建时间;<br /> 2、暂停状态下,可修改,可删除;<br /> 3、开通后,记载开通时间,且开通后不能修改、不能再停用、也不能删除;<br /> 4、业务账号修改资费时,在下月底统一触发,修改其关联的资费ID(此触发动作由程序处理) </p> </div> <!--分页--> <div id="pages"> <s:if test="page==1"> <a href="#">上一页</a> </s:if> <s:else> <a href="findCost?page=<s:property value='page-1'/>">上一页</a> </s:else> <s:iterator begin="1" end="totalPage" var="p"> <s:if test="#p==page"> <a href="findCost?page=<s:property value="#p"/>" class="current_page"><s:property value="#p"/></a> </s:if> <s:else> <a href="findCost?page=<s:property value="#p"/>"><s:property value="#p"/></a> </s:else> </s:iterator> <s:if test="page==totalPage"> <a href="#">下一页</a> </s:if> <s:else> <a href="findCost?page=<s:property value='page+1'/>">下一页</a> </s:else> </div> </form> </div> <!--主要区域结束--> <div id="footer"> <p>[源自北美的技术,最优秀的师资,最真实的企业环境,最适用的实战项目]</p> <p>版权所有(C)加拿大达内IT培训集团公司 </p> </div> </body> </html>
当前系统中,如果业务代码报错,会在页面上直接输出错误信息,如下图,客户体验度很差。要求处理这些业务代码中的异常,当任何业务代码发生异常时,请跳转至一个统一的错误页面。
图-9
由于每个功能的业务代码中都有可能报错,因此这种处理异常的行为是所有业务代码都需要做的,是典型的通用业务逻辑,所以可以采用拦截器进行处理,发生异常时转向统一的错误页面即可。
实现此案例需要按照如下步骤进行。
步骤一:创建异常处理拦截器
创建com.tarena.interceptor包,并在包下创建异常处理拦截器ExceptionInterceptor,代码如下:
package com.tarena.interceptor; import com.opensymphony.xwork2.ActionInvocation; import com.opensymphony.xwork2.interceptor.Interceptor; /** * 异常处理拦截器 */ public class ExceptionInterceptor implements Interceptor{ @Override public void destroy() { } @Override public void init() { } @Override public String intercept(ActionInvocation ai) throws Exception { try { return ai.invoke(); } catch (Exception e) { e.printStackTrace(); // 处理异常,报错时转向统一的错误页面 return "error"; } } }
步骤二:注册及引用拦截器
在struts.xml中,注册该拦截器,并设置所有Action都引用这个拦截器,代码如下:
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.1.7//EN" "http://struts.apache.org/dtds/struts-2.1.7.dtd"> <struts> #cold_bold <!-- 公共的包,封装了通用的拦截器、通用的result --> #cold_bold <package name="netctoss" extends="json-default"> #cold_bold <interceptors> #cold_bold <!-- 异常处理拦截器 --> #cold_bold <interceptor name="exceptionInterceptor" #cold_bold class="com.tarena.interceptor.ExceptionInterceptor"/> #cold_bold <!-- 拦截器栈 --> #cold_bold <interceptor-stack name="netctossStack"> #cold_bold <interceptor-ref name="exceptionInterceptor"/> #cold_bold <!-- 不要丢掉默认的拦截器栈,里面有很多Struts2依赖的拦截器 --> #cold_bold <interceptor-ref name="defaultStack"/> #cold_bold </interceptor-stack> #cold_bold </interceptors> #cold_bold <!-- 设置action默认引用的拦截器 --> #cold_bold <default-interceptor-ref name="netctossStack"/> #cold_bold <!-- 全局的result,包下所有的action都可以共用 --> #cold_bold <global-results> #cold_bold <!-- 跳转到错误页面的result --> #cold_bold <result name="error"> #cold_bold /WEB-INF/main/error.jsp #cold_bold </result> #cold_bold </global-results> #cold_bold </package> #cold_bold <package name="cost" #cold_bold namespace="/cost" extends="netctoss"> <action name="findCost" class="findCostAction" method="load"> <!-- 注入页容量 --> <param name="pageSize">3</param> <result name="success"> /WEB-INF/cost/find_cost.jsp </result> </action> <!-- 打开修改页面 --> <action name="toUpdateCost" class="toUpdateCostAction" method="load"> <result name="success"> /WEB-INF/cost/update_cost.jsp </result> </action> <!-- 修改保存 --> <action name="updateCost" class="updateCostAction" method="update"> <result name="success" type="redirectAction"> findCost </result> </action> <!-- 打开新增页面 --> <action name="toAddCost"> <result name="success"> /WEB-INF/cost/add_cost.jsp </result> </action> <!-- 新增保存 --> <action name="addCost" class="addCostAction" method="add"> <result name="success" type="redirectAction"> findCost </result> </action> <!-- 删除 --> <action name="deleteCost" class="deleteCostAction" method="delete"> <result name="success" type="redirectAction"> findCost </result> </action> </package> </struts>
步骤三:创建统一的错误页面
在WEB-INF/main下,创建统一的错误页面error.jsp,代码如下:
<%@page pageEncoding="utf-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>达内-NetCTOSS</title> <link type="text/css" rel="stylesheet" media="all" href="../styles/global.css" /> <link type="text/css" rel="stylesheet" media="all" href="../styles/global_color.css" /> <script language="javascript" type="text/javascript"> var timer; //启动跳转的定时器 function startTimes() { timer = window.setInterval(showSecondes,1000); } var i = 5; function showSecondes() { if (i > 0) { i--; document.getElementById("secondes").innerHTML = i; } else { window.clearInterval(timer); location.href = "login.html"; } } //取消跳转 function resetTimer() { if (timer != null && timer != undefined) { window.clearInterval(timer); location.href = "login.html"; } } </script> </head> <body class="error_page" onload="startTimes();"> <h1 id="error"> 遇到错误, <span id="secondes">5</span> 秒后将自动跳转,立即跳转请点击 <a href="javascript:resetTimer();">返回</a> </h1> </body> </html>
步骤四:模拟一个业务异常
在CostDaoImpl中分页查询方法中模拟一个异常,代码如下:
package com.tarena.dao; import java.sql.SQLException; import java.util.List; import javax.annotation.Resource; import org.hibernate.HibernateException; import org.hibernate.Query; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.springframework.orm.hibernate3.HibernateCallback; import org.springframework.orm.hibernate3.support.HibernateDaoSupport; import org.springframework.stereotype.Repository; import com.tarena.entity.Cost; @Repository public class CostDaoImpl extends HibernateDaoSupport implements ICostDao { @Resource public void setSF(SessionFactory sf) { super.setSessionFactory(sf); } @Override public List<Cost> findAll() { String hql = "from Cost"; return getHibernateTemplate().find(hql); } @Override public List<Cost> findByPage(final int page, final int pageSize) { #cold_bold Integer.valueOf("abc"); /* * executeFind方法需要传入接口对象,该接口对象的doInHibernate方法 * 会在执行查询时自动被Spring调用,并且doInHibernate方法的返回值 * 将作为最终的结果返回。 * */ return getHibernateTemplate().executeFind(new HibernateCallback() { /* * doInHibernate方法类似于回调函数,是在executeFind方法的内部被调用的。 * 该方法中可以直接使用session,这个session由Spring负责管理,不需要我们 * 创建和关闭。 */ @Override public Object doInHibernate(Session session) throws HibernateException, SQLException { String hql = "from Cost"; Query query = session.createQuery(hql); /* * 设置分页参数,注意在内层函数中调用外层函数的参数, * 要求外层函数的参数必须是final的,因此需要将 * page、pageSize设置为final。 * */ query.setFirstResult((page-1)*pageSize); query.setMaxResults(pageSize); return query.list(); } }); } @Override public int findTotalPage(int pageSize) { String hql = "select count(*) from Cost"; List<Object> list = getHibernateTemplate().find(hql); // 查询出总行数 int rows = Integer.valueOf(list.get(0).toString()); // 根据总行数计算总页数 if (rows % pageSize == 0) return rows / pageSize; else return rows / pageSize + 1; } @Override public Cost findById(int id) { // 使用延迟加载的方法实现 return (Cost) getHibernateTemplate().load(Cost.class, id); } @Override public void update(Cost cost) { if (cost == null) return; getHibernateTemplate().update(cost); } @Override public void save(Cost cost) { if(cost == null) return; getHibernateTemplate().save(cost); } @Override public void delete(int id) { Cost cost = new Cost(); cost.setId(id); getHibernateTemplate().delete(cost); } }
步骤五:测试
重新部署项目并启动tomcat,访问资费列表,效果如下图:
图-10
本案例的完整代码如下所示:
ExceptionInterceptor完整代码如下:
package com.tarena.interceptor; import com.opensymphony.xwork2.ActionInvocation; import com.opensymphony.xwork2.interceptor.Interceptor; /** * 异常处理拦截器 */ public class ExceptionInterceptor implements Interceptor{ @Override public void destroy() { } @Override public void init() { } @Override public String intercept(ActionInvocation ai) throws Exception { try { return ai.invoke(); } catch (Exception e) { e.printStackTrace(); // 处理异常,报错时转向统一的错误页面 return "error"; } } }
struts.xml完整代码如下:
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.1.7//EN" "http://struts.apache.org/dtds/struts-2.1.7.dtd"> <struts> <!-- 公共的包,封装了通用的拦截器、通用的result --> <package name="netctoss" extends="json-default"> <interceptors> <!-- 异常处理拦截器 --> <interceptor name="exceptionInterceptor" class="com.tarena.interceptor.ExceptionInterceptor"/> <!-- 拦截器栈 --> <interceptor-stack name="netctossStack"> <interceptor-ref name="exceptionInterceptor"/> <!-- 不要丢掉默认的拦截器栈,里面有很多Struts2依赖的拦截器 --> <interceptor-ref name="defaultStack"/> </interceptor-stack> </interceptors> <!-- 设置action默认引用的拦截器 --> <default-interceptor-ref name="netctossStack"/> <!-- 全局的result,包下所有的action都可以共用 --> <global-results> <!-- 跳转到错误页面的result --> <result name="error"> /WEB-INF/main/error.jsp </result> </global-results> </package> <package name="cost" namespace="/cost" extends="netctoss"> <action name="findCost" class="findCostAction" method="load"> <!-- 注入页容量 --> <param name="pageSize">3</param> <result name="success"> /WEB-INF/cost/find_cost.jsp </result> </action> <!-- 打开修改页面 --> <action name="toUpdateCost" class="toUpdateCostAction" method="load"> <result name="success"> /WEB-INF/cost/update_cost.jsp </result> </action> <!-- 修改保存 --> <action name="updateCost" class="updateCostAction" method="update"> <result name="success" type="redirectAction"> findCost </result> </action> <!-- 打开新增页面 --> <action name="toAddCost"> <result name="success"> /WEB-INF/cost/add_cost.jsp </result> </action> <!-- 新增保存 --> <action name="addCost" class="addCostAction" method="add"> <result name="success" type="redirectAction"> findCost </result> </action> <!-- 删除 --> <action name="deleteCost" class="deleteCostAction" method="delete"> <result name="success" type="redirectAction"> findCost </result> </action> </package> </struts>
CostDaoImpl完整代码如下:
package com.tarena.dao; import java.sql.SQLException; import java.util.List; import javax.annotation.Resource; import org.hibernate.HibernateException; import org.hibernate.Query; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.springframework.orm.hibernate3.HibernateCallback; import org.springframework.orm.hibernate3.support.HibernateDaoSupport; import org.springframework.stereotype.Repository; import com.tarena.entity.Cost; @Repository public class CostDaoImpl extends HibernateDaoSupport implements ICostDao { @Resource public void setSF(SessionFactory sf) { super.setSessionFactory(sf); } @Override public List<Cost> findAll() { String hql = "from Cost"; return getHibernateTemplate().find(hql); } @Override public List<Cost> findByPage(final int page, final int pageSize) { Integer.valueOf("abc"); /* * executeFind方法需要传入接口对象,该接口对象的doInHibernate方法 * 会在执行查询时自动被Spring调用,并且doInHibernate方法的返回值 * 将作为最终的结果返回。 * */ return getHibernateTemplate().executeFind(new HibernateCallback() { /* * doInHibernate方法类似于回调函数,是在executeFind方法的内部被调用的。 * 该方法中可以直接使用session,这个session由Spring负责管理,不需要我们 * 创建和关闭。 */ @Override public Object doInHibernate(Session session) throws HibernateException, SQLException { String hql = "from Cost"; Query query = session.createQuery(hql); /* * 设置分页参数,注意在内层函数中调用外层函数的参数, * 要求外层函数的参数必须是final的,因此需要将 * page、pageSize设置为final。 * */ query.setFirstResult((page-1)*pageSize); query.setMaxResults(pageSize); return query.list(); } }); } @Override public int findTotalPage(int pageSize) { String hql = "select count(*) from Cost"; List<Object> list = getHibernateTemplate().find(hql); // 查询出总行数 int rows = Integer.valueOf(list.get(0).toString()); // 根据总行数计算总页数 if (rows % pageSize == 0) return rows / pageSize; else return rows / pageSize + 1; } @Override public Cost findById(int id) { // 使用延迟加载的方法实现 return (Cost) getHibernateTemplate().load(Cost.class, id); } @Override public void update(Cost cost) { if (cost == null) return; getHibernateTemplate().update(cost); } @Override public void save(Cost cost) { if(cost == null) return; getHibernateTemplate().save(cost); } @Override public void delete(int id) { Cost cost = new Cost(); cost.setId(id); getHibernateTemplate().delete(cost); } }