之前介绍过基于Hibernate分页的原理和设计,这里我们所用的分页都是物理分页技术,不是JS实现的页面分页技术,是在SQL语句上执行的分页,可以获取结果集数量固定的列表,执行效率很高。下面来看看iBatis中如何设计分页,本文基于Struts2,Spring3来整合,因为暂时Spring不支持MyBatis3(可以选用MyBatis官方的MyBatis-Spring插件来实现,配有中文文档,很好理解),我们这里仍然以iBatis2作为载体来介绍。
首先就是搭建开发环境,这里可以说也是对Struts2,Spring3和iBatis2进行了简单的整合,大家也可以来参考。项目的结构如下,使用Maven创建的web项目:
添加必要的依赖,因为整合了Struts2和Spring,依赖就比较多了,如下:
首先来配置一下Struts2,这个就比较简单了,相信大家都不陌生。在web.xml中:
然后是struts.xml,配置Struts相关的内容,这里我们配置freemarker为默认的结果类型,然后配置一个测试的Action,因为和Spring进行了集成,所以Action具体的配置放到Spring中来进行,如下即可:
对Freemarker做一个简单的设置,卸载freeemarer.properties文件中即可,这里我们主要是引用了一个宏文件,就是分页宏的配置,如下:
Log4J的配置这里不再贴出代码,大家可以去下载源码,一看就明白了,之后我们配置Spring,在resources文件夹下创建spring子目录,里面放置Spring的配置文件,在web.xml中如下设置即可加载Spring的配置文件:
Spring中主要配置数据源,iBatis的SqlMapClient和SqlMapClientTemplate,事务处理还有Action和Service的管理,其实内容大家也都很熟悉了,比较简单:
之后对Service和Action进行配置:
下面来看一下iBatis的配置,在配置SqlMapClient的时候,加入了iBatis的配置文件,我们来看看sqlMapConfig.xml如何来设置:
其实内容也很简单,就是设置一下必要的信息,其中的含义可以参考之前写过的对iBatis的介绍的相关文章,最后不要忘了加入sqlMaps配置文件即可,这里我们就一个user.xml文件,为了测试,也就是一条查询,针对这个查询进行分页操作:
ParameterMap在之前的介绍中也多次出现,这里我们也再来看下:
其实就是扩展了一下HashMap类,来进行参数的放置,注意参数类型是可变参数的形式,也就是名-值对的形式出现的,不过本例中没有使用它。下面就是分页类的设计了:
写好分页类,还要和框架进行集成,那么我们可以抽象出Service的基类,在业务逻辑层中调用它来获取分页信息:
两个构造方法我们都使用了,也就是一个带参数,一个不带参数。下面来看抽象出的Action基类,主要是处理页面传入的分页参数的处理:
这里为了演示,我们将分页的信息都直接定义死了,大家可以根据需要来修改,其中处理信息的QueryUtil大家可以直接参考源代码,这里不做说明了,下面是UserAction处理代码的编写:
根据前面的配置,我们也不难写出代码,下面就是视图处理了,我们使用了Freemarker进行解析,也编写了FreeMarker的分页宏:
之后,我们来运行项目:
可以通过点击全部显示和页面来查看分页效果。
本文系作者本人的实践探索,方案可能不是最佳实践,希望和大家交流沟通,源码随附件可以下载。另外关于本文涉及到的技术可以参看本博客中以往对iBatis的介绍:
框架系列: 框架技术,
最后,希望它对使用者和学习有用。根据大家的反馈意见全新修改。
首先就是搭建开发环境,这里可以说也是对Struts2,Spring3和iBatis2进行了简单的整合,大家也可以来参考。项目的结构如下,使用Maven创建的web项目:

添加必要的依赖,因为整合了Struts2和Spring,依赖就比较多了,如下:

首先来配置一下Struts2,这个就比较简单了,相信大家都不陌生。在web.xml中:
- <!--Struts2的过滤器-->
- <filter>
- <filter-name>struts2</filter-name>
- <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
- </filter>
- <filter-mapping>
- <filter-name>struts2</filter-name>
- <url-pattern>*.action</url-pattern>
- </filter-mapping>
然后是struts.xml,配置Struts相关的内容,这里我们配置freemarker为默认的结果类型,然后配置一个测试的Action,因为和Spring进行了集成,所以Action具体的配置放到Spring中来进行,如下即可:
- <?xmlversion="1.0"encoding="UTF-8"?>
- <!DOCTYPEstrutsPUBLIC
- "-//ApacheSoftwareFoundation//DTDStrutsConfiguration2.1.7//EN"
- "http://struts.apache.org/dtds/struts-2.1.7.dtd">
- <struts>
- <packagename="ibatis-paging"extends="struts-default"
- namespace="/">
- <result-types>
- <result-typename="freemarker"
- class="org.apache.struts2.views.freemarker.FreemarkerResult"
- default="true"/>
- </result-types>
- <actionname="user_*"class="userAction"method="{1}">
- <resultname="list">user_list.ftl</result>
- </action>
- </package>
- </struts>
对Freemarker做一个简单的设置,卸载freeemarer.properties文件中即可,这里我们主要是引用了一个宏文件,就是分页宏的配置,如下:
- template_update_delay=5
- default_encoding=UTF-8
- url_escaping_charset=UTF-8
- number_format=0.#
- date_format=yyyy-MM-dd
- time_format=HH:mm:ss
- datetime_format=yyyy-MM-ddHH:mm:ss
- boolean_format=true,false
- whitespace_stripping=true
- tag_syntax=auto_detect
- auto_import=/Freemarker/page_macro.ftlasp
Log4J的配置这里不再贴出代码,大家可以去下载源码,一看就明白了,之后我们配置Spring,在resources文件夹下创建spring子目录,里面放置Spring的配置文件,在web.xml中如下设置即可加载Spring的配置文件:
- <context-param>
- <param-name>contextConfigLocation</param-name>
- <param-value>classpath:spring/*.xml</param-value>
- </context-param>
- <!--Spring加载配置文件来初始化IoC容器-->
- <listener><listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
- </listener>
Spring中主要配置数据源,iBatis的SqlMapClient和SqlMapClientTemplate,事务处理还有Action和Service的管理,其实内容大家也都很熟悉了,比较简单:
- <beanid="dataSource"class="org.apache.commons.dbcp.BasicDataSource"
- destroy-method="close">
- <propertyname="driverClassName"value="com.mysql.jdbc.Driver"/>
- <propertyname="url"value="jdbc:mysql://localhost:3306/test"/>
- <propertyname="username"value="root"/>
- <propertyname="password"value="123"/>
- <propertyname="maxActive"value="100"/>
- <propertyname="maxIdle"value="50"/>
- <propertyname="maxWait"value="100"/>
- <propertyname="defaultAutoCommit"value="true"/>
- </bean>
- <!--创建JdbcTemplate-->
- <beanid="jdbcTemplate"class="org.springframework.jdbc.core.JdbcTemplate">
- <propertyname="dataSource"ref="dataSource"/>
- </bean>
- <!--创建sqlMapClient-->
- <beanid="sqlMapClient"class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">
- <propertyname="configLocation"value="classpath:sqlMapConfig.xml"/>
- <propertyname="dataSource"ref="dataSource"/>
- </bean>
- <!--创建sqlMapClientTemplate-->
- <beanid="sqlMapClientTemplate"class="org.springframework.orm.ibatis.SqlMapClientTemplate">
- <constructor-arg>
- <refbean="sqlMapClient"/>
- </constructor-arg>
- </bean>
- <!--事务管理器-->
- <beanid="transactionManager"
- class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
- <propertyname="dataSource"ref="dataSource"/>
- </bean>
- <!--配置事务处理通知-->
- <tx:adviceid="txAdvice"transaction-manager="transactionManager">
- <tx:attributes>
- <tx:methodname="get*"read-only="true"/>
- <tx:methodname="add*"rollback-for="Exception"/>
- <tx:methodname="addOrUpdate*"rollback-for="Exception"/>
- <tx:methodname="del*"rollback-for="Exception"/>
- <tx:methodname="update*"rollback-for="Exception"/>
- </tx:attributes>
- </tx:advice>
- <aop:configproxy-target-class="true">
- <aop:pointcutid="serviceMethod"
- expression="execution(*org.ourpioneer.service.*Service.*(..))"/>
- <aop:advisoradvice-ref="txAdvice"pointcut-ref="serviceMethod"/>
- </aop:config>
之后对Service和Action进行配置:
- <beanid="baseService"class="org.ourpioneer.service.BaseService">
- <propertyname="jdbcTemplate"ref="jdbcTemplate"></property>
- </bean>
- <beanid="userService"class="org.ourpioneer.service.UserService"
- parent="baseService">
- <propertyname="sqlMapClientTemplate"ref="sqlMapClientTemplate"/>
- </bean>
- <beanid="userAction"class="org.ourpioneer.action.UserAction">
- <propertyname="userService"ref="userService"/>
- </bean>
下面来看一下iBatis的配置,在配置SqlMapClient的时候,加入了iBatis的配置文件,我们来看看sqlMapConfig.xml如何来设置:
- <?xmlversion="1.0"encoding="UTF-8"?>
- <!DOCTYPEsqlMapConfig
- PUBLIC"-//iBATIS.com//DTDSQLMapConfig2.0//EN"
- "http://www.ibatis.com/dtd/sql-map-config-2.dtd">
- <sqlMapConfig>
- <settingscacheModelsEnabled="true"enhancementEnabled="true"
- lazyLoadingEnabled="true"errorTracingEnabled="true"maxRequests="32"
- maxSessions="10"maxTransactions="5"/>
- <sqlMapresource="sqlMaps/user.xml"/>
- </sqlMapConfig>
其实内容也很简单,就是设置一下必要的信息,其中的含义可以参考之前写过的对iBatis的介绍的相关文章,最后不要忘了加入sqlMaps配置文件即可,这里我们就一个user.xml文件,为了测试,也就是一条查询,针对这个查询进行分页操作:
- <?xmlversion="1.0"encoding="UTF-8"?>
- <!DOCTYPEsqlMapPUBLIC"-//ibatis.apache.org//DTDSQLMap2.0//EN""http://ibatis.apache.org/dtd/sql-map-2.dtd">
- <sqlMap>
- <typeAliasalias="parameterMap"type="org.pioneer.bean.ParameterMap"/>
- <selectid="selectAllUsers"resultClass="java.util.HashMap">
- select*fromuser
- </select>
- </sqlMap>
ParameterMap在之前的介绍中也多次出现,这里我们也再来看下:
- packageorg.ourpioneer.bean;
- importjava.util.HashMap;
- publicclassParameterMapextendsHashMap{
- publicParameterMap(Object...parameters){
- for(inti=0;i<parameters.length-1;i+=2){
- super.put(parameters[i],parameters[i+1]);
- }
- }
- }
其实就是扩展了一下HashMap类,来进行参数的放置,注意参数类型是可变参数的形式,也就是名-值对的形式出现的,不过本例中没有使用它。下面就是分页类的设计了:
- packageorg.ourpioneer.bean;
- importjava.util.HashMap;
- importjava.util.List;
- importorg.springframework.jdbc.core.JdbcTemplate;
- importorg.springframework.orm.ibatis.SqlMapClientTemplate;
- importcom.ibatis.sqlmap.engine.impl.SqlMapClientImpl;
- importcom.ibatis.sqlmap.engine.mapping.sql.Sql;
- importcom.ibatis.sqlmap.engine.mapping.statement.MappedStatement;
- importcom.ibatis.sqlmap.engine.scope.SessionScope;
- importcom.ibatis.sqlmap.engine.scope.StatementScope;
- /**
- *iBatis分页类
- *
- *@authorNanlei
- *
- */
- publicclassPagingList{
- privateintrowCount=0;//记录总数
- privateintpageCount=1;//分页总数
- privateintpageSize=10;//每页记录数
- privateintpageNum=1;//当前页数
- privateintstartIndex=1;//起始记录数
- privateintendIndex=1;//结束记录数
- privateListlist;//记录列表
- /**
- *构造方法,进行分页
- *
- *@paramstatementName
- *iBatis中语句的ID
- *@paramparameterObject
- *SQL语句参数
- *@parampageNum
- *起始页数
- *@parampageSize
- *每页大小
- *@paramsqlMapClientTemplate
- *iBatis的sqlMapClientTemplate对象
- */
- publicPagingList(StringstatementName,ObjectparameterObject,
- intpageNum,intpageSize,
- SqlMapClientTemplatesqlMapClientTemplate,JdbcTemplatejdbcTemplate){
- preProcessParams(pageNum,pageSize);
- execute(statementName,parameterObject,pageNum,pageSize,
- sqlMapClientTemplate,jdbcTemplate);
- }
- /**
- *构造方法,进行分页
- *
- *@paramstatementName
- *iBatis中语句的ID
- *@parampageNum
- *起始页数
- *@parampageSize
- *每页大小
- *@paramsqlMapClientTemplate
- *iBatis的sqlMapClientTemplate对象
- */
- publicPagingList(StringstatementName,intpageNum,intpageSize,
- SqlMapClientTemplatesqlMapClientTemplate,JdbcTemplatejdbcTemplate){
- preProcessParams(pageNum,pageSize);
- execute(statementName,pageNum,pageSize,sqlMapClientTemplate,
- jdbcTemplate);
- }
- /**
- *执行方法
- *
- *@paramstatementName
- *@paramparameterObject
- *@parampageNum
- *@parampageSize
- *@paramsqlMapClientTemplate
- */
- publicvoidexecute(StringstatementName,ObjectparameterObject,
- intpageNum,intpageSize,
- SqlMapClientTemplatesqlMapClientTemplate,JdbcTemplatejdbcTemplate){
- //计算记录总数
- this.rowCount=jdbcTemplate.queryForInt(
- getCountSql(getSrcSql(statementName,parameterObject,
- sqlMapClientTemplate)),((HashMap)parameterObject)
- .values().toArray());
- System.out.println(rowCount);
- //计算分页数及起止记录
- countPage();
- //获取分页列表
- this.list=sqlMapClientTemplate.queryForList(statementName,
- parameterObject,(pageNum-1)*pageSize,pageSize);
- }
- /**
- *执行方法
- *
- *@paramstatementName
- *@parampageNum
- *@parampageSize
- *@paramsqlMapClientTemplate
- */
- publicvoidexecute(StringstatementName,intpageNum,intpageSize,
- SqlMapClientTemplatesqlMapClientTemplate,JdbcTemplatejdbcTemplate){
- //计算记录总数
- this.rowCount=jdbcTemplate.queryForInt(getCountSql(getSrcSql(
- statementName,null,sqlMapClientTemplate)));
- System.out.println(rowCount);
- //计算分页数及起止记录
- countPage();
- //获取分页列表
- this.list=sqlMapClientTemplate.queryForList(statementName,
- (pageNum-1)*pageSize,pageSize);
- }
- /**
- *预处理SQL语句和页面参数
- */
- privatevoidpreProcessParams(intpageNum,intpageSize){
- if(pageNum>0){
- this.pageNum=pageNum;
- }
- if(pageSize>0){
- this.pageSize=pageSize;
- }
- if(pageSize>1000){
- this.pageSize=1000;
- }
- }
- /**
- *计算分页数及起止记录
- */
- privatevoidcountPage(){
- //计算分页总数
- if((rowCount%pageSize)==0){
- pageCount=rowCount/pageSize;
- }else{
- pageCount=rowCount/pageSize+1;
- }
- if(pageCount==0){
- pageCount=1;
- }
- //判断pageNum是否过界
- if(pageNum>pageCount&&rowCount!=0){
- pageNum=pageCount;
- }
- //计算起止记录
- startIndex=(pageNum-1)*pageSize+1;
- endIndex=(pageNum)*pageSize;
- }
- /**
- *获得对象列表
- */
- publicListgetList(){
- returnlist;
- }
- /*获得起始记录数*/
- publicintgetStartIndex(){
- returnstartIndex;
- }
- publicIntegergetStartIndexInteger(){
- returnnewInteger(startIndex);
- }
- /*获得结束记录数*/
- publicintgetEndIndex(){
- returnendIndex;
- }
- publicIntegergetEndIndexInteger(){
- returnnewInteger(endIndex);
- }
- /*获得分页其它信息*/
- publicintgetPageCount(){
- returnpageCount;
- }
- publicintgetPageNum(){
- returnpageNum;
- }
- publicintgetPageSize(){
- returnpageSize;
- }
- publicintgetRowCount(){
- returnrowCount;
- }
- privateStringgetSrcSql(StringstatementName,ObjectparameterObject,
- SqlMapClientTemplatesqlMapClientTemplate){
- SqlMapClientImplsqlMapClientImpl=(SqlMapClientImpl)sqlMapClientTemplate
- .getSqlMapClient();
- MappedStatementmappedStatement=sqlMapClientImpl
- .getMappedStatement(statementName);
- Sqlsql=mappedStatement.getSql();
- StatementScopestatementScope=newStatementScope(newSessionScope());
- StringsrcSql=sql.getSql(statementScope,parameterObject);
- returnsrcSql;
- }
- privateStringgetCountSql(StringsrcSql){
- return"SELECTCOUNT(*)FROM("+srcSql+")CTBL_";
- }
- }
写好分页类,还要和框架进行集成,那么我们可以抽象出Service的基类,在业务逻辑层中调用它来获取分页信息:
- packageorg.ourpioneer.service;
- importorg.ourpioneer.bean.PagingList;
- importorg.springframework.orm.ibatis.SqlMapClientTemplate;
- importcom.opensymphony.xwork2.ActionContext;
- importcom.opensymphony.xwork2.util.ValueStack;
- publicclassBaseService{
- privateJdbcTemplatejdbcTemplate;
- publicvoidsetJdbcTemplate(JdbcTemplatejdbcTemplate){
- this.jdbcTemplate=jdbcTemplate;
- }
- /**
- *获取ValueStack
- *
- *@returnValueStack对象
- */
- publicValueStackgetValueStack(){
- returnActionContext.getContext().getValueStack();
- }
- /**
- *获取分页的List
- *
- *@paramstatementName
- *@paramsqlMapClientTemplate
- *@return
- */
- publicPagingListgetPagingList(StringstatementName,
- SqlMapClientTemplatesqlMapClientTemplate){
- intpageNum=((Integer)getValueStack().findValue("pageNum"))
- .intValue();
- intpageSize=((Integer)getValueStack().findValue("pageSize"))
- .intValue();
- returnnewPagingList(statementName,pageNum,pageSize,
- sqlMapClientTemplate,jdbcTemplate);
- }
- /**
- *获取分页的List
- *
- *@paramstatementName
- *@paramparameterObject
- *@paramsqlMapClientTemplate
- *@return
- */
- publicPagingListgetPagingList(StringstatementName,
- ObjectparameterObject,SqlMapClientTemplatesqlMapClientTemplate){
- intpageNum=((Integer)getValueStack().findValue("pageNum"))
- .intValue();
- intpageSize=((Integer)getValueStack().findValue("pageSize"))
- .intValue();
- returnnewPagingList(statementName,parameterObject,pageNum,
- pageSize,sqlMapClientTemplate,jdbcTemplate);
- }
- }
两个构造方法我们都使用了,也就是一个带参数,一个不带参数。下面来看抽象出的Action基类,主要是处理页面传入的分页参数的处理:
- packageorg.ourpioneer.action;
- importjava.util.Map;
- importjavax.servlet.http.HttpServletRequest;
- importorg.apache.struts2.ServletActionContext;
- importorg.ourpioneer.util.QueryUtil;
- importcom.opensymphony.xwork2.ActionContext;
- importcom.opensymphony.xwork2.ActionSupport;
- publicclassBaseActionextendsActionSupport{
- @Override
- publicStringexecute()throwsException{
- returnSUCCESS;
- }
- publicMap<String,Object>getParameters(){
- returnActionContext.getContext().getParameters();
- }
- publicHttpServletRequestgetRequest(){
- returnServletActionContext.getRequest();
- }
- /*分页信息*/
- protectedintpageNum=1;
- protectedintpageSize=10;
- publicintgetPageNum(){
- returnpageNum;
- }
- publicvoidsetPageNum(intpageNum){
- this.pageNum=pageNum;
- }
- publicintgetPageSize(){
- returnpageSize;
- }
- publicvoidsetPageSize(intpageSize){
- this.pageSize=pageSize;
- }
- publicintgetMaxPageSize(){
- return1000;
- }
- publicintgetDefaultPageSize(){
- return10;
- }
- //页面解析分页信息使用的方法
- publicStringgetQueryStringWithoutPageNum(){
- Map<String,Object>m=getParameters();
- m.remove("pageNum");
- returnQueryUtil.getQueryString(m);
- }
- publicStringgetFullUrlWithoutPageNum(){
- returngetRequest().getServletPath()+"?"
- +getQueryStringWithoutPageNum();
- }
- publicStringgetQueryStringWithoutPageInfo(){
- Map<String,Object>m=getParameters();
- m.remove("pageNum");
- m.remove("pageSize");
- returnQueryUtil.getQueryString(m);
- }
- publicStringgetFullUrlWithoutPageInfo(){
- returngetRequest().getServletPath()+"?"
- +getQueryStringWithoutPageInfo();
- }
- }
这里为了演示,我们将分页的信息都直接定义死了,大家可以根据需要来修改,其中处理信息的QueryUtil大家可以直接参考源代码,这里不做说明了,下面是UserAction处理代码的编写:
- packageorg.ourpioneer.action;
- importorg.ourpioneer.bean.PagingList;
- importorg.ourpioneer.service.UserService;
- publicclassUserActionextendsBaseAction{
- privateUserServiceuserService;
- publicPagingListuserList;
- publicvoidsetUserService(UserServiceuserService){
- this.userService=userService;
- }
- publicPagingListgetUserList(){
- returnuserList;
- }
- publicStringlist(){
- userList=userService.getAllUsers();
- return"list";
- }
- }
根据前面的配置,我们也不难写出代码,下面就是视图处理了,我们使用了Freemarker进行解析,也编写了FreeMarker的分页宏:
- <#--处理分页参数-->
- <#functiongetPageUrlpageNum>
- <#localpageUrl=base+fullUrlWithoutPageInfo>
- <#ifpageUrl?ends_with("?")>
- <#returnpageUrl+"pageSize="+pageSize+"&pageNum="+pageNum>
- <#else>
- <#returnpageUrl+"&pageSize="+pageSize+"&pageNum="+pageNum>
- </#if>
- </#function>
- <#--全部或分页显示-->
- <#functiongetPageUrlResizesize>
- <#localpageUrl=base+fullUrlWithoutPageInfo>
- <#ifpageUrl?ends_with("?")>
- <#returnpageUrl+"pageNum=1&pageSize="+size>
- <#else>
- <#returnpageUrl+"&pageNum=1&pageSize="+size>
- </#if>
- </#function>
- <#--分页信息-->
- <#macropagingpagingList>
- <#localpageCount=pagingList.pageCount>
- <#localrowCount=pagingList.rowCount>
- <#localpageNum=pagingList.pageNum>
- <#localpageSize=pagingList.pageSize>
- <#ifrowCount==0>
- <#ifuseFlag?exists>
- <divstyle="border:1pxsolid#666;padding:2525;background:#efefef;color:#333">没有相关记录</div>
- <#else>
- <#assignuseFlag=1>
- </#if>
- <#else>
- <table>
- <tr>
- <tdstyle="line-height:150%">共${rowCount}条记录${pageCount}页
- <#ifpageCountgt1&&pageSize!=maxPageSize>
- <spanclass="selectedPage"style="padding:2px3px03px"><aclass="page"href="${getPageUrlResize(maxPageSize)}">全部显示</a></span>
- <#elseifpageSize==maxPageSize>
- <spanclass="selectedPage"style="padding:2px3px03px"><aclass="page"href="${getPageUrlResize(defaultPageSize)}">分页显示</a></span>
- </#if>
- <#if(pageCount<=11)>
- <#localstartPage=1>
- <#localendPage=pageCount>
- <#elseif(pageNum+5>pageCount)>
- <#localstartPage=pageCount-10>
- <#localendPage=pageCount>
- <#elseif(pageNum-5<1)>
- <#localstartPage=1>
- <#localendPage=11>
- <#else>
- <#localstartPage=pageNum-5>
- <#localendPage=pageNum+5>
- </#if>
- <#if(pageCount>1)>
- <#if(pageNum!=1)>
- <#if(pageCount>11)>
- <aclass="page"href="${getPageUrl(1)}"style="font-family:Webdings"title="首页">9</a>
- </#if>
- <aclass="page"href="${getPageUrl(pageNum-1)}"style="font-family:Webdings"title="上页">3</a>
- <#else>
- <#if(pageCount>11)>
- <spanstyle="font-family:Webdings;color:#999">9</span>
- </#if>
- <spanstyle="font-family:Webdings;color:#999">3</span>
- </#if>
- <#liststartPage..endPageasx>
- <#ifx=pageNum>
- <spanclass="selectedPage">${x}</span>
- <#else>
- <spanclass="noSelectedPage"><aclass="page"href="${getPageUrl(x)}">${x}</a></span>
- </#if>
- </#list>
- <#if(pageCount!=pageNum)>
- <aclass="page"href="${getPageUrl(pageNum+1)}"style="font-family:Webdings"title="下页">4</a>
- <#if(pageCount>11)>
- <aclass="page"href="${getPageUrl(pageCount)}"style="font-family:Webdings"title="尾页">:</a>
- </#if>
- <#else>
- <spanstyle="font-family:Webdings;color:#999">4</span>
- <#if(pageCount>11)>
- <spanstyle="font-family:Webdings;color:#999">:</span>
- </#if>
- </#if>
- </#if>
- </td>
- </tr>
- </table>
- </#if>
- </#macro>
之后,我们来运行项目:

可以通过点击全部显示和页面来查看分页效果。
本文系作者本人的实践探索,方案可能不是最佳实践,希望和大家交流沟通,源码随附件可以下载。另外关于本文涉及到的技术可以参看本博客中以往对iBatis的介绍:
框架系列: 框架技术,
最后,希望它对使用者和学习有用。根据大家的反馈意见全新修改。