之前介紹過基于Hibernate分頁的原理和設計,這里我們所用的分頁都是物理分頁技術,不是JS實現(xiàn)的頁面分頁技術,是在SQL語句上執(zhí)行的分頁,可以獲取結果集數(shù)量固定的列表,執(zhí)行效率很高。下面來看看iBatis中如何設計分頁,本文基于Struts2,Spring3來整合,因為暫時Spring不支持MyBatis3(可以選用MyBatis官方的MyBatis-Spring插件來實現(xiàn),配有中文文檔,很好理解),我們這里仍然以iBatis2作為載體來介紹。
首先就是搭建開發(fā)環(huán)境,這里可以說也是對Struts2,Spring3和iBatis2進行了簡單的整合,大家也可以來參考。項目的結構如下,使用Maven創(chuàng)建的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中來進行,如下即可: <?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.1.7//EN"
"http://struts.apache.org/dtds/struts-2.1.7.dtd">
<struts>
<package name="ibatis-paging" extends="struts-default"
namespace="/">
<result-types>
<result-type name="freemarker"
class="org.apache.struts2.views.freemarker.FreemarkerResult"
default="true" />
</result-types>
<action name="user_*" class="userAction" method="{1}">
<result name="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-dd HH:mm:ss
boolean_format=true,false
whitespace_stripping=true
tag_syntax=auto_detect
auto_import=/Freemarker/page_macro.ftl as p
Log4J的配置這里不再貼出代碼,大家可以去下載源碼,一看就明白了,之后我們配置Spring,在resources文件夾下創(chuàng)建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中主要配置數(shù)據(jù)源,iBatis的SqlMapClient和SqlMapClientTemplate,事務處理還有Action和Service的管理,其實內容大家也都很熟悉了,比較簡單:<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/test" />
<property name="username" value="root" />
<property name="password" value="123" />
<property name="maxActive" value="100" />
<property name="maxIdle" value="50" />
<property name="maxWait" value="100" />
<property name="defaultAutoCommit" value="true" />
</bean>
<!-- 創(chuàng)建JdbcTemplate -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- 創(chuàng)建sqlMapClient -->
<bean id="sqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">
<property name="configLocation" value="classpath:sqlMapConfig.xml" />
<property name="dataSource" ref="dataSource" />
</bean>
<!-- 創(chuàng)建sqlMapClientTemplate -->
<bean id="sqlMapClientTemplate" class="org.springframework.orm.ibatis.SqlMapClientTemplate">
<constructor-arg>
<ref bean="sqlMapClient" />
</constructor-arg>
</bean>
<!-- 事務管理器 -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- 配置事務處理通知 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="get*" read-only="true" />
<tx:method name="add*" rollback-for="Exception" />
<tx:method name="addOrUpdate*" rollback-for="Exception" />
<tx:method name="del*" rollback-for="Exception" />
<tx:method name="update*" rollback-for="Exception" />
</tx:attributes>
</tx:advice>
<aop:config proxy-target-class="true">
<aop:pointcut id="serviceMethod"
expression="execution(* org.ourpioneer.service.*Service.*(..))" />
<aop:advisor advice-ref="txAdvice" pointcut-ref="serviceMethod" />
</aop:config>
之后對Service和Action進行配置:<bean id="baseService" class="org.ourpioneer.service.BaseService">
<property name="jdbcTemplate" ref="jdbcTemplate"></property>
</bean>
<bean id="userService" class="org.ourpioneer.service.UserService"
parent="baseService">
<property name="sqlMapClientTemplate" ref="sqlMapClientTemplate" />
</bean>
<bean id="userAction" class="org.ourpioneer.action.UserAction">
<property name="userService" ref="userService" />
</bean>
下面來看一下iBatis的配置,在配置SqlMapClient的時候,加入了iBatis的配置文件,我們來看看sqlMapConfig.xml如何來設置: <?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE sqlMapConfig
PUBLIC "-//iBATIS.com//DTD SQL Map Config 2.0//EN"
"http://www.ibatis.com/dtd/sql-map-config-2.dtd">
<sqlMapConfig>
<settings cacheModelsEnabled="true" enhancementEnabled="true"
lazyLoadingEnabled="true" errorTracingEnabled="true" maxRequests="32"
maxSessions="10" maxTransactions="5" />
<sqlMap resource="sqlMaps/user.xml" />
</sqlMapConfig>
其實內容也很簡單,就是設置一下必要的信息,其中的含義可以參考之前寫過的對iBatis的介紹的相關文章,最后不要忘了加入sqlMaps配置文件即可,這里我們就一個user.xml文件,為了測試,也就是一條查詢,針對這個查詢進行分頁操作: <?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE sqlMap PUBLIC "-//ibatis.apache.org//DTD SQL Map 2.0//EN" "http://ibatis.apache.org/dtd/sql-map-2.dtd">
<sqlMap>
<typeAlias alias="parameterMap" type="org.pioneer.bean.ParameterMap" />
<select id="selectAllUsers" resultClass="java.util.HashMap">
select * from user
</select>
</sqlMap>
ParameterMap在之前的介紹中也多次出現(xiàn),這里我們也再來看下: package org.ourpioneer.bean;
import java.util.HashMap;
public class ParameterMap extends HashMap {
public ParameterMap(Object... parameters) {
for (int i = 0; i < parameters.length - 1; i += 2) {
super.put(parameters[i], parameters[i + 1]);
}
}
}
其實就是擴展了一下HashMap類,來進行參數(shù)的放置,注意參數(shù)類型是可變參數(shù)的形式,也就是名-值對的形式出現(xiàn)的,不過本例中沒有使用它。下面就是分頁類的設計了: package org.ourpioneer.bean;
import java.util.HashMap;
import java.util.List;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.orm.ibatis.SqlMapClientTemplate;
import com.ibatis.sqlmap.engine.impl.SqlMapClientImpl;
import com.ibatis.sqlmap.engine.mapping.sql.Sql;
import com.ibatis.sqlmap.engine.mapping.statement.MappedStatement;
import com.ibatis.sqlmap.engine.scope.SessionScope;
import com.ibatis.sqlmap.engine.scope.StatementScope;
/**
* iBatis分頁類
*
* @author Nanlei
*
*/
public class PagingList {
private int rowCount = 0; // 記錄總數(shù)
private int pageCount = 1; // 分頁總數(shù)
private int pageSize = 10; // 每頁記錄數(shù)
private int pageNum = 1; // 當前頁數(shù)
private int startIndex = 1; // 起始記錄數(shù)
private int endIndex = 1; // 結束記錄數(shù)
private List list;// 記錄列表
/**
* 構造方法,進行分頁
*
* @param statementName
* iBatis中語句的ID
* @param parameterObject
* SQL語句參數(shù)
* @param pageNum
* 起始頁數(shù)
* @param pageSize
* 每頁大小
* @param sqlMapClientTemplate
* iBatis的sqlMapClientTemplate對象
*/
public PagingList(String statementName, Object parameterObject,
int pageNum, int pageSize,
SqlMapClientTemplate sqlMapClientTemplate, JdbcTemplate jdbcTemplate) {
preProcessParams(pageNum, pageSize);
execute(statementName, parameterObject, pageNum, pageSize,
sqlMapClientTemplate, jdbcTemplate);
}
/**
* 構造方法,進行分頁
*
* @param statementName
* iBatis中語句的ID
* @param pageNum
* 起始頁數(shù)
* @param pageSize
* 每頁大小
* @param sqlMapClientTemplate
* iBatis的sqlMapClientTemplate對象
*/
public PagingList(String statementName, int pageNum, int pageSize,
SqlMapClientTemplate sqlMapClientTemplate, JdbcTemplate jdbcTemplate) {
preProcessParams(pageNum, pageSize);
execute(statementName, pageNum, pageSize, sqlMapClientTemplate,
jdbcTemplate);
}
/**
* 執(zhí)行方法
*
* @param statementName
* @param parameterObject
* @param pageNum
* @param pageSize
* @param sqlMapClientTemplate
*/
public void execute(String statementName, Object parameterObject,
int pageNum, int pageSize,
SqlMapClientTemplate sqlMapClientTemplate, JdbcTemplate jdbcTemplate) {
// 計算記錄總數(shù)
this.rowCount = jdbcTemplate.queryForInt(
getCountSql(getSrcSql(statementName, parameterObject,
sqlMapClientTemplate)), ((HashMap) parameterObject)
.values().toArray());
System.out.println(rowCount);
// 計算分頁數(shù)及起止記錄
countPage();
// 獲取分頁列表
this.list = sqlMapClientTemplate.queryForList(statementName,
parameterObject, (pageNum - 1) * pageSize, pageSize);
}
/**
* 執(zhí)行方法
*
* @param statementName
* @param pageNum
* @param pageSize
* @param sqlMapClientTemplate
*/
public void execute(String statementName, int pageNum, int pageSize,
SqlMapClientTemplate sqlMapClientTemplate, JdbcTemplate jdbcTemplate) {
// 計算記錄總數(shù)
this.rowCount = jdbcTemplate.queryForInt(getCountSql(getSrcSql(
statementName, null, sqlMapClientTemplate)));
System.out.println(rowCount);
// 計算分頁數(shù)及起止記錄
countPage();
// 獲取分頁列表
this.list = sqlMapClientTemplate.queryForList(statementName,
(pageNum - 1) * pageSize, pageSize);
}
/**
* 預處理SQL語句和頁面參數(shù)
*/
private void preProcessParams(int pageNum, int pageSize) {
if (pageNum > 0) {
this.pageNum = pageNum;
}
if (pageSize > 0) {
this.pageSize = pageSize;
}
if (pageSize > 1000) {
this.pageSize = 1000;
}
}
/**
* 計算分頁數(shù)及起止記錄
*/
private void countPage() {
// 計算分頁總數(shù)
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;
}
/**
* 獲得對象列表
*/
public List getList() {
return list;
}
/* 獲得起始記錄數(shù) */
public int getStartIndex() {
return startIndex;
}
public Integer getStartIndexInteger() {
return new Integer(startIndex);
}
/* 獲得結束記錄數(shù) */
public int getEndIndex() {
return endIndex;
}
public Integer getEndIndexInteger() {
return new Integer(endIndex);
}
/* 獲得分頁其它信息 */
public int getPageCount() {
return pageCount;
}
public int getPageNum() {
return pageNum;
}
public int getPageSize() {
return pageSize;
}
public int getRowCount() {
return rowCount;
}
private String getSrcSql(String statementName, Object parameterObject,
SqlMapClientTemplate sqlMapClientTemplate) {
SqlMapClientImpl sqlMapClientImpl = (SqlMapClientImpl) sqlMapClientTemplate
.getSqlMapClient();
MappedStatement mappedStatement = sqlMapClientImpl
.getMappedStatement(statementName);
Sql sql = mappedStatement.getSql();
StatementScope statementScope = new StatementScope(new SessionScope());
String srcSql = sql.getSql(statementScope, parameterObject);
return srcSql;
}
private String getCountSql(String srcSql) {
return "SELECT COUNT(*) FROM ( " + srcSql + " ) CTBL_";
}
}
寫好分頁類,還要和框架進行集成,那么我們可以抽象出Service的基類,在業(yè)務邏輯層中調用它來獲取分頁信息: package org.ourpioneer.service;
import org.ourpioneer.bean.PagingList;
import org.springframework.orm.ibatis.SqlMapClientTemplate;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.util.ValueStack;
public class BaseService {
private JdbcTemplate jdbcTemplate;
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
/**
* 獲取ValueStack
*
* @return ValueStack對象
*/
public ValueStack getValueStack() {
return ActionContext.getContext().getValueStack();
}
/**
* 獲取分頁的List
*
* @param statementName
* @param sqlMapClientTemplate
* @return
*/
public PagingList getPagingList(String statementName,
SqlMapClientTemplate sqlMapClientTemplate) {
int pageNum = ((Integer) getValueStack().findValue("pageNum"))
.intValue();
int pageSize = ((Integer) getValueStack().findValue("pageSize"))
.intValue();
return new PagingList(statementName, pageNum, pageSize,
sqlMapClientTemplate, jdbcTemplate);
}
/**
* 獲取分頁的List
*
* @param statementName
* @param parameterObject
* @param sqlMapClientTemplate
* @return
*/
public PagingList getPagingList(String statementName,
Object parameterObject, SqlMapClientTemplate sqlMapClientTemplate) {
int pageNum = ((Integer) getValueStack().findValue("pageNum"))
.intValue();
int pageSize = ((Integer) getValueStack().findValue("pageSize"))
.intValue();
return new PagingList(statementName, parameterObject, pageNum,
pageSize, sqlMapClientTemplate, jdbcTemplate);
}
}
兩個構造方法我們都使用了,也就是一個帶參數(shù),一個不帶參數(shù)。下面來看抽象出的Action基類,主要是處理頁面?zhèn)魅氲姆猪搮?shù)的處理:package org.ourpioneer.action;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.apache.struts2.ServletActionContext;
import org.ourpioneer.util.QueryUtil;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;
public class BaseAction extends ActionSupport {
@Override
public String execute() throws Exception {
return SUCCESS;
}
public Map<String, Object> getParameters() {
return ActionContext.getContext().getParameters();
}
public HttpServletRequest getRequest() {
return ServletActionContext.getRequest();
}
/* 分頁信息 */
protected int pageNum = 1;
protected int pageSize = 10;
public int getPageNum() {
return pageNum;
}
public void setPageNum(int pageNum) {
this.pageNum = pageNum;
}
public int getPageSize() {
return pageSize;
}
public void setPageSize(int pageSize) {
this.pageSize = pageSize;
}
public int getMaxPageSize() {
return 1000;
}
public int getDefaultPageSize() {
return 10;
}
// 頁面解析分頁信息使用的方法
public String getQueryStringWithoutPageNum() {
Map<String, Object> m = getParameters();
m.remove("pageNum");
return QueryUtil.getQueryString(m);
}
public String getFullUrlWithoutPageNum() {
return getRequest().getServletPath() + "?"
+ getQueryStringWithoutPageNum();
}
public String getQueryStringWithoutPageInfo() {
Map<String, Object> m = getParameters();
m.remove("pageNum");
m.remove("pageSize");
return QueryUtil.getQueryString(m);
}
public String getFullUrlWithoutPageInfo() {
return getRequest().getServletPath() + "?"
+ getQueryStringWithoutPageInfo();
}
}
這里為了演示,我們將分頁的信息都直接定義死了,大家可以根據(jù)需要來修改,其中處理信息的QueryUtil大家可以直接參考源代碼,這里不做說明了,下面是UserAction處理代碼的編寫: package org.ourpioneer.action;
import org.ourpioneer.bean.PagingList;
import org.ourpioneer.service.UserService;
public class UserAction extends BaseAction {
private UserService userService;
public PagingList userList;
public void setUserService(UserService userService) {
this.userService = userService;
}
public PagingList getUserList() {
return userList;
}
public String list() {
userList = userService.getAllUsers();
return "list";
}
}
根據(jù)前面的配置,我們也不難寫出代碼,下面就是視圖處理了,我們使用了Freemarker進行解析,也編寫了FreeMarker的分頁宏: <#-- 處理分頁參數(shù) -->
<#function getPageUrl pageNum>
<#local pageUrl=base+fullUrlWithoutPageInfo>
<#if pageUrl?ends_with("?")>
<#return pageUrl + "pageSize=" + pageSize + "&pageNum=" + pageNum>
<#else>
<#return pageUrl + "&pageSize=" + pageSize + "&pageNum=" + pageNum>
</#if>
</#function>
<#-- 全部或分頁顯示 -->
<#function getPageUrlResize size>
<#local pageUrl=base+fullUrlWithoutPageInfo>
<#if pageUrl?ends_with("?")>
<#return pageUrl + "pageNum=1&pageSize=" + size>
<#else>
<#return pageUrl + "&pageNum=1&pageSize=" + size>
</#if>
</#function>
<#-- 分頁信息 -->
<#macro paging pagingList>
<#local pageCount=pagingList.pageCount>
<#local rowCount=pagingList.rowCount>
<#local pageNum=pagingList.pageNum>
<#local pageSize=pagingList.pageSize>
<#if rowCount == 0>
<#if useFlag?exists>
<div style="border:1px solid #666;padding:2 5 2 5;background:#efefef;color:#333">沒有相關記錄</div>
<#else>
<#assign useFlag = 1>
</#if>
<#else>
<table>
<tr>
<td style="line-height:150%">共 ${rowCount} 條記錄 ${pageCount} 頁
<#if pageCount gt 1 && pageSize!=maxPageSize>
<span class="selectedPage" style="padding:2px 3px 0 3px"><a class="page" href="${getPageUrlResize(maxPageSize)}">全部顯示</a></span>
<#elseif pageSize==maxPageSize>
<span class="selectedPage" style="padding:2px 3px 0 3px"><a class="page" href="${getPageUrlResize(defaultPageSize)}">分頁顯示</a></span>
</#if>
<#if (pageCount <= 11)>
<#local startPage = 1>
<#local endPage = pageCount>
<#elseif (pageNum + 5 > pageCount)>
<#local startPage = pageCount - 10>
<#local endPage = pageCount>
<#elseif (pageNum - 5 < 1)>
<#local startPage = 1>
<#local endPage = 11>
<#else>
<#local startPage = pageNum - 5>
<#local endPage = pageNum + 5>
</#if>
<#if (pageCount > 1)>
<#if (pageNum != 1)>
<#if (pageCount > 11)>
<a class="page" href="${getPageUrl(1)}" style="font-family:Webdings" title="首頁">9</a>
</#if>
<a class="page" href="${getPageUrl(pageNum-1)}" style="font-family:Webdings" title="上頁">3</a>
<#else>
<#if (pageCount > 11)>
<span style="font-family:Webdings;color:#999">9</span>
</#if>
<span style="font-family:Webdings;color:#999">3</span>
</#if>
<#list startPage..endPage as x>
<#if x=pageNum>
<span class="selectedPage">${x}</span>
<#else>
<span class="noSelectedPage"><a class="page" href="${getPageUrl(x)}">${x}</a></span>
</#if>
</#list>
<#if (pageCount != pageNum)>
<a class="page" href="${getPageUrl(pageNum+1)}" style="font-family:Webdings" title="下頁">4</a>
<#if (pageCount > 11)>
<a class="page" href="${getPageUrl(pageCount)}" style="font-family:Webdings" title="尾頁">:</a>
</#if>
<#else>
<span style="font-family:Webdings;color:#999">4</span>
<#if (pageCount > 11)>
<span style="font-family:Webdings;color:#999">:</span>
</#if>
</#if>
</#if>
</td>
</tr>
</table>
</#if>
</#macro>
之后,我們來運行項目: 可以通過點擊全部顯示和頁面來查看分頁效果。
更多建議: