首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 软件管理 > 软件架构设计 >

也显示一上小弟我的Struts2分页

2012-11-06 
也显示一下我的Struts2分页? 最近看见网上有不少基于Struts2+spring分页的东西,自己感觉都不是很适合,许多

也显示一下我的Struts2分页

? 最近看见网上有不少基于Struts2+spring分页的东西,自己感觉都不是很适合,许多都只是对一个列表进行了分页,而没有考虑到查询条件等。个人认为,分页就是从前台把查询条件输入进去然后返回查询结果列表。这个问题的难点就是查询条件的传递,因为第一次查询时候需要输入查询条件,而以后点下一页时候,就不需要再次输入查询条件了。所以,第一次查询时候,需要把查询条件保存到某个地方,再次查询时候,把条件取出来,同时,这些要做的通用,等有许多查询页面时候,能最大限度的重用代码,好了,废话少说,下面展示我的分页代码

首先写一个Pages类,用来保存分页的一些信息,包括当前页码、记录总数、总页数、每页显示的记录数、查询条件等。代码如下:

public class Pages {private int pageNo = 1; // 当前页码private int totalNum = -1;// 记录总数private int totalPage = -1; // 总页数int perPageNum = 10; // 每页显示记录数private String queryParames;// 查询条件public String getQueryParames() {return queryParames;}public void setQueryParames(String queryParames) {this.queryParames = queryParames;}public Pages() {}public Pages(int pageNo, int totalNum, int perPageNum) {this.pageNo = pageNo;this.totalNum = totalNum;this.perPageNum = perPageNum;this.executeCount();}public void executeCount() {this.totalPage = (int) Math.ceil((this.totalNum + this.perPageNum - 1)/ this.perPageNum);if (this.totalPage <= 0) {this.totalPage = 1;}int intPage = this.pageNo;if (intPage > this.totalPage) {this.pageNo = this.totalPage;} else {this.pageNo = intPage;}}public int getPageNo() {return pageNo;}public void setPageNo(int pageNo) {this.pageNo = pageNo;}public int getPerPageNum() {return perPageNum;}public void setPerPageNum(int perPageNum) {this.perPageNum = perPageNum;}public int getTotalNum() {return totalNum;}public void setTotalNum(int totalNum) {this.totalNum = totalNum;}public int getTotalPage() {return totalPage;}public void setTotalPage(int totalPage) {this.totalPage = totalPage;}public Map transQueryCondition() {Map<String, String> map = new HashMap<String, String>();if (StringUtils.isNull(queryParames)) {return map;}String[] condtions = queryParames.split(";");for (int i = 0; i < condtions.length; i++) {if (StringUtils.isNull(condtions[i])) {continue;}map.put(condtions[i].split(",")[0], condtions[i].split(",")[1]);}return map;}

?下面写一个通用的Action类,用来初始化查询参数:

public abstract class ListAction extends ActionSupport {protected static final String LIST = "list";Logger logger = Logger.getLogger(ListAction.class);/* * 当前页 */private int pageNo = 1;/* * 总页数 */private int totalPage = -1;/* * 总记录数 */private int totalNum = -1;/* * 查询参数 */private String queryParames;private PageList pageList;public final String execute() {initQuerycondition();Pages pages = new Pages();pages.setPageNo(this.getPageNo());pages.setPerPageNum(10);pages.setTotalNum(totalNum);pages.executeCount();this.setTotalPage(pages.getTotalPage());pages.setQueryParames(queryParames);queryPageList(pages);this.setTotalPage(pages.getTotalPage());this.setTotalNum(pages.getTotalNum());return LIST;}/** * 实现每个查询结果列表,依据查询结果的不同而不同 * @param pages */protected abstract void queryPageList(Pages pages);/** * 初始化查询参数的配置 */protected void initQuerycondition() {/* *如果不是第一次查询,直接返回,否则设置查询参数,拼装成 queryParames */if (totalNum >= 0) {return;}Map map = ActionContext.getContext().getParameters();Iterator iter = map.keySet().iterator();StringBuffer query = new StringBuffer();while (iter.hasNext()) {String key = (String) iter.next();Object keyValue = ((Object[]) map.get(key))[0];String value = "";if (!StringUtils.isObjNull(keyValue)) {query.append(";").append(key);if (keyValue instanceof Date) {value = DateUtils.trunsDateToString((Date) keyValue);} else {value = String.valueOf(keyValue);}query.append(",").append(value);}}this.setQueryParames(query.toString());}//get.....set...方法}

再写一个Page类,继承Component,用来获取ValueStack中的参数值。

public class Page extends Component {private static final Log logger = LogFactory.getLog(Page.class);public Page(ValueStack stack) {super(stack);}private String pageNo; // 当前页码private String totalPage; // 总页数private String totalNum; // 总记录数private String styleClass; // 分页的样式private String theme; // 分页的主题private String url; // action的路径private String urlType; // 路径的类型,主要用于URL重写的扩展private String queryParames;public String getQueryParames() {return queryParames;}public void setQueryParames(String queryParames) {this.queryParames = queryParames;}public boolean start(Writer writer) {boolean result = super.start(writer);Object obj = this.getStack().findValue(pageNo);pageNo = String.valueOf((Integer) obj);obj = this.getStack().findValue(totalPage);totalPage = String.valueOf((Integer) obj);obj = this.getStack().findValue(totalNum);totalNum = String.valueOf((Integer) obj);StringBuilder str = new StringBuilder();Map cont = this.getStack().getContext();StrutsRequestWrapper req = (StrutsRequestWrapper) cont.get(StrutsStatics.HTTP_REQUEST);if (url == null || "".equals(url)) {url = (String) req.getAttribute("javax.servlet.forward.request_uri");}String pageStr = "?totalNum=" + totalNum + "&pageNo=";if ("dir".equals(urlType)) {pageStr = "";if ("1".equals(pageNo)) {// 第一页时if (url.lastIndexOf("/") != url.length() - 1) {if (url.lastIndexOf("1") == url.length() - 1) {// 如果有页码1,则去掉1url = url.substring(0, url.length() - 1);} else if (url.lastIndexOf("/") != url.length() - 1) {// 如果没有页码1,并且最后不是'/'时,加上'/'url = url + "/";}}} else {url = url.substring(0, url.lastIndexOf("/") + 1);}}// 下面这段处理主要是用来处理动态查询的参数,并拼接成urlStringBuffer perUrl = new StringBuffer("");obj = this.getStack().findValue(queryParames);if (!StringUtils.isObjNull(obj)) {perUrl.append("&").append("queryParames").append("=").append(StringUtils.decode((String) obj));}//当前页Integer cpageInt = Integer.valueOf(pageNo);str.append("<span> ");// 文本样式if (theme == null || "text".equals(theme)) {// 当前页与总页数相等if (pageNo.equals(totalPage)) {// 如果total = 1,则无需分页,显示“[第1页] [共1页]”if ("1".equals(totalPage)) {str.append("[第 " + pageNo + " 页]");str.append(" [共 " + totalPage + " 页]");} else {// 到达最后一页,显示“[首页] [上一页] [末页]”str.append("<a href='" + url + pageStr + "1" + perUrl+ "'>[首  页]</a> ");str.append("<a href='" + url + pageStr + (cpageInt - 1)+ perUrl + "'>[上一页]</a>");str.append("[下一页]");str.append(" <a href='" + url + pageStr + totalPage+ perUrl + "'>[末  页]</a> ");}} else {// 当前页与总页数不相同if ("1".equals(pageNo)) {// 第一页,显示“[首 页] [下一页] [末页]”str.append("<a href='" + url + pageStr + "1" + perUrl+ "'>[首  页]</a>");str.append("[上一页]");str.append("<a href='" + url + pageStr + (cpageInt + 1)+ perUrl + "'>[下一页]</a>");str.append("<a href='" + url + pageStr + totalPage + perUrl+ "'>[末  页]</a>");} else {// 不是第一页,显示“[首页] [上一页] [下一页] [末页]”str.append("<a href='" + url + pageStr + "1" + perUrl+ "'>[首  页]</a>");str.append("<a href='" + url + pageStr + (cpageInt - 1)+ perUrl + "'>[上一页]</a>");str.append("<a href='" + url + pageStr + (cpageInt + 1)+ perUrl + "'>[下一页]</a>");str.append("<a href='" + url + pageStr + totalPage + perUrl+ "'>[末  页]</a>");}}} else if ("number".equals(theme)) { // 数字样式 [1 2 3 4 5 6 7 8 9 10 >// >>]Integer totalInt = Integer.valueOf(totalPage);// 如果只有一页,则无需分页str.append("[ ");if (totalInt == 1) {str.append("<strong>1</strong> ");} else {//当前页if (cpageInt > 1) {// 当前不是第一组,要显示“<< <”// <<:返回前一组第一页// <:返回前一页str.append("<a href='" + url + pageStr + "1" + perUrl+ "'> << </a> ");str.append("<a href='" + url + pageStr + (cpageInt - 1)+ perUrl);str.append("'><</a> ");} else {str.append("<< < ");}int v = (cpageInt - 4) > 0 ? (cpageInt - 4) : 1;int v1 = (cpageInt + 4) < totalInt ? (cpageInt + 4) : totalInt;if (v1 == totalInt) {v = totalInt - 10;v = (v <= 0 ? 1 : v); // 如果为负数,则修改为1} else if (v == 1 && v1 < totalInt) {v1 = totalInt > 10 ? 10 : totalInt;}// 10个为一组显示for (int i = v; i <= v1; i++) {if (cpageInt == i) { // 当前页要加粗显示str.append("<strong>" + i + "</strong> ");} else {str.append("<a href='" + url + pageStr + i + perUrl+ "'>" + i + "</a> ");}}// 如果多于1组并且不是最后一组,显示“> >>”if (cpageInt < totalInt) {// >>:返回下一组最后一页// >:返回下一页str.append("<a href='" + url + pageStr + (cpageInt + 1)+ perUrl);str.append("'>></a> ");str.append("<a href='" + url + pageStr + totalInt + perUrl);str.append("'> >> </a> ");} else {str.append("> >> ");}}str.append("]");}str.append("</span>");try {writer.write(str.toString());} catch (IOException e) {logger.error(e);}return result;}//get set 方法区}

?PageTag类,继承Struts2标签的 ComponentTagSupport。

public class PageTag extends ComponentTagSupport {private static final long serialVersionUID = -5371048231966321759L;private String pageNo;private String totalPage;private String totalNum;private String styleClass;private String theme;private String url;private String urlType;private String queryParames;@Overrideprotected void populateParams() {super.populateParams();Page page = (Page) component;page.setPageNo(pageNo);page.setTotalPage(totalPage);page.setTotalNum(totalNum);page.setStyleClass(styleClass);page.setTheme(theme);page.setUrl(url);page.setUrlType(urlType);page.setQueryParames(queryParames);}@Overridepublic Component getBean(ValueStack stack, HttpServletRequest request,HttpServletResponse response) {return new Page(stack);}// get set 方法.......}

?自定义分页的tld文件:

<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE taglib PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN" "http://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd"><taglib><tlib-version>2.2.3</tlib-version><jsp-version>1.2</jsp-version><short-name>fly</short-name><uri>/fly</uri><display-name>"fly Tags"</display-name><tag><name>pages</name><tag-class>com.fly.web.taglib.PageTag</tag-class><body-content>jsp</body-content><description>分页标签</description><attribute><name>pageNo</name><required>false</required><rtexprvalue>true</rtexprvalue></attribute><attribute><name>totalPage</name><required>false</required><rtexprvalue>true</rtexprvalue><description>总页数</description></attribute><attribute><name>totalNum</name><required>false</required><rtexprvalue>true</rtexprvalue><description>总记录数。</description></attribute><attribute><name>styleClass</name><required>false</required><rtexprvalue>true</rtexprvalue><description>分页标签的样式,不配置此项将采用默认的样式</description></attribute><attribute><name>theme</name><required>false</required><rtexprvalue>true</rtexprvalue><description>分页的主题,现支持number|text两种主题</description></attribute><attribute><name>url</name><required>false</required><rtexprvalue>true</rtexprvalue><description>分页提交的路径,默认不用配置该项,自动获取系统访问路径。</description></attribute><attribute><name>urlType</name><required>false</required><rtexprvalue>true</rtexprvalue></attribute><attribute><name>queryParames</name><required>false</required><rtexprvalue>false</rtexprvalue><description>查询参数</description></attribute></tag></taglib>  

?

? 我们在jsp页面直接写

<td align="center" nowrap>  记录总数<s:property value="%{totalNum}" />条  每页显示10条第<s:property value="pageNo" />页  <fly:pages pageNo="pageNo" totalPage="totalPage" totalNum="totalNum" styletheme="text"  queryParames="queryParames"></fly:pages></td>

? 就这样简单一个通用的分页框架就完成了。

12 楼 風一樣的男子 2010-01-31   看到 ListAction 里那么多实例变量就讨厌
有 Pages  类了, Action 里还有当前页什么的
另外用 tag 也不怎么推荐
一旦要变更 View,改死,特别改的人不是写的人 13 楼 tangbo530 2010-01-31   生成页面工具条,建议封装成标签比较好! 14 楼 binlaniua 2010-01-31   的确

我们都是在JavaScript做分页的

后台做分页。。多麻烦 15 楼 hommy8 2010-01-31      我也刚写了个分页,js写的,我认为查询条件等参数也不应该耦合到分页控件中。我的分页控件只需要在页面初始化一次,初始化的时候客户端告诉我当用户点击分页的上一页、下一页等按钮的时候,用哪一个函数去响应,我会自动计算好数据表的起始索引,以及结束的索引,然后传递给这个函数,如果查询条件等参数不变,客户端可以直接把查询条件以及我传递给客户端的分页参数发送到后台,获得正确的结果。如果查询条件改变了,reset一下分页控件,等这次后台返回给前台的时候从第一页开始。  
   这样我根本不关心你的查询条件以及url是如何的。 
   这样做可能不够自动化,但是灵活性强。   我认为分页控件只需把翻页的参数计算好传递给客户端的响应函数已经足够了,参数给你了,至于你是同步还是异步,或者是如何返回后台,我不管。 不知道这样做是否好呢?请大家拍砖! 16 楼 hommy8 2010-01-31   这样做是否满足了要求了?是否能减轻程序员的工作量? 我还在思考阶段。 17 楼 yipbxx 2010-02-01   daquan198163 写道yipbxx 写道kjj 写道比较复杂,业务逻辑与分页逻辑还是不要耦合为妙
同感,最好用JS封装一个分页,用的时候调用下就行了,有那么麻烦么?分页不就传几个参数么,想怎么显示就怎么显示,显示是表现层的问题,我就不明白了,你把分页逻辑放到action里想干什么?
能做到每次只取出一页数据么?
参考mysql的limit关键字。select * from table limit 0,10;你只要计算出当前页索引就行了
18 楼 unika_ly12 2010-02-01   hommy8 写道   我也刚写了个分页,js写的,我认为查询条件等参数也不应该耦合到分页控件中。我的分页控件只需要在页面初始化一次,初始化的时候客户端告诉我当用户点击分页的上一页、下一页等按钮的时候,用哪一个函数去响应,我会自动计算好数据表的起始索引,以及结束的索引,然后传递给这个函数,如果查询条件等参数不变,客户端可以直接把查询条件以及我传递给客户端的分页参数发送到后台,获得正确的结果。如果查询条件改变了,reset一下分页控件,等这次后台返回给前台的时候从第一页开始。  
   这样我根本不关心你的查询条件以及url是如何的。 
   这样做可能不够自动化,但是灵活性强。   我认为分页控件只需把翻页的参数计算好传递给客户端的响应函数已经足够了,参数给你了,至于你是同步还是异步,或者是如何返回后台,我不管。 不知道这样做是否好呢?请大家拍砖!
在数据量很大的情况下,比如几万条,甚至几十万条数据,你也在页面初始化一次,页面恐怕要慢的不行了。 19 楼 yipbxx 2010-02-01   zhangjunji111 写道daquan198163 写道yipbxx 写道kjj 写道比较复杂,业务逻辑与分页逻辑还是不要耦合为妙
同感,最好用JS封装一个分页,用的时候调用下就行了,有那么麻烦么?分页不就传几个参数么,想怎么显示就怎么显示,显示是表现层的问题,我就不明白了,你把分页逻辑放到action里想干什么?
能做到每次只取出一页数据么?
我附上了源码,可以看一下,不过,我这里只是针对mysql做的分页处理,如果是oracle或者sqlServer,需要重新修改一下。

针对mysql更简单,不用重复查询,mysql提供limit关键字,分页起来更简单
20 楼 ziyu_1 2010-02-01   分页一定要和struts扯到一起么? 21 楼 daquan198163 2010-02-01   yipbxx 写道daquan198163 写道yipbxx 写道kjj 写道比较复杂,业务逻辑与分页逻辑还是不要耦合为妙
同感,最好用JS封装一个分页,用的时候调用下就行了,有那么麻烦么?分页不就传几个参数么,想怎么显示就怎么显示,显示是表现层的问题,我就不明白了,你把分页逻辑放到action里想干什么?
能做到每次只取出一页数据么?
参考mysql的limit关键字。select * from table limit 0,10;你只要计算出当前页索引就行了

我是问:只用JS封装的方案可以每次只取出一页数据么?
从你的回答看,服务端还是难免要有分页逻辑的。

能把你的方案详细介绍一下么,最好再上传一个完整的示例。 22 楼 yipbxx 2010-02-01   daquan198163 写道yipbxx 写道daquan198163 写道yipbxx 写道kjj 写道比较复杂,业务逻辑与分页逻辑还是不要耦合为妙
同感,最好用JS封装一个分页,用的时候调用下就行了,有那么麻烦么?分页不就传几个参数么,想怎么显示就怎么显示,显示是表现层的问题,我就不明白了,你把分页逻辑放到action里想干什么?
能做到每次只取出一页数据么?
参考mysql的limit关键字。select * from table limit 0,10;你只要计算出当前页索引就行了

我是问:只用JS封装的方案可以每次只取出一页数据么?
从你的回答看,服务端还是难免要有分页逻辑的。

能把你的方案详细介绍一下么,最好再上传一个完整的示例。
建议你了多解下mysql,查下limit的用法就清楚了,你只要传进当前页索引就行了,它指针直接定位到某条记录,int pageIndex = (pageCurrent-1)*pagesize;其中pageCurrent是你从页面得到的当前页,pagesize当前页大小由自己定,limit只接受pageIndex 和pagesize两个参数,取出来的就是你想要的结果。等下附上我自己写的js分页。 23 楼 honda418 2010-02-01   pengkey 写道兄弟
用个jsp专门写个分页控件
在列表页面include一下就行了
还要写得那么复杂吗?

对。。。。

另外,我觉得查询条件没必要封装,每次点页码的时候传递查询条件,触发一个查询,不是更方便么? 24 楼 wangdgsc 2010-02-01   我个人觉得,这个分页与框架进行了耦合,如果有一天,有一个特殊的需要,让你把struts2换成struts1,可能会比较的麻烦,而且,你将分页的一些数据写到了action中,让其他的action继承,我想,组合是不是会更好一点,如果你的这个action中只是用来进行分页还好一点,如果说你的action需要包含其他的一些公共的,通用的方法,那你的这个action是不是看起来有点而复杂,不够单一,还有,我觉得,分页只是为了用来进行视图层显示的,好像也需要与数据库进行耦合吧,如果更单一点,会不会更通用一点,个人一点愚见,如果不对,请多多指正,我暂时是使用struts1,不过,我想我的分页组件换成struts2也一样的可以使用 25 楼 zhangjunji111 2010-02-03   wangdgsc 写道我个人觉得,这个分页与框架进行了耦合,如果有一天,有一个特殊的需要,让你把struts2换成struts1,可能会比较的麻烦,而且,你将分页的一些数据写到了action中,让其他的action继承,我想,组合是不是会更好一点,如果你的这个action中只是用来进行分页还好一点,如果说你的action需要包含其他的一些公共的,通用的方法,那你的这个action是不是看起来有点而复杂,不够单一,还有,我觉得,分页只是为了用来进行视图层显示的,好像也需要与数据库进行耦合吧,如果更单一点,会不会更通用一点,个人一点愚见,如果不对,请多多指正,我暂时是使用struts1,不过,我想我的分页组件换成struts2也一样的可以使用
  个人感觉,不是任何时候,组合都优于技术,本分页框架是基于struts2的,如果换成别的技术了,整个分页都需要重新写了。另外,我这个分页于web进行了解耦,有些局部需要调整,但整体上的设计还是很好的。框架的设计本来就是为了简化代码编写的。
26 楼 yipbxx 2010-02-05   zhangjunji111 写道wangdgsc 写道我个人觉得,这个分页与框架进行了耦合,如果有一天,有一个特殊的需要,让你把struts2换成struts1,可能会比较的麻烦,而且,你将分页的一些数据写到了action中,让其他的action继承,我想,组合是不是会更好一点,如果你的这个action中只是用来进行分页还好一点,如果说你的action需要包含其他的一些公共的,通用的方法,那你的这个action是不是看起来有点而复杂,不够单一,还有,我觉得,分页只是为了用来进行视图层显示的,好像也需要与数据库进行耦合吧,如果更单一点,会不会更通用一点,个人一点愚见,如果不对,请多多指正,我暂时是使用struts1,不过,我想我的分页组件换成struts2也一样的可以使用
  个人感觉,不是任何时候,组合都优于技术,本分页框架是基于struts2的,如果换成别的技术了,整个分页都需要重新写了。另外,我这个分页于web进行了解耦,有些局部需要调整,但整体上的设计还是很好的。框架的设计本来就是为了简化代码编写的。

分页是一种数据的展示方式,用户需要怎样的展示你执行就对了,这个为什么一定要和技术框架扯上关系?非要弄个struts2的分页,如果用的是struts1是不是也要搞个struts1的分页?如果其他技术呢?你这样是为了简化了代码的编写?我看不出来。。。 27 楼 haplone 2010-02-05   楼主,精神可嘉,只是对于一般应用,这样写的话,代价太大了。考虑下,使用异步,第一次读取全部数据到缓存,之后再去缓存一页页取 28 楼 zhangjunji111 2010-02-05   haplone 写道楼主,精神可嘉,只是对于一般应用,这样写的话,代价太大了。考虑下,使用异步,第一次读取全部数据到缓存,之后再去缓存一页页取
如果数据量过大的话,比如超过10万条的话,可以吗?
29 楼 yangfuchao418 2010-02-07   honda418 写道pengkey 写道兄弟
用个jsp专门写个分页控件
在列表页面include一下就行了
还要写得那么复杂吗?

对。。。。

另外,我觉得查询条件没必要封装,每次点页码的时候传递查询条件,触发一个查询,不是更方便么?

数据量几百万 上千万的时候你难道也每次去触发一下吗? 30 楼 liberD 2010-03-02   学习一下楼主的思路! 31 楼 zhangshixi 2010-05-28   您的这种实现代码耦合太多,灵活性不足,不能满足通用的分页需求。
楼主可参考我写的两片文章:构建自己的通用分页组件(上)(下)。
http://zhangshixi.iteye.com/blog/676906
http://www.iteye.com/topic/677469

热点排行