刚开始学这几个框架没多久,遇到很多问题是在所难免的,这两天写用户信息管理模块,要用到分页,在网上也看了不少,很多都是用ibatis里自带的方法写,不过实现方式好像是先从数据库中读取全部数据,然后再分,感觉数据量大的时候,效率肯定会很慢,在hibernate里有专门分页的方法(函数),ibatis也有,但是针对少量数据的时候能用。
原先用jdbc写的时候,分页很简单,用哪部分,就读取那部分并显示出来,感觉这个方法挺简单,于是我就用了这个思想,每个主流数据库基本上都有自己的查询特定数据的sql语句,我的数据库用的是mssql2000的,它的分页查询sql语句是:
pagesize: 每页显示记录数
currentpage:当前页数
select * from ( select TOP pagesize * FROM ( SELECT TOP pagesize*cureentpage * from user_table ORDER BY id ASC ) as aSysTable ORDER BY id DESC ) as bSysTable ORDER BY id ASC
在网上看到一个用基于struts2+spring+hibernate写的管理系统,我用了他的分页思想,和部分代码,在获取数据的时候,他用的是hibernate,而我给转化成是ibatis。
就从表现层一步一步开始吧!
(一)表现层
memberlist.jsp的主要代码
<TABLE class=table_southidc cellSpacing=1 cellPadding=2 width="100%" align=center
border=0>
<TBODY>
<TR>
<TD class=back_southidc align=middle colSpan=6 height=25><font color="white"><B>会员信息管理</B></font></TD></TR>
<TR class=tr_southidc>
<TD width="10%" height=23><FONT class=t4><B>用户ID</B></FONT></TD>
<TD width="10%" height=23><FONT class=t4><B>用户名</B></FONT></TD>
<TD width=""><FONT class=t4><B>地址</B></FONT></TD>
<TD width="20%"><FONT class=t4><B>邮箱</B></FONT></TD>
</TR>
<s:iterator value="users">
<TR class=tr_southidc>
<TD width="10%" height=23><FONT class=t1><s:property value="id"/></FONT></TD>
<TD width="10%" <FONT class=t1><s:property value="username"/></FONT></TD>
<TD width=""><FONT class=t1><s:property value="address"/></FONT></TD>
<TD width="20%"><FONT class=t1><s:property value="email"/></FONT></TD></TR>
</s:iterator>
</TBODY></TABLE>
<BR>
<BR>
<BR>
<table width="95%" border="0" align="center" cellpadding="0" cellspacing="0">
<tr>
<td align=right>
共<s:property value="pager.getTotalRows()"/>行
第<s:property value="currentPage"/>页
共<s:property value="pager.getTotalPages()"/>页
<a href="<s:url value="memberlist!getuserslist.action">
<s:param name="currentPage" value="currentPage"/>
<s:param name="pagerMethod" value="'first'"/>
</s:url>">首页</a>
<a href="<s:url value="memberlist!getuserslist.action">
<s:param name="currentPage" value="currentPage"/>
<s:param name="pagerMethod" value="'previous'"/>
</s:url>">上一页</a>
<a href="<s:url value="memberlist!getuserslist.action">
<s:param name="currentPage" value="currentPage"/>
<s:param name="pagerMethod" value="'next'"/>
</s:url>">下一页</a>
<a href="<s:url value="memberlist!getuserslist.action">
<s:param name="currentPage" value="currentPage"/>
<s:param name="pagerMethod" value="'last'"/>
</s:url>">尾页</a>
</td>
</tr>
</table>
(二)控制层
MemberListAction
上面文件里用到memberlist 这个action的getuserslist方法,下面是memberlist这个action:
这个action通过spring注入了usersservice业务组件,另外还有Pager,pager类里包括查询分页返回的用户信息列表,currentPage和pagerMethod是接收前台页面传来的参数。
package com.netshop.action.admin;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.ActionContext;
import com.netshop.service.UsersService;
import java.util.Map;
import java.util.List;
import java.util.ArrayList;
import com.netshop.util.Pager;
import com.netshop.domain.Users;
/**
*
* @author LuZhou
*/
public class MemberListAction extends ActionSupport {
public MemberListAction() {
}
protected Pager pager;//查看(五)
protected String currentPage;
protected String pagerMethod;
private UsersService usersservice;
private List users;
public Pager getPager() {
return pager;
}
public void setPager(Pager pager) {
this.pager = pager;
}
public String getPagerMethod() {
return pagerMethod;
}
public void setPagerMethod(String pagerMethod) {
this.pagerMethod = pagerMethod;
}
public String getCurrentPage() {
return currentPage;
}
public void setCurrentPage(String currentPage) {
this.currentPage = currentPage;
}
public List getUsers()
{
return users;
}
public void setUsers(List users)
{
this.users=users;
}
public void setUsersservice(UsersService usersservice)
{
this.usersservice=usersservice;
}
public String getuserslist() throws Exception {
Map session=ActionContext.getContext().getSession();
if(null!=session.get("user"))
{
pager=usersservice.getPager(getCurrentPage(),getPagerMethod());
users=(ArrayList)pager.getElements();
this.setCurrentPage(String.valueOf(pager.getCurrentPage()));
return SUCCESS;
}
else
return ERROR;
}
}
上面的memberlist这个acion配置,如下:
struts.xml里的action
<action name="memberlist" class="MemberListAction" method="getuserslist">
<result name="success">/admin/memberlist.jsp</result>
<result name="error">/admin/error.jsp</result>
</action>
我使用spring来管理所有组件的包括action,
spring配置文件里的部分代码:
<bean id="MemberListAction" class="com.netshop.action.admin.MemberListAction" scope="prototype">
<property name="usersservice" ref="UsersService"/>
</bean>
(三)业务逻辑层
上面usersservice的getPager()方法,其实是调用了我写的dao层basedao的方法getPager(),basedao是个基类,以后其他继承自该类的,都可以实现分页。
basedao的代码
package com.netshop.dao;
import org.springframework.orm.ibatis.support.SqlMapClientDaoSupport;
import org.springframework.orm.ibatis.SqlMapClientTemplate;
import com.netshop.util.Pager;
import java.util.List;
import java.util.ArrayList;
import com.netshop.domain.Users;
/**
* @author : LuZhou
* @E-mail : mailluzhou@163.com
* @version : 1.0
* @Date : 2009-5-10
*
*/
public class BaseDao
extends SqlMapClientDaoSupport{
protected SqlMapClientTemplate smcTemplate = this.getSqlMapClientTemplate();
//获取查询数据的总记录,用于计算页数时用到
public int getCounts()
{
return ((Integer)smcTemplate.queryForObject("getCounts")).intValue();//查看(四)
}
//根据表现层传过来的页面和表现层传过来的方法获取特定页面的数据,在控制层action会调用这个方法,
public Pager getPager(String currentPage,String pagerMethod)。
{
int totalRows=this.getCounts();//先获取总记录数,也是用sql语句获取。
List items=new ArrayList();
Pager pager;
pager=new Pager(totalRows);
if(currentPage!=null)
{
pager.refresh(Integer.parseInt(currentPage));
}
if(pagerMethod!=null)
{
if(pagerMethod.equals("first"))
{
pager.first();
}
else if(pagerMethod.equals("previous"))
{
pager.previous();
}
else if(pagerMethod.equals("next"))
{
pager.next();
}
else if(pagerMethod.equals("last"))
{
pager.last();
}
}
/*
通过sqlmap查询特定数据,“getListByPager”需要传给他两个参数,就是最开始讲的mssql2000分页sql语句用到得两个参数pagesize和cureentpage,这两个参数都封装在pager类里,因为sql语句中要得到pagesize和cureentpage的乘积,在配置文件中直接获取不到,所以我将pagesize和cureentpage的乘积存放到另一个参数里,在pager类里能看到,就是pageSizeCurrentPage。
*/
items=(ArrayList)smcTemplate.queryForList("getListByPager",pager);//查看(四)
pager.setElements(items);//将获取的列表赋给pager的elements,然后返回给前台。
return pager;
}
}
(四)
看看sqlmap的配置文件
<typeAlias alias="users" type="com.netshop.domain.Users" />
<typeAlias alias="pager" type="com.netshop.util.Pager"/>
<!-- 查找所有用户的总记录数 -->
<select id="getCounts" resultClass="int">
<![CDATA[
SELECT count(id) FROM users
]]>
</select>
<!-- 查找用户的特定记录数 分页查询 -->
<select id="getListByPager" parameterClass="pager" resultClass="users">
下面被注释掉的语句,在获取最后一页的记录时,不能够正确显示,
<!--select * from (select TOP $pageSize$ * FROM ( SELECT TOP $pageSizeCurrentPage$ * from users ORDER BY id ASC ) as a ORDER BY id DESC ) as b ORDER BY id ASC -->
用下面的语句无错误,不过此时的 $pageSizeCurrentPage$,跟上面的不一样,$pageSizeCurrentPage$=pagesize*(当前页数-1),在Pager.java里已经改过了。
SELECT TOP $pageSize$ * FROM products WHERE ID NOT IN (SELECT TOP $pageSizeCurrentPage$ ID FROM products ORDER BY ID DESC)ORDER BY ID DESC
</select>
上面需要说明的是,给id="getListByPager"传递参数的时候,必须用$pageSize$这种方式,而非#pageSize#,用它在编译的时候会出错,获取不到pageSize这个值
就像我们用jdbc时的Statement和PreparedStatement 一样,用#pageSize#和PreparedStatement一样的性质,先预处理,而用Statement是直接在sql语句参数化之前,将数值注入到sql语句中,$pageSize$就是这个目的,当然也会面临sql注入的问题,不过这里还好。这也是能分页的关键所在。
(五)辅助类
package com.netshop.util;
import java.util.List;
/**
*
* @author : LuZhou
* @Email : mailluzhou@163.com
* @version : 1.0
* @Date : 2009-5-10
*
* @desciprion :
*/
public class Pager {
private int totalRows;//记录总数
private int pageSize=4;//没页显示记录数
private int currentPage=1;//当前页码
private int totalPages;//总页数
private int startRow;//当前页码开始记录数
private List elements;//获取的记录列表
private int pageSizeCurrentPage;//记录currentPage与pageSize的乘积,在ibatis的sqlmap配置文件里要用到
public Pager()
{
}
public Pager(int totalRows)
{
this.totalRows=totalRows;
//更新于2009-6-11 原先为 totalPages=totalRows/pageSize;
//解决totalRows为0时,页面出错!如下:
//-----------------------------------------------
totalPages=totalRows==0?1:totalRows/pageSize;
//-------------------------------------------
int mod=totalRows%pageSize;
if(mod>0)
{
totalPages++;
}
currentPage=1;
startRow=0;
}
public void first()
{
currentPage=1;
startRow=0;
}
public void previous()
{
if(currentPage==1)
{
return;
}
currentPage--;
startRow=(currentPage-1)*pageSize;
}
public void next()
{
if(currentPage<totalPages)
{
currentPage++;
}
startRow=(currentPage-1)*pageSize;
}
public void last()
{
currentPage=totalPages;
startRow=(currentPage-1)*pageSize;
}
public void refresh(int currentPage)
{
this.currentPage=currentPage;
if(currentPage>totalPages)
last();
}
public int getTotalRows()
{
return totalRows;
}
public void setTotalRows(int totalRows)
{
this.totalRows=totalRows;
}
public int getPageSize()
{
return pageSize;
}
public void setPageSize(int pageSize)
{
this.pageSize=pageSize;
}
public int getCurrentPage()
{
return currentPage;
}
public void setCurrentPage(int currentPage)
{
this.currentPage=currentPage;
}
public int getTotalPages()
{
return totalPages;
}
public void setTotalPages(int totalPages)
{
this.totalPages=totalPages;
}
public int getStartRow()
{
return startRow;
}
public void setStartRow(int startRow)
{
this.startRow=startRow;
}
public List getElements()
{
return elements;
}
public void setElements(List elements)
{
this.elements=elements;
}
/*
*一个错误,浪费了我大半天的时间,整个分页代码走查了好几遍,挨个又测试了一遍,原来在这出错。
*getPageSizeCurrentPage()方法原先写的时候 写顺手了,直接返回pageSizeCurrentPage,
* 其实应该返回pageSize*currentPage,难怪sqlMap里老是得不到数据,郁闷了半天,终于搞定。
*
*/
public int getPageSizeCurrentPage()
{
// return pageSize*currentPage;原先分页语句中的参数,不过会出错。
return pageSize*(currentPage-1)
}
public void setPageSizeCurrentPage()
{
// this.pageSizeCurrentPage=pageSize*currentPage;
this.pageSizeCurrentPage=pageSize*(currentPage-1)
}
}
上面就是具体的实现过程。
<script src="http://www.shaguang.com/PLUGIN/copytofriends/copy.js" type="text/javascript"></script>