Top

JAVA SSH DAY01

  1. SSH中的分页查询
  2. SSH中使用延迟加载
  3. 使用Hibernate进行修改
  4. 资费新增
  5. 资费删除
  6. 异常处理

1 SSH中的分页查询

1.1 问题

为资费列表页面增加分页功能。

1.2 方案

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来自定义查询。

1.3 步骤

实现此案例需要按照如下步骤进行。

步骤一:在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

1.4 完整代码

本案例的完整代码如下所示:

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>

2 SSH中使用延迟加载

2.1 问题

开发资费修改功能,要求可以打开修改页面,并显示出要修改的数据,其中对于修改数据的查询要求使用延迟加载的方法。

2.2 方案

由于DAO中使用延迟加载方法查询出修改数据,而该数据在修改页面中才被使用,即在使用数据时session已经关闭。因此,要想解决这个问题,需要使用Open session in view技术。在SSH中使用Open session in view技术很简单,只需要在web.xml中配置一个filter即可,并且这个filter已经由Spring预置好了,名为org.springframework.orm.hibernate3.support.OpenSessionInViewFilter。

2.3 步骤

实现此案例需要按照如下步骤进行。

步骤一:在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

2.4 完整代码

本案例的完整代码如下所示:

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>

3 使用Hibernate进行修改

3.1 问题

完成资费修改保存功能,并在项目中解决使用Hibernate修改带来的问题。

3.2 方案

使用Hibernate修改时,它会根据映射关系文件,自动拼出一个update语句,然后执行修改,其中映射关系文件中往往配置表中全部的字段,而很多时候,页面上不需要修改表中全部的字段,只需要修改其中的一部分,因此页面上的字段少于表中字段,在提交保存时,缺少的字段就成了空值,那么再按照完整的update语句来执行更新,就会把这些不需要更新的字段更新为空。

这个问题如果使用JDBC或者MyBatis是不会存在的,因为SQL是自己写的。而Hibernate自动生成SQL,就出现了这个问题。

本案例中,我们采用动态更新的方式来解决这个问题,即在映射关系文件中通过dynamic-update=”true”来声明更新方式为动态更新,届时Hibernate在自动生成update语句时会判断属性值是否发生改变,若改变则将属性拼入SQL,否则忽略掉这个属性。

这种方式要求传给Hibernate的对象必须是持久态的,而通过页面传入的对象是Struts2自动初始化的,是临时态的。我们可以通过ID查询出持久态对象,然后通过Spring中的BeanUtils工具类,将临时态对象的属性值复制给持久态对象,然后用这个持久态对象进行更新即可。

3.3 步骤

实现此案例需要按照如下步骤进行。

步骤一:在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

3.4 完整代码

本案例的完整代码如下所示:

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>

4 资费新增

4.1 问题

完成资费新增功能。

4.2 方案

资费新增和资费修改十分相似,不同点在于新增页面不需要默认显示数据,因此该功能可以参考资费修改完成。

4.3 步骤

实现此案例需要按照如下步骤进行。

步骤一:配置资费新增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

4.4 完整代码

本案例的完整代码如下所示:

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>

5 资费删除

5.1 问题

完成资费删除功能。

5.2 方案

资费删除功能比较简单,在页面上选中一条资费,将ID传入Action,然后调用DAO使用Hibernate删除这条数据即可。

5.3 步骤

实现此案例需要按照如下步骤进行。

步骤一: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,访问资费列表页面,选择一条数据并删除,然后看列表页面上是否删除了这条数据。

5.4 完整代码

本案例的完整代码如下所示:

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>

6 异常处理

6.1 问题

当前系统中,如果业务代码报错,会在页面上直接输出错误信息,如下图,客户体验度很差。要求处理这些业务代码中的异常,当任何业务代码发生异常时,请跳转至一个统一的错误页面。

图-9

6.2 方案

由于每个功能的业务代码中都有可能报错,因此这种处理异常的行为是所有业务代码都需要做的,是典型的通用业务逻辑,所以可以采用拦截器进行处理,发生异常时转向统一的错误页面即可。

6.3 步骤

实现此案例需要按照如下步骤进行。

步骤一:创建异常处理拦截器

创建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">
	        遇到错误,&nbsp;<span id="secondes">5</span>&nbsp;秒后将自动跳转,立即跳转请点击&nbsp;
            <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

6.4 完整代码

本案例的完整代码如下所示:

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);
	}

}