分页也是一个大家经常讨论的话题,网上也有过很多的分页方法的介绍,但往往仅仅只是局限在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类
Java代码
/**
* 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类
Java代码
/**
* 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>JAVA 教程,FENGfly.com http://www.fengfly.com/ComputerNetwork/ 雨 枫技 术 教程 网 WWW.FENGFLY.COM 雨枫 技术 教程 FengFLY.com JAVA 教程,FENGfly.com
原文来自:雨枫技术教程网 http://www.fengfly.com
原文网址:http://www.fengfly.com/plus/view-169039-1.html
首先,在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类
Java代码
/**
* 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类
Java代码
/**
* 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>JAVA 教程,FENGfly.com http://www.fengfly.com/ComputerNetwork/ 雨 枫技 术 教程 网 WWW.FENGFLY.COM 雨枫 技术 教程 FengFLY.com JAVA 教程,FENGfly.com
原文来自:雨枫技术教程网 http://www.fengfly.com
原文网址:http://www.fengfly.com/plus/view-169039-1.html
相关推荐
U盘量产工具FLASH量产工具SM3280&3281&3282-AvidiaV0209整合版
java课程期末考试
分布式消息中间件,参考kafka,未完成
修木工施工规范及流程.docx
内容概要:本文详细介绍了VECTOR提供的MICROSAR OBD协议栈解决方案,涵盖了OBD模块、ECU支持、监控功能和服务请求等方面的内容。此外,还讨论了OBD在不同国家和地区的技术标准与法规要求,以及MICROSAR OBD解决方案的优势,如适应不同项目的需求和高度集成于AUTOSAR 4平台。 适合人群:汽车电子工程师、软件开发者、汽车制造商及相关行业从业人员。 使用场景及目标:① 适用于车辆诊断系统的开发和维护;②帮助工程师理解和掌握OBD协议的具体实施方法和应用场景;③ 提供了一个成熟、可扩展的解决方案,用于满足OBD相关标准和法规的要求。 其他说明:本文不仅提供了技术层面的详细解析,还探讨了实际操作过程中可能遇到的问题和解决方案。同时强调了屏蔽信息过载的重要性,提醒工程师保持内心平静,专注做好本职工作。
适用于 Python 的 LINE 消息 API SDK适用于 Python 的 LINE Messaging API 的 SDK。介绍适用于 Python 的 LINE Messaging API SDK 可以轻松使用 LINE Messaging API 开发机器人,您可以在几分钟内创建一个示例机器人。文档请参阅官方 API 文档了解更多信息英语https //developers.line.biz/en/docs/messaging-api/overview/日语https://developers.line.biz/ja/docs/messaging-api/overview/要求Python >= 3.9安装$ pip 安装 line-bot-sdk概要用法from flask import Flask, request, abortfrom linebot.v3 import ( WebhookHandler)from linebot.v3.exceptions import ( InvalidSig
Java字节码工程工具包Javassist 版本 3版权所有 (C) 1999-2023 Shigeru Chiba,保留所有权利。Javassist(JAVA 编程助手)使 Java 字节码操作变得简单。它是一个用于编辑 Java 字节码的类库它使 Java 程序能够在运行时定义新类并在 JVM 加载类文件时对其进行修改。与其他类似的字节码编辑器不同,Javassist 提供两个级别的 API源代码级别和字节码级别。如果用户使用源代码级别 API,他们可以编辑类文件而无需了解 Java 字节码的规范。整个 API 仅使用 Java 语言的词汇表进行设计。您甚至可以以源文本的形式指定插入的字节码Javassist 会即时编译它。另一方面,字节码级别 API 允许用户像其他编辑器一样直接编辑类文件。该软件根据 Mozilla 公共许可证版本 1.1、GNU 宽通用公共许可证版本 2.1 或更高版本或 Apache 许可证版本 2.0 分发。文件README.md 此自述文件。Changes.md 发行说明。License.html 许可证文件。tuto
本项目是基于Python语言开发的西西家居全屋定制系统,旨在为家居行业提供一个高效、智能的定制解决方案。项目涵盖了从客户需求分析、设计方案生成、材料选购到最终订单生成的全过程,力求实现家居定制的数字化和智能化。 在主要功能方面,系统具备强大的客户管理模块,能够详细记录和分析客户的定制需求。设计模块则采用先进的三维建模技术,为客户提供直观、真实的家居设计方案预览。此外,系统还整合了丰富的材料数据库,方便客户根据自身喜好和预算进行材料选择。 框架方面,项目采用了B/S架构,确保了系统的稳定性和可扩展性。后端使用Python的Django框架,前端则结合了HTML、CSS和JavaScript等技术,实现了用户界面的友好和响应速度。 开发此项目的目的,不仅是为了满足家居行业对个性化定制的需求,也为计算机相关专业的学生提供了一个实践和学习的平台,有助于提升他们的实际开发能力。
Javascript 是数字化创新的起点,是语言的基础,也是基本概念。Basecamp JavascriptJavascript 是数字化创新的起点,是语言的基础,也是基本概念。嵌套存储库,可作为启动项下待办事项的实践活动。
已弃用 — Coinbase Python APICoinbase Coinbase API V2的官方 Python 库。重要提示此库当前针对的是 API V2,而 OAuth 客户端需要 V2 权限(即wallet:accounts:read)。如果您仍在使用 API V1,请使用此库的旧版本。特征接近 100% 的测试覆盖率。支持API Key + Secret和OAuth 2身份验证。调用 API 的便捷方法 - 为您打包 JSON!自动将 API 响应解析为相关的 Python 对象。使用IPython时,所有对象都具有可制表完成的方法和属性。安装coinbase可以在PYPI上使用。使用以下命令安装pippip install coinbase或者easy_installeasy_install coinbase该库目前针对 Python 版本 2.7 和 3.4+ 进行了测试。注意此软件包名称过去是指George Sibble维护的非官方 coinbase_python 库。George 慷慨地允许我们使用此软件包
基于RBAC权限控制的基础后台
本项目是基于Python爬虫的网络小说数据分析系统的设计与实现,旨在为计算机相关专业的大学生提供一个实践平台,特别是在毕业设计和项目实战练习方面。项目通过Python强大的网络爬虫技术,从流行的网络小说网站自动抓取数据,包括书籍信息、章节内容、用户评论等。 主要功能涵盖数据采集、数据清洗、数据存储和数据分析。数据采集模块利用Scrapy等爬虫框架高效抓取网页内容;数据清洗模块确保数据的准确性和一致性;数据存储则采用MySQL等数据库系统,便于数据管理和查询;数据分析模块通过Pandas、NumPy等工具进行数据处理和分析,生成多维度的统计报告和可视化图表。 此项目不仅帮助学生掌握Python编程和网络爬虫技术,还能让他们深入了解数据分析的全过程,提升解决实际问题的能力。同时,系统的实现和应用也反映了现代信息技术在文学创作和消费领域的应用价值和潜力。
本项目是一个基于Java的在线日语培训平台的设计与实现,采用SSM框架(Spring+SpringMVC+MyBatis)进行开发,旨在为计算机相关专业的学生提供一个实践和学习的平台,同时也为日语学习者提供一个在线学习的空间。项目中主要功能涵盖了用户管理、课程管理、学习资源上传下载、在线测试与反馈等多个方面。通过该平台,教师能够轻松管理课程内容和学生信息,学生则可以随时随地访问学习资源,参与在线课程和测试,从而提高学习效率和兴趣。 在开发此项目的过程中,我们重点关注了系统的可维护性和可扩展性,确保代码结构清晰,便于后续的功能迭代和优化。此外,通过使用SSM框架,实现了前后端的分离,提高了开发效率和系统的响应速度。该项目不仅能够满足毕设的需求,还能作为Java学习者提升编程能力和实践经验的实用工具。
基于java的机票管理系统设计与实现.docx
该项目为《基于Java实现的数据结构设计源码》,共包含51个文件,主要由46个Java源文件构成,辅以2个文本文件、1个Git忽略文件、1个许可证文件以及1个XML文件,全面涵盖了数据结构设计的核心内容。
绿色食品 水稻生产操作规程.docx
他妈的 Fuck是一款出色的应用程序,其灵感来自@liamosaur 的 推文,它可以纠正以前控制台命令中的错误。The Fuck太慢了吗?试试实验性的即时模式!更多示例➜ apt-get install vimE: Could not open lock file /var/lib/dpkg/lock - open (13: Permission denied)E: Unable to lock the administration directory (/var/lib/dpkg/), are you root?➜ fucksudo apt-get install vim [enter/↑/↓/ctrl+c][sudo] password for nvbn:Reading package lists... Done...➜ git pushfatal: The current branch master has no upstream branch.To push the current branch and set the remote
全国大学生FPGA创新设计竞赛作品 “泡罩包装药品质量在线检测平台“.zip
桃苗木质量基本要求表.docx
使用 Python 漂亮地打印表格数据,这是一个库和一个命令行实用程序。存储库从 bitbucket.org/astanin/python-tabulate 迁移而来。python-tabulate使用 Python、库和命令行实用程序漂亮地打印表格数据。该库的主要用例是轻松打印小表格只需一个函数调用,格式由数据本身引导为轻量级纯文本标记创作表格数据多种输出格式适合进一步编辑或转换混合文本和数字数据的可读表示智能列对齐、可配置数字格式、小数点对齐安装要安装 Python 库和命令行实用程序,请运行pip install tabulate命令行实用程序将在 Linux 上安装为(例如tabulate)或者在 Windows 上的 Python 安装中安装为(例如)。bin/usr/bintabulate.exeScriptsC:\Python39\Scripts\tabulate.exe您可以考虑仅为当前用户安装该库pip install tabulate --user在这种情况下,命令行实用程序将安装到 ~/.local/bin/tabula