- 浏览: 73038 次
- 性别:
- 来自: 杭州
最新评论
-
zhaojian770627:
...
B+树的Java实现 -
kevindude:
包含在了clyde库里
Clyde学习笔记三(Config) -
silverys:
能否提供demo下载学习一下,谢谢
3D MMO Demo -
silverys:
请问下,这个Three Rings Resource Edit ...
Clyde学习笔记三(Config) -
Bactryki:
要用什么包呢?
基于Struts2+Spring+iBatis的web应用最佳实践系列之二(访问控制篇上)
分页也是一个大家经常讨论的话题,网上也有过很多的分页方法的介绍,但往往仅仅只是局限在web层或数据访问层的一个分页组件,对于一个典型的三层web应用来说笔者还没有见到过一个完整的例子。又或者是使用了这个组件后往往还要手工书写繁琐的代码。在这里笔者就象大家介绍一种基于Struts2+Spring+iBatis三层框架结构,而不用手工写一行代码的分页组件(或者说框架)。
首先,在web层我们可以考虑使用自定义标签来实现我们的分页组件,这样可以最大限度的实现组件的可复用性而使用Struts2又可以非常方便的扩展已有的组件。通过下面这张类图结构可以看到,所有Struts2的tag都继承自BodyTagSupport这个类,在BodyTagSupport这个类以及之上是属于sun servlet/jsp标准的一部分而从StrutsBodyTagSupport之后则是Struts框架的一部分,只要我们继承自Struts2的框架则我们的标签则自动的获得了对ognl表达式以及valueStack的支持。而所有的ui标签则继承自AbstractUITag这个类,对于我们需要的分页标签来说,这是一个很好的扩展点。
而每一个ui tag都会有一个对应的ui bean component。这个ui bean封装了对具体数据的处理(ognl表达式解析,从valueStack上取值),并且可以指定一个freemarker模板来渲染html输出。
通过下面的这个类图结构可以看到,所有Struts2的内置标签都继承了UIBean这个类,我们的分页标签ui component也会继承自这个类。
分页标签的tag类
/** * struts2 版分页标签 */ public class PagerTag extends AbstractUITag { private static final long serialVersionUID = 719669934024053141L; protected String totalRecord; protected String totalPage; protected String curPage; protected String pageLimit; protected String url; protected String curCssClass; protected String showTotalPage; protected String showTotalRecord; protected String directJumpType; protected void populateParams() { super.populateParams(); Pager pager = (Pager) component; pager.setTotalRecord(totalRecord); pager.setTotalPage(totalPage); pager.setCurPage(curPage); pager.setPageLimit(pageLimit); pager.setUrl(url); pager.setCurCssClass(curCssClass); pager.setShowTotalPage(showTotalPage); pager.setShowTotalRecord(showTotalRecord); pager.setDirectJumpType(directJumpType); } @Override public Component getBean(ValueStack stack, HttpServletRequest request, HttpServletResponse response) { return new Pager(stack, request, response); } public void setTotalRecord(String totalRecord){ this.totalRecord = totalRecord; } public void setTotalPage(String totalPage) { this.totalPage = totalPage; } public void setCurPage(String curPage) { this.curPage = curPage; } public void setPageLimit(String pageLimit) { this.pageLimit = pageLimit; } public void setUrl(String url) { this.url = url; } public void setCurCssClass(String curCssClass) { this.curCssClass = curCssClass; } public void setShowTotalPage(String showTotalPage) { this.showTotalPage = showTotalPage; } public void setShowTotalRecord(String showTotalRecord) { this.showTotalRecord = showTotalRecord; } public void setDirectJumpType(String directJumpType) { this.directJumpType = directJumpType; } }
分页标签的ui component类
/** * struts2版的分页标签 * */ @StrutsTag(name = "pager", tldTagClass = "com.meidusa.toolkit.web.page.PagerTag", description = "meidusa pager tag") public class Pager extends UIBean { final public static String TEMPLATE = "pager"; protected String totalRecord; protected String totalPage; protected String curPage; protected String pageLimit; protected String url; protected String curCssClass; protected String showTotalPage; protected String showTotalRecord; protected String directJumpType; public Pager(ValueStack stack, HttpServletRequest request, HttpServletResponse response) { super(stack, request, response); } /** * 用于返回模板的名字,Struts2会自动在后面加入.ftl扩展名以找到特定的模板文件。 */ @Override protected String getDefaultTemplate() { return TEMPLATE; } /** * 设置UIBean的属性,一般Tag中有几个这样的属性,这里就有几个 StrutsTagAttribute注解,说明该属性是int类型,这一步很重要 * * @param totalPage */ @StrutsTagAttribute(description = "total records", type = "Long") public void setTotalRecord(String totalRecord) { this.totalRecord = totalRecord; } @StrutsTagAttribute(description = "total pages", type = "Integer") public void setTotalPage(String totalPage) { this.totalPage = totalPage; } @StrutsTagAttribute(description = "current page", type = "Integer") public void setCurPage(String curPage) { this.curPage = curPage; } @StrutsTagAttribute(description = "how many pages in a panel once", type = "Integer") public void setPageLimit(String pageLimit) { this.pageLimit = pageLimit; } @StrutsTagAttribute(description = "url to be linked", type = "String") public void setUrl(String url) { this.url = url; } @StrutsTagAttribute(description = "css style of current page", type = "String") public void setCurCssClass(String curCssClass) { this.curCssClass = curCssClass; } @StrutsTagAttribute(description = "whether to show totalPage", type = "Boolean", defaultValue = "true") public void setShowTotalPage(String showTotalPage) { this.showTotalPage = showTotalPage; } @StrutsTagAttribute(description = "whether to show currentPage", type = "Boolean", defaultValue = "false") public void setShowTotalRecord(String showTotalRecord) { this.showTotalRecord = showTotalRecord; } // TODO 直接页面跳转 // 这里的directJumpType默认值为none, 可选值为 'select', 'goto' @StrutsTagAttribute(description = "show type of direct jump type. such as select,textbox which can lead going to a page directly", type = "String", defaultValue = "none") public void setDirectJumpType(String directJumpType) { this.directJumpType = directJumpType; } /** * 重写evaluateExtraParams()方法,在UIBean初始化后会调用这个方法来初始化设定参数,如addParameter方法,会在freemarker里的parameters里加入一个key * value。这里要注意findString,还有相关的findxxxx方法,它们是已经封装好了的解释ognl语法的工具,具体是怎么样的,大家可以查看一下UIBean的api * doc */ @Override protected void evaluateExtraParams() { super.evaluateExtraParams(); // findValue()方法本身已对OGNL进行了处理 if (totalRecord != null) { addParameter("totalRecord", findValue(totalRecord)); } if (totalPage != null) { addParameter("totalPage", findValue(totalPage)); } if (curPage != null) { addParameter("curPage", findValue(curPage)); } if (pageLimit != null) { addParameter("pageLimit", findValue(pageLimit)); } if (url != null) { addParameter("url", findValue(url, String.class)); } if (curCssClass != null) { addParameter("curCssClass", findValue(curCssClass,String.class)); } if (showTotalPage != null) { addParameter("showTotalPage", findValue(showTotalPage, Boolean.class)); } if (showTotalRecord != null) { addParameter("showTotalRecord", findValue(showTotalRecord,Boolean.class)); } if (directJumpType != null) { addParameter("directJumpType", findValue(directJumpType)); } } }
除了标签类和ui组件类之外,根据sun的servlet/jsp标准,我们还需要一个标签库的定义,才可以在jsp页面上使用这个标签。
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <!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>m</short-name> <uri>/meidusa-tags</uri> <display-name>"meidusa tags"</display-name> <description><![CDATA["meidusa tags supports custom components that usually used in web develop"]]></description> <tag> <name>pager</name> <tag-class>com.meidusa.toolkit.web.page.PagerTag</tag-class> <body-content>JSP</body-content> <attribute> <name>totalRecord</name> <required>false</required> <rtexprvalue>true</rtexprvalue> <description><![CDATA[总记录数]]></description> </attribute> <attribute> <name>totalPage</name> <required>true</required> <rtexprvalue>true</rtexprvalue> <description><![CDATA[总页数]]></description> </attribute> <attribute> <name>curPage</name> <required>true</required> <rtexprvalue>true</rtexprvalue> <description><![CDATA[当前页号]]></description> </attribute> <attribute> <name>pageLimit</name> <required>false</required> <rtexprvalue>true</rtexprvalue> <description><![CDATA[每版最多显示的页数,最好为奇数]]></description> </attribute> <attribute> <name>url</name> <required>true</required> <rtexprvalue>true</rtexprvalue> <description><![CDATA[翻页时的请求地址,如:list?page={page},其中的{page}会被动态地替换]]></description> </attribute> <attribute> <name>curCssClass</name> <required>false</required> <rtexprvalue>true</rtexprvalue> <description><![CDATA[当前页的span标签的cssClass]]></description> </attribute> <attribute> <name>showTotalPage</name> <required>false</required> <rtexprvalue>true</rtexprvalue> <description><![CDATA[是否显示总页数,默认为"true"]]></description> </attribute> <attribute> <name>showTotalRecord</name> <required>false</required> <rtexprvalue>true</rtexprvalue> <description><![CDATA[是否显示总记录数,只有设置了总记录数属性的时候才有效,默认为"false"]]></description> </attribute> <attribute> <name>directJumpType</name> <required>false</required> <rtexprvalue>true</rtexprvalue> <description><![CDATA[直接跳转的方式,"goto"或"select",默认为none]]></description> </attribute> <!--以下是原UIBean通用的属性--> <attribute> <name>accesskey</name> <required>false</required> <rtexprvalue>false</rtexprvalue> <description><![CDATA[Set the html accesskey attribute on rendered html element]]></description> </attribute> <attribute> <name>cssClass</name> <required>false</required> <rtexprvalue>false</rtexprvalue> <description><![CDATA[The css class to use for element]]></description> </attribute> <attribute> <name>cssStyle</name> <required>false</required> <rtexprvalue>false</rtexprvalue> <description><![CDATA[The css style definitions for element to use]]></description> </attribute> <attribute> <name>disabled</name> <required>false</required> <rtexprvalue>false</rtexprvalue> <description><![CDATA[Set the html disabled attribute on rendered html element]]></description> </attribute> <attribute> <name>id</name> <required>false</required> <rtexprvalue>false</rtexprvalue> <description><![CDATA[id for referencing element. For UI and form tags it will be used as HTML id attribute]]></description> </attribute> <attribute> <name>key</name> <required>false</required> <rtexprvalue>false</rtexprvalue> <description><![CDATA[Set the key (name, value, label) for this particular component]]></description> </attribute> <attribute> <name>label</name> <required>false</required> <rtexprvalue>false</rtexprvalue> <description><![CDATA[Label expression used for rendering a element specific label]]></description> </attribute> <attribute> <name>labelposition</name> <required>false</required> <rtexprvalue>false</rtexprvalue> <description><![CDATA[Define label position of form element (top/left)]]></description> </attribute> <attribute> <name>name</name> <required>false</required> <rtexprvalue>false</rtexprvalue> <description><![CDATA[The name to set for element]]></description> </attribute> <attribute> <name>onblur</name> <required>false</required> <rtexprvalue>false</rtexprvalue> <description><![CDATA[ Set the html onblur attribute on rendered html element]]></description> </attribute> <attribute> <name>onchange</name> <required>false</required> <rtexprvalue>false</rtexprvalue> <description><![CDATA[Set the html onchange attribute on rendered html element]]></description> </attribute> <attribute> <name>onclick</name> <required>false</required> <rtexprvalue>false</rtexprvalue> <description><![CDATA[Set the html onclick attribute on rendered html element]]></description> </attribute> <attribute> <name>ondblclick</name> <required>false</required> <rtexprvalue>false</rtexprvalue> <description><![CDATA[Set the html ondblclick attribute on rendered html element]]></description> </attribute> <attribute> <name>onfocus</name> <required>false</required> <rtexprvalue>false</rtexprvalue> <description><![CDATA[Set the html onfocus attribute on rendered html element]]></description> </attribute> <attribute> <name>onkeydown</name> <required>false</required> <rtexprvalue>false</rtexprvalue> <description><![CDATA[Set the html onkeydown attribute on rendered html element]]></description> </attribute> <attribute> <name>onkeypress</name> <required>false</required> <rtexprvalue>false</rtexprvalue> <description><![CDATA[Set the html onkeypress attribute on rendered html element]]></description> </attribute> <attribute> <name>onkeyup</name> <required>false</required> <rtexprvalue>false</rtexprvalue> <description><![CDATA[Set the html onkeyup attribute on rendered html element]]></description> </attribute> <attribute> <name>onmousedown</name> <required>false</required> <rtexprvalue>false</rtexprvalue> <description><![CDATA[Set the html onmousedown attribute on rendered html element]]></description> </attribute> <attribute> <name>onmousemove</name> <required>false</required> <rtexprvalue>false</rtexprvalue> <description><![CDATA[Set the html onmousemove attribute on rendered html element]]></description> </attribute> <attribute> <name>onmouseout</name> <required>false</required> <rtexprvalue>false</rtexprvalue> <description><![CDATA[Set the html onmouseout attribute on rendered html element]]></description> </attribute> <attribute> <name>onmouseover</name> <required>false</required> <rtexprvalue>false</rtexprvalue> <description><![CDATA[Set the html onmouseover attribute on rendered html element]]></description> </attribute> <attribute> <name>onmouseup</name> <required>false</required> <rtexprvalue>false</rtexprvalue> <description><![CDATA[Set the html onmouseup attribute on rendered html element]]></description> </attribute> <attribute> <name>onselect</name> <required>false</required> <rtexprvalue>false</rtexprvalue> <description><![CDATA[Set the html onselect attribute on rendered html element]]></description> </attribute> <attribute> <name>required</name> <required>false</required> <rtexprvalue>false</rtexprvalue> <description><![CDATA[If set to true, the rendered element will indicate that input is required]]></description> </attribute> <attribute> <name>requiredposition</name> <required>false</required> <rtexprvalue>false</rtexprvalue> <description><![CDATA[Define required position of required form element (left|right)]]></description> </attribute> <attribute> <name>tabindex</name> <required>false</required> <rtexprvalue>false</rtexprvalue> <description><![CDATA[Set the html tabindex attribute on rendered html element]]></description> </attribute> <attribute> <name>template</name> <required>false</required> <rtexprvalue>false</rtexprvalue> <description><![CDATA[The template (other than default) to use for rendering the element]]></description> </attribute> <attribute> <name>templateDir</name> <required>false</required> <rtexprvalue>false</rtexprvalue> <description><![CDATA[The template directory.]]></description> </attribute> <attribute> <name>theme</name> <required>false</required> <rtexprvalue>false</rtexprvalue> <description><![CDATA[The theme (other than default) to use for rendering the element]]></description> </attribute> <attribute> <name>title</name> <required>false</required> <rtexprvalue>false</rtexprvalue> <description><![CDATA[Set the html title attribute on rendered html element]]></description> </attribute> <attribute> <name>tooltip</name> <required>false</required> <rtexprvalue>false</rtexprvalue> <description><![CDATA[Set the tooltip of this particular component]]></description> </attribute> <attribute> <name>tooltipConfig</name> <required>false</required> <rtexprvalue>false</rtexprvalue> <description><![CDATA[Set the tooltip configuration]]></description> </attribute> <attribute> <name>value</name> <required>false</required> <rtexprvalue>false</rtexprvalue> <description><![CDATA[Preset the value of input element.]]></description> </attribute> </tag> <tag> <name>textfield</name> <tag-class>org.apache.struts2.views.jsp.ui.TextFieldTag</tag-class> <body-content>JSP</body-content> <description><![CDATA[Render an HTML input field of type text]]></description> </tag> </taglib>
freemarker模板。根据Struts2的框架,我们可以为不同的主题定义多个freemarker模板,再结合css和参数控制,完全可以做到使用同一个分页标签实现多种风格不同的显示输出。
<#--显示总记录数,只有同时设置了总记录数和显示总记录数时才有效--> <#if (parameters.totalRecord?exists) && (parameters.showTotalRecord?exists) && (parameters.showTotalRecord == true)> <span>${parameters.totalRecord}</span><#rt/> </#if> <#--打印选择页的面板 --> <#--首先,当parameters.pageLimit为空时将其设定为total--> <#if (parameters.pageLimit?exists)> <#assign limit = parameters.pageLimit?number /> <#else> <#assign limit = parameters.totalPage?number /> </#if> <#assign cur = (parameters.curPage!1)?number /> <#assign total = parameters.totalPage?number /> <@pagePanel cur=cur total=total url=parameters.url limit=limit /> <#--打印选择页的面板结束--> <#--显示总页数--> <#if parameters.showTotalPage?exists && (parameters.showTotalPage == true)> <span><a>共${parameters.totalPage}页</a></span><#rt/> </#if> <#--打印翻页面板的宏,配合printPage,printButton--> <#macro pagePanel cur total limit url curCssClass = "cur_page"><#--curCssClass默认值为cur_page--> <#--limit的中间数--> <#assign l_mid = (limit/2)?int + 1 /> <#--total的中间数--> <#assign t_mid = (total/2)?int /> <#--情况一:总页数小于等于限制显示页数,这时显示所有页--> <#if total ==0> <#elseif total ==1> <#elseif (cur ==1) && (total <= limit)> <@printPage left = 1 right = total cur = cur url = url curCssClass = curCssClass /> <@printNext/> <#elseif (cur ==total) && (total <= limit)> <@printPrev/> <@printPage left = 1 right = total cur = cur url = url curCssClass = curCssClass /> <#elseif (cur > 1) && (cur < total) && (total <= limit)> <@printPrev/> <@printPage left = 1 right = total cur = cur url = url curCssClass = curCssClass /> <@printNext/> <#else> <#--情况二:总页数大于限制显示页数,这时又分三种情况--> <#--情况1:显示的limit个页号在total个页面中偏向左端,1作为最左边的页号,当前页没能显示在中间,偏左,例: total = 20,cur = 2,limit = 5.显示的页面为:1 [2] 3 4 5 这种情况 cur <= l_mid --> <#if cur <= l_mid> <#if cur !=1> <@printPrev/> </#if> <@printPage left = 1 right = limit cur = cur url = url curCssClass = curCssClass /> <@printNext/> <@printEnd/> <#--情况2:显示的limit个页号在total个页面中偏向右端,total作为最右边的页号,当前页没能显示在中间,偏右,例: total = 20,cur = 19,limit = 5.显示的页面为:16 17 18 [19] 20 这种情况 cur > total - l_mid --> <#elseif (cur > (total - l_mid))> <@printStart/> <@printPrev/> <@printPage left = (total - limit + 1) right = total cur = cur url = url curCssClass = curCssClass /> <#if cur !=total> <@printNext/> </#if> <#--在中间的情况--> <#else> <@printStart/> <@printPrev/> <@printPage left = (cur - l_mid + 1) right = (cur + l_mid -1) cur = cur url = url curCssClass = curCssClass /> <@printNext/> <@printEnd/> </#if> </#if> </#macro> <#--根据最左与最右的页号来打印所显示的页面,当前页为的cssStyle为curCssClass--> <#macro printPage left right cur url curCssClass> <#list left..right as p> <#if p == cur> <span class = "${curCssClass}" >${p}</span><#rt/> <#else> <a href = "<@makeURL text=url page=p />">${p}</a><#rt/> </#if> </#list> </#macro> <#macro printStart> <a href="<@makeURL text=parameters.url page=1 />"> 首页 </a><#rt/> </#macro> <#macro printEnd> <a href="<@makeURL text=parameters.url page=total />"> 末页 </a><#rt/> </#macro> <#macro printNext> <a href="<@makeURL text=parameters.url page=cur+1 />"> 下一页 </a><#rt/> </#macro> <#macro printPrev> <a href="<@makeURL text=parameters.url page=cur-1 />"> 上一页 </a><#rt/> </#macro> <#--产生动态URL的宏--> <#macro makeURL text page> <#if text?last_index_of("{page}") < 0> ${text}?page=${page} <#else> ${text?replace("{page}",page)} </#if> </#macro>
在jsp页面中我们需要首先载入标签库
<%@ taglib prefix="m" uri="/meidusa-tags" %>
然后在页面中的调用就现得十分简单,我们只需要给出total page和total record的参数同时配合具体负责列表显示的aciton即可,在这个负责列表显示的aciton中,我们需要给出两个参数,pno和rows。pno指定当前显示第几页,rows则表示每页显示多少条记录。
<m:pager curPage="%{#parameters.pno[0]}" totalPage="%{#paging.totalPage}" theme="simple" url="/demo/browse.action?pno={page}" pageLimit="5" showTotalPage="false" totalRecord="%{#paging.totalRecord}" showTotalRecord="false" /> <s:action name="post!list" namespace="/demo" executeResult="true" > <s:param name="pno" value="#parameters.pno"/> <s:param name="rows" value="10"/> </s:action>
照理说这样已经可以获得不错的重用性了,但问题是对于total page和total record,如果每次执行不同的业务逻辑都需要单独手工书写业务代码获取就显得十分的繁琐。那有没有一种比较方便的,并且可以通用的方法获得total page和total record呢?
在这里我们使用一个专门获取total page和total record的action。在调用这个aciton的时候我们只要告诉它我们需要每页显示多少条记录,它会帮我们计算出total page和total record。
<s:action name="postPaging" id="paging" namespace="/demo" executeResult="false"> <s:param name="rows" value="10"/> </s:action>
这个aciton的实现非常简单,只是简单的继承了一下我们的PagingAction类
public class PagingAction extends com.meidusa.toolkit.web.common.action.PagingAction<Cookie> { }
PagingAction类的实现,主要调用的baseAO中的doPaging方法。
public class PagingAction<T extends ClientCookie> extends ActionSupport implements ClientCookieAware<T>{ private int totalPage; private int totalRecord; private int rows; private BaseAO<?> ao; private T cookie; public void setAo(BaseAO<?> ao) { this.ao = ao; } public void setClientCookie(T cookie){ this.cookie = cookie; } public int getTotalPage() { return totalPage; } public void setTotalPage(int totalPage) { this.totalPage = totalPage; } public long getTotalRecord() { return totalRecord; } public void setTotalRecord(int totalRecord) { this.totalRecord = totalRecord; } public int getRows() { return rows; } public void setRows(int rows) { this.rows = rows; } public String execute(){ Result result = ao.doPaging(rows, getForeignKey()); if (!result.isSuccess()){ ResultCode resultCode = result.getResultCode(); this.addActionError(resultCode.getMessage().getMessage()); return ERROR; } totalPage = (Integer)result.getModels().get("totalPage"); totalRecord = (Integer)result.getModels().get("totalRecord"); return SUCCESS; } public String getForeignKey(){ return cookie.getLoginId(); } }
doPaging在AbstractAO中的默认实现。它首先取得total record,然后再根据rows,也就是每页显示的记录数计算出total page的值。
public Result doPaging(int rows, String foreignKey){ Result result = new ResultSupport(); result.setSuccess(false); K example = doExample(foreignKey); int totalRecord = dao.countByExample(example); int totalPage = (int)Math.ceil(totalRecord / (double)rows); result.setDefaultModel("totalRecord", totalRecord); result.setDefaultModel("totalPage", totalPage); result.setSuccess(true); return result; }
我们再回过头来看一下在显示列表的时候具体是如何实现分页的(不是分页标签,而是对具体显示数据的分页)。在这个jsp页面中,调用了post action中的list方法。
<s:action name="post!list" namespace="/demo" executeResult="true" > <s:param name="pno" value="#parameters.pno"/> <s:param name="rows" value="10"/> </s:action>
post aciton本身非常的简单,主要是继承了我们的baseAciotn(在上一篇介绍CRUDL文章中已经详细讲解过,这里主要看一下跟分页相关的代码)。在list方法中主要是调用的业务层ao的doList方法。在调用这个方法时候还传入了一个paging作为参数,那么这个paging究竟是什么呢?
@SkipValidation public String list() { Result result = ao.doList(requestId, paging); if (result.isSuccess()){ list = (List)result.getDefaultModel(); return Constants.LIST; }else { ResultCode resultCode = result.getResultCode(); this.addActionError(resultCode.getMessage().getMessage()); return ERROR; } }
paging就是一个pojo,非常的简单,主要就是包含了分页所需要的信息。pno表示我们需要第几页的数据,rows表示每页显示多少条记录,offset则是通过pno和rows计算出来的,表示从第几条数据开始取,主要是针对mysql数据库的分页实现。
public class Paging { private int pno; private int rows; public int getPno() { return pno; } public void setPno(int pno) { this.pno = pno; } public int getRows() { return rows; } public void setRows(int rows) { this.rows = rows; } public int getOffset(){ if (pno == 0){ pno = 1; } return (pno-1)*rows; } }
在调用post!list方法时会通过一个PagingIntercept拦截器把参数rows的值注入到这个paging对象中,这个拦截器有点类似于modelDriven拦截器,只不过aciton需要实现Pageable接口。我们的baseAciton默认的实现了这个接口。
public class PagingIntercept extends AbstractInterceptor { @Override public String intercept(ActionInvocation invocation) throws Exception { Object action = invocation.getAction(); if (action instanceof Pageable) { Pageable pageable = (Pageable) action; ValueStack stack = invocation.getStack(); if (pageable.getPaging() != null) { stack.push(pageable.getPaging()); } } return invocation.invoke(); } }
我们的baseAciton默认的实现了这个接口。
public interface Pageable { public Paging getPaging(); }
那么再来看一下业务层ao的doList方法又是如何做到分页的呢,AbstractAO对doList的默认实现
public Result doList(String foreignKey, Paging paging){ Result result = new ResultSupport(); result.setSuccess(false); K example = doExample(foreignKey); if (paging.getRows()!=0){ example.setOffset(paging.getOffset()); example.setRows(paging.getRows()); } List<T> list = dao.selectByExampleWithoutBLOBs(example); result.setDefaultModel(list); result.setSuccess(true); return result; }
在调用dao层得到列表的同时传入了一个example对象作为参数,而这个example对象则包含了需要分页的信息,rows和offset。那么这个example到底是个什么东西呢?
如果大家用过Ibator这个工具的话一定不会对example感到陌生,在本系列的上一篇当中也提到了Ibator这个工具,它是一个eclipse插件,专门用来自动生成ibatis的代码,除了model,dao接口及实现,sqlmapper外还包括了example对象。这个example对象可以自动生成针对SELECT语句的WHERE字句的各种条件的Criteria,而笔者对Ibator做了一番简单的改造,使其生成的所有example对象都继承自一个抽象类,并且加入了针对mysql数据库的分页支持。笔者把这个工具叫做IbatorPlus。
AbstractExample
public abstract class AbstractExample<T extends AbstractExample.Criteria> { protected String orderByClause; protected List<T> oredCriteria; public AbstractExample() { oredCriteria = new ArrayList<T>(); } protected int offset; protected int rows; protected boolean distinct; public boolean isDistinct() { return distinct; } public void setDistinct(boolean distinct) { this.distinct = distinct; } public void setOffset(int offset){ this.offset = offset; } public int getOffset(){ return offset; } public void setRows(int rows){ this.rows = rows; } public int getRows(){ return rows; } protected AbstractExample(AbstractExample<T> example) { this.orderByClause = example.orderByClause; this.oredCriteria = example.oredCriteria; this.offset = example.offset; this.rows = example.rows; this.distinct = example.distinct; } public void setOrderByClause(String orderByClause) { this.orderByClause = orderByClause; } public String getOrderByClause() { return orderByClause; } public List<T> getOredCriteria() { return oredCriteria; } public void or(T criteria) { oredCriteria.add(criteria); } public T createCriteria() { T criteria = createCriteriaInternal(); if (oredCriteria.size() == 0) { oredCriteria.add(criteria); } return criteria; } protected abstract T createCriteriaInternal(); public void clear() { oredCriteria.clear(); orderByClause = null; distinct = false; offset = 0; rows = 0; } public static abstract class Criteria { protected List<String> criteriaWithoutValue; protected List<Map<String, Object>> criteriaWithSingleValue; protected List<Map<String, Object>> criteriaWithListValue; protected List<Map<String, Object>> criteriaWithBetweenValue; protected Criteria() { super(); criteriaWithoutValue = new ArrayList<String>(); criteriaWithSingleValue = new ArrayList<Map<String, Object>>(); criteriaWithListValue = new ArrayList<Map<String, Object>>(); criteriaWithBetweenValue = new ArrayList<Map<String, Object>>(); } public boolean isValid() { return criteriaWithoutValue.size() > 0 || criteriaWithSingleValue.size() > 0 || criteriaWithListValue.size() > 0 || criteriaWithBetweenValue.size() > 0; } public List<String> getCriteriaWithoutValue() { return criteriaWithoutValue; } public List<Map<String, Object>> getCriteriaWithSingleValue() { return criteriaWithSingleValue; } public List<Map<String, Object>> getCriteriaWithListValue() { return criteriaWithListValue; } public List<Map<String, Object>> getCriteriaWithBetweenValue() { return criteriaWithBetweenValue; } protected void addCriterion(String condition) { if (condition == null) { throw new RuntimeException("Value for condition cannot be null"); } criteriaWithoutValue.add(condition); } protected void addCriterion(String condition, Object value, String property) { if (value == null) { throw new RuntimeException("Value for " + property + " cannot be null"); } Map<String, Object> map = new HashMap<String, Object>(); map.put("condition", condition); map.put("value", value); criteriaWithSingleValue.add(map); } protected void addCriterion(String condition, List<? extends Object> values, String property) { if (values == null || values.size() == 0) { throw new RuntimeException("Value list for " + property + " cannot be null or empty"); } Map<String, Object> map = new HashMap<String, Object>(); map.put("condition", condition); map.put("values", values); criteriaWithListValue.add(map); } protected void addCriterion(String condition, Object value1, Object value2, String property) { if (value1 == null || value2 == null) { throw new RuntimeException("Between values for " + property + " cannot be null"); } List<Object> list = new ArrayList<Object>(); list.add(value1); list.add(value2); Map<String, Object> map = new HashMap<String, Object>(); map.put("condition", condition); map.put("values", list); criteriaWithBetweenValue.add(map); } public abstract Criteria andForeignKeyEqualTo(String value); } }
ibatorPlus生成的example类
public class PostExample extends AbstractExample<PostExample.Criteria> { /** * This method was generated by Apache iBATIS Ibator. * This method corresponds to the database table post * * @ibatorgenerated Thu Feb 04 16:15:41 CST 2010 */ public PostExample() { super(); } /** * This method was generated by Apache iBATIS Ibator. * This method corresponds to the database table post * * @ibatorgenerated Thu Feb 04 16:15:41 CST 2010 */ public PostExample(PostExample example) { super(example); } /** * This method was generated by Apache iBATIS Ibator. * This method corresponds to the database table post * * @ibatorgenerated Thu Feb 04 16:15:41 CST 2010 */ protected Criteria createCriteriaInternal() { Criteria criteria = new Criteria(); return criteria; } /** * This class was generated by Apache iBATIS Ibator. * This class corresponds to the database table post * * @ibatorgenerated Thu Feb 04 16:15:41 CST 2010 */ public static class Criteria extends AbstractExample.Criteria { public Criteria andForeignKeyEqualTo(String value) { addCriterion("loginId like", value, "loginId"); return this; } public Criteria andIdIsNull() { addCriterion("id is null"); return this; } public Criteria andIdIsNotNull() { addCriterion("id is not null"); return this; } public Criteria andIdEqualTo(String value) { addCriterion("id =", value, "id"); return this; } public Criteria andIdNotEqualTo(String value) { addCriterion("id <>", value, "id"); return this; } public Criteria andIdGreaterThan(String value) { addCriterion("id >", value, "id"); return this; } public Criteria andIdGreaterThanOrEqualTo(String value) { addCriterion("id >=", value, "id"); return this; } public Criteria andIdLessThan(String value) { addCriterion("id <", value, "id"); return this; } public Criteria andIdLessThanOrEqualTo(String value) { addCriterion("id <=", value, "id"); return this; } public Criteria andIdLike(String value) { addCriterion("id like", value, "id"); return this; } public Criteria andIdNotLike(String value) { addCriterion("id not like", value, "id"); return this; } public Criteria andIdIn(List<String> values) { addCriterion("id in", values, "id"); return this; } public Criteria andIdNotIn(List<String> values) { addCriterion("id not in", values, "id"); return this; } public Criteria andIdBetween(String value1, String value2) { addCriterion("id between", value1, value2, "id"); return this; } public Criteria andIdNotBetween(String value1, String value2) { addCriterion("id not between", value1, value2, "id"); return this; } public Criteria andTitleIsNull() { addCriterion("title is null"); return this; } public Criteria andTitleIsNotNull() { addCriterion("title is not null"); return this; } public Criteria andTitleEqualTo(String value) { addCriterion("title =", value, "title"); return this; } public Criteria andTitleNotEqualTo(String value) { addCriterion("title <>", value, "title"); return this; } public Criteria andTitleGreaterThan(String value) { addCriterion("title >", value, "title"); return this; } public Criteria andTitleGreaterThanOrEqualTo(String value) { addCriterion("title >=", value, "title"); return this; } public Criteria andTitleLessThan(String value) { addCriterion("title <", value, "title"); return this; } public Criteria andTitleLessThanOrEqualTo(String value) { addCriterion("title <=", value, "title"); return this; } public Criteria andTitleLike(String value) { addCriterion("title like", value, "title"); return this; } public Criteria andTitleNotLike(String value) { addCriterion("title not like", value, "title"); return this; } public Criteria andTitleIn(List<String> values) { addCriterion("title in", values, "title"); return this; } public Criteria andTitleNotIn(List<String> values) { addCriterion("title not in", values, "title"); return this; } public Criteria andTitleBetween(String value1, String value2) { addCriterion("title between", value1, value2, "title"); return this; } public Criteria andTitleNotBetween(String value1, String value2) { addCriterion("title not between", value1, value2, "title"); return this; } public Criteria andLoginidIsNull() { addCriterion("loginId is null"); return this; } public Criteria andLoginidIsNotNull() { addCriterion("loginId is not null"); return this; } public Criteria andLoginidEqualTo(String value) { addCriterion("loginId =", value, "loginid"); return this; } public Criteria andLoginidNotEqualTo(String value) { addCriterion("loginId <>", value, "loginid"); return this; } public Criteria andLoginidGreaterThan(String value) { addCriterion("loginId >", value, "loginid"); return this; } public Criteria andLoginidGreaterThanOrEqualTo(String value) { addCriterion("loginId >=", value, "loginid"); return this; } public Criteria andLoginidLessThan(String value) { addCriterion("loginId <", value, "loginid"); return this; } public Criteria andLoginidLessThanOrEqualTo(String value) { addCriterion("loginId <=", value, "loginid"); return this; } public Criteria andLoginidLike(String value) { addCriterion("loginId like", value, "loginid"); return this; } public Criteria andLoginidNotLike(String value) { addCriterion("loginId not like", value, "loginid"); return this; } public Criteria andLoginidIn(List<String> values) { addCriterion("loginId in", values, "loginid"); return this; } public Criteria andLoginidNotIn(List<String> values) { addCriterion("loginId not in", values, "loginid"); return this; } public Criteria andLoginidBetween(String value1, String value2) { addCriterion("loginId between", value1, value2, "loginid"); return this; } public Criteria andLoginidNotBetween(String value1, String value2) { addCriterion("loginId not between", value1, value2, "loginid"); return this; } } }
使用ibatorPlus生成的sqlmap也自动实现对分页的支持。
<select id="selectByExample" parameterClass="com.meidusa.demo.dal.model.PostExample" resultMap="BaseResultMap"> <!-- WARNING - @ibatorgenerated This element is automatically generated by Apache iBATIS Ibator, do not modify. This element was generated on Thu Feb 04 16:15:41 CST 2010. --> select <isParameterPresent> <isEqual compareValue="true" property="distinct"> distinct </isEqual> </isParameterPresent> <include refid="post.Base_Column_List" /> from post <isParameterPresent> <include refid="post.Example_Where_Clause" /> <isNotNull property="orderByClause"> order by $orderByClause$ </isNotNull> <isNotEqual compareValue="0" property="rows"> limit $offset$, $rows$ </isNotEqual> </isParameterPresent> </select>
看到这里有些读者朋友可能会问,那如果我用的数据库不是mysql怎么办呢?在回答这个问题前,不妨先来看一下我们的ibatorPlus配置文件,这个配置完全遵照了ibator配置文件的dtd规范,笔者没有改动。
有所不同的是,targetRuntime被替换成我们自己的实现类HierarchyIntrospectedTable,从而我们自动生成的model类,example类和dao接口都继承自一个抽象父类或抽象接口,使得我们可以对所有的CRUDL以及分页行为抽象,在具体的应用中只要继承自这些抽象父类或接口就会自动获得所有的默认功能。在这里,笔者也不禁想说,ibator本身就提供了很好的扩展机制,我们不仅可以替换targetRuntime的实现,还可以有针对性的写一些插件,比如在生成的sqlmap中加入对mysql分页的支持是通过MysqlPagingPlugin这个插件来实现的。
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE ibatorConfiguration PUBLIC "-//Apache Software Foundation//DTD Apache iBATIS Ibator Configuration 1.0//EN" "http://ibatis.apache.org/dtd/ibator-config_1_0.dtd" > <ibatorConfiguration > <classPathEntry location="D:\Temp\mysql-connector-java-5.1.6.jar" /> <ibatorContext id="context1" targetRuntime="com.meidusa.toolkit.plugin.ibator.core.HierarchyIntrospectedTable"> <ibatorPlugin type="com.meidusa.toolkit.plugin.ibator.core.MysqlPagingPlugin"/> <jdbcConnection driverClass="com.mysql.jdbc.Driver" connectionURL="jdbc:mysql://localhost:3306/demo" userId="root"> </jdbcConnection> <javaModelGenerator targetPackage="com.meidusa.demo.dal.model" targetProject="demo-dal/src/main/java" > <property name="exampleRootInnerClass" value="com.meidusa.toolkit.dal.ibator.AbstractExample.Criteria"/> <property name="rootClass" value="com.meidusa.toolkit.dal.ibator.AbstractModel"/> </javaModelGenerator> <sqlMapGenerator targetPackage="com.meidusa.demo.dal.sqlmap" targetProject="demo-dal/src/main/resources" /> <daoGenerator targetPackage="com.meidusa.demo.dal" targetProject="demo-dal/src/main/java" type="SPRING" > </daoGenerator> <table schema="demo" tableName="post" domainObjectName="Post"> <property name="exampleRootClass" value="com.meidusa.toolkit.dal.ibator.AbstractExample<com.meidusa.demo.dal.model.PostExample.Criteria>"/> <property name="rootInterface" value="com.meidusa.toolkit.dal.ibator.AbstractDAO<Post,PostExample>"/> </table> </ibatorContext> </ibatorConfiguration>
评论
大哥厉害,最近我也在研究S2SI的东西。
发表评论
-
基于Struts2+Spring+iBatis的web应用最佳实践系列之七(Demo篇)
2010-02-08 15:01 3125终于来到了本系列的最 ... -
基于Struts2+Spring+iBatis的web应用最佳实践系列之六(验证码篇)
2010-02-05 18:34 1809本篇主要讨论下如何使用Struts2实现一个通用的验证码(ch ... -
基于Struts2+Spring+iBatis的web应用最佳实践系列之四(CRUDL篇)
2010-02-04 23:08 3152让我们首先来看一下什 ... -
基于Struts2+Spring+iBatis的web应用最佳实践系列之三(访问控制篇下)
2010-02-03 18:47 2293在本系列的上一篇中我 ... -
基于Struts2+Spring+iBatis的web应用最佳实践系列之二(访问控制篇上)
2010-02-03 15:40 2477访问控制对于一个web应用来说几乎是不可或缺的。当访问web应 ... -
基于Struts2+Spring+iBatis的web应用最佳实践系列之一(自动配置篇)
2010-02-02 00:13 3737由于最近有点时间,便 ...
相关推荐
综上所述,这个实例展示了如何整合Struts2、Spring、iBatis和Oracle来构建一个完整的Web应用,实现了动态分页搜索和附件上传功能。这种架构具有良好的可扩展性和可维护性,适用于各种中大型企业级项目。开发者可以...
Struts2+Spring2.5+Ibatis2.3架构是一种经典的Java Web开发技术栈,广泛应用于企业级应用系统中。这个架构结合了Struts2的MVC框架、Spring的依赖注入(DI)和面向切面编程(AOP)以及Ibatis的持久层解决方案,为...
【标题】"简单公文管理"是一个基于Struts、Spring、iBatis和Ajax技术实现的Web应用程序,它主要用于管理和追踪组织内部的公文流转。这个项目的核心目标是提高工作效率,确保公文处理的规范性和及时性。 【描述】...
Struts2、Spring和iBatis是Java Web开发中常用的三个框架,它们分别负责MVC模式中的Action层、业务逻辑层以及数据访问层。在这个"struts2+spring+ibatis 实现分页"的项目中,我们将探讨如何将这三个框架整合起来,...
Struts2、Spring和iBatis是Java Web开发中经典的三大框架组合,它们协同工作能够构建出高效、灵活的企业级应用程序。在这个“struts2+spring+ibatis增删查改翻页代码示例”中,我们将深入探讨这三个框架如何协同实现...
Struts2、Spring和iBatis是Java Web开发中常用的三个框架,它们组合起来可以构建出高效、可维护的Web应用程序。在这个例子中,我们将深入探讨如何利用这三个框架实现分页功能。 首先,Struts2作为MVC(模型-视图-...
Struts2+Spring+Ibatis学生管理Demo是一个典型的Java Web应用程序,它展示了如何将三个流行的开源框架——Struts2、Spring和Ibatis有效地集成在一起,用于构建高效且可维护的学生信息管理系统。在这个系统中,Struts...
### Struts2 + Spring + iBatis 整合详解 #### 一、概述 随着企业级应用需求的不断增加,为了更好地实现项目的模块化管理和提高代码的可维护性,越来越多的项目选择采用MVC架构模式,并结合不同的框架进行开发。...
**SSI+EXT(Struts2+Spring+Ibatis+Ext)**是一个常见的Java Web开发框架组合,用于构建高效、可扩展的企业级应用。这个框架集合了Struts2作为MVC框架,Spring作为依赖注入和事务管理工具,Ibatis作为持久层解决方案...
Struts2、Spring和iBatis是Java Web开发中常用的三个框架,它们组合起来可以构建出高效、可维护的企业级应用程序。Struts2是一个MVC(Model-View-Controller)框架,Spring则是一个全面的后端解决方案,包括依赖注入...
在Java Web开发中,"Struts2+Spring+IBatis"是一个常见的企业级应用框架组合,也被称为SSI(Struts2、Spring、iBatis)框架。这种组合提供了模型-视图-控制器(MVC)架构,事务管理,以及灵活的数据访问机制。以下是...
Struts2、Spring和iBatis是Java Web开发中经典的三大框架,它们组合在一起形成了所谓的SSI(Struts2、Spring、iBatis)架构。这个实例项目提供了使用这些框架实现基本CRUD(创建、读取、更新、删除)操作以及分页...
struts spring ibatis mysql 分页,增删改查,以及导出excle
总结起来,"struts+spring+ibatis(SSI)的最简分页及标签"是一个关于如何在Java Web开发中结合Struts的MVC架构、Spring的依赖管理和iBatis的数据访问来实现分页功能以及创建自定义标签的实践。这个过程涉及到Struts的...
这里我们讨论的是一个基于Ibatis、Struts2、Spring3的整合应用,涵盖了数据访问、业务逻辑和控制层的关键技术,以及分页和CRUD操作的实现。下面我们将详细探讨这些知识点。 1. **Ibatis**:Ibatis是一个优秀的持久...
Struts、Spring、iBatis和MySQL是Java Web开发中常用的四大框架,它们结合使用可以构建功能丰富的Web应用程序。在这个小例子中,我们将探讨如何利用这些技术实现分页、增删改查(CRUD)操作以及Excel数据导出。 1. ...
Struts2、Spring2、iBatis2、jQuery 和 JSON 是构建现代Web应用程序的重要技术栈。这个项目似乎演示了如何利用这些技术实现页面无刷新分页功能,这在提升用户体验和提高网页性能方面非常关键。下面我们将详细探讨...
标题中的"ssi_struts2_spring_ibatis"指的是一个基于Java技术栈的Web应用程序开发框架组合,主要包括Struts2、Spring和iBatis。这三个组件是企业级Java应用中的常见选择,它们各自承担着不同的职责。 1. **Struts2*...
Struts2、Spring和iBatis是Java Web开发中常用的三大框架,它们分别负责MVC模式中的动作控制、依赖注入和数据库操作。本教程将详细讲解如何整合这三大框架,并利用存储过程实现分页功能。 首先,让我们了解这三个...