iBatis分页(基于Struts2和Freemarker)

之前介绍过基于Hibernate分页的原理和设计,这里我们所用的分页都是物理分页技术,不是JS实现的页面分页技术,是在SQL语句上执行的分页,可以获取结果集数量固定的列表,执行效率很高。下面来看看iBatis中如何设计分页,本文基于Struts2,Spring3来整合,因为暂时Spring不支持MyBatis3(可以选用MyBatis官方的MyBatis-Spring插件来实现,配有中文文档,很好理解),我们这里仍然以iBatis2作为载体来介绍。
首先就是搭建开发环境,这里可以说也是对Struts2,Spring3和iBatis2进行了简单的整合,大家也可以来参考。项目的结构如下,使用Maven创建的web项目:
[img]http://dl.iteye.com/upload/attachment/473764/24608ab3-bca9-39ba-919d-47693b08c52d.jpg[/img]
添加必要的依赖,因为整合了Struts2和Spring,依赖就比较多了,如下:
[img]http://dl.iteye.com/upload/attachment/473766/eb33519b-2b4b-3590-b825-2ea45a9ff8ee.jpg[/img]
首先来配置一下Struts2,这个就比较简单了,相信大家都不陌生。在web.xml中:
	

struts2
org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter


struts2
*.action

然后是struts.xml,配置Struts相关的内容,这里我们配置freemarker为默认的结果类型,然后配置一个测试的Action,因为和Spring进行了集成,所以Action具体的配置放到Spring中来进行,如下即可:

"-//Apache Software Foundation//DTD Struts Configuration 2.1.7//EN"
"http://struts.apache.org/dtds/struts-2.1.7.dtd">

namespace="/">

class="org.apache.struts2.views.freemarker.FreemarkerResult"
default="true" />


user_list.ftl




对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文件夹下创建spring子目录,里面放置Spring的配置文件,在web.xml中如下设置即可加载Spring的配置文件:
	
contextConfigLocation
classpath:spring/*.xml


org.springframework.web.context.ContextLoaderListener

Spring中主要配置数据源,iBatis的SqlMapClient和SqlMapClientTemplate,事务处理还有Action和Service的管理,其实内容大家也都很熟悉了,比较简单:
		destroy-method="close">

























class="org.springframework.jdbc.datasource.DataSourceTransactionManager">













expression="execution(* org.ourpioneer.service.*Service.*(..))" />


之后对Service和Action进行配置:
	



parent="baseService">






下面来看一下iBatis的配置,在配置SqlMapClient的时候,加入了iBatis的配置文件,我们来看看sqlMapConfig.xml如何来设置:
 
PUBLIC "-//iBATIS.com//DTD SQL Map Config 2.0//EN"
"http://www.ibatis.com/dtd/sql-map-config-2.dtd">

lazyLoadingEnabled="true" errorTracingEnabled="true" maxRequests="32"
maxSessions="10" maxTransactions="5" />


其实内容也很简单,就是设置一下必要的信息,其中的含义可以参考之前写过的对iBatis的介绍的相关文章,最后不要忘了加入sqlMaps配置文件即可,这里我们就一个user.xml文件,为了测试,也就是一条查询,针对这个查询进行分页操作:






ParameterMap在之前的介绍中也多次出现,这里我们也再来看下:
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类,来进行参数的放置,注意参数类型是可变参数的形式,也就是名-值对的形式出现的,不过本例中没有使用它。下面就是分页类的设计了:
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; // 记录总数
private int pageCount = 1; // 分页总数
private int pageSize = 10; // 每页记录数
private int pageNum = 1; // 当前页数
private int startIndex = 1; // 起始记录数
private int endIndex = 1; // 结束记录数
private List list;// 记录列表

/**
* 构造方法,进行分页
*
* @param statementName
* iBatis中语句的ID
* @param parameterObject
* SQL语句参数
* @param pageNum
* 起始页数
* @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
* 起始页数
* @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);
}

/**
* 执行方法
*
* @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) {
// 计算记录总数
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);
}

/**
* 执行方法
*
* @param statementName
* @param pageNum
* @param pageSize
* @param sqlMapClientTemplate
*/
public void execute(String statementName, int pageNum, int pageSize,
SqlMapClientTemplate sqlMapClientTemplate, JdbcTemplate jdbcTemplate) {
// 计算记录总数
this.rowCount = jdbcTemplate.queryForInt(getCountSql(getSrcSql(
statementName, null, sqlMapClientTemplate)));
System.out.println(rowCount);
// 计算分页数及起止记录
countPage();
// 获取分页列表
this.list = sqlMapClientTemplate.queryForList(statementName,
(pageNum - 1) * pageSize, pageSize);
}

/**
* 预处理SQL语句和页面参数
*/
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;
}
}

/**
* 计算分页数及起止记录
*/
private void countPage() {
// 计算分页总数
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;
}

/* 获得起始记录数 */
public int getStartIndex() {
return startIndex;
}

public Integer getStartIndexInteger() {
return new Integer(startIndex);
}

/* 获得结束记录数 */
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的基类,在业务逻辑层中调用它来获取分页信息:
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);
}
}

两个构造方法我们都使用了,也就是一个带参数,一个不带参数。下面来看抽象出的Action基类,主要是处理页面传入的分页参数的处理:
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 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 m = getParameters();
m.remove("pageNum");
return QueryUtil.getQueryString(m);
}
public String getFullUrlWithoutPageNum() {
return getRequest().getServletPath() + "?"
+ getQueryStringWithoutPageNum();
}
public String getQueryStringWithoutPageInfo() {
Map m = getParameters();
m.remove("pageNum");
m.remove("pageSize");
return QueryUtil.getQueryString(m);
}
public String getFullUrlWithoutPageInfo() {
return getRequest().getServletPath() + "?"
+ getQueryStringWithoutPageInfo();
}
}

这里为了演示,我们将分页的信息都直接定义死了,大家可以根据需要来修改,其中处理信息的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";
}
}

根据前面的配置,我们也不难写出代码,下面就是视图处理了,我们使用了Freemarker进行解析,也编写了FreeMarker的分页宏:
<#-- 处理分页参数 -->
<#function getPageUrl pageNum>
<#local pageUrl=base+fullUrlWithoutPageInfo>
<#if pageUrl?ends_with("?")>
<#return pageUrl + "pageSize=" + pageSize + "&pageNum=" + pageNum>
<#else>
<#return pageUrl + "&pageSize=" + pageSize + "&pageNum=" + pageNum>


<#-- 全部或分页显示 -->
<#function getPageUrlResize size>
<#local pageUrl=base+fullUrlWithoutPageInfo>
<#if pageUrl?ends_with("?")>
<#return pageUrl + "pageNum=1&pageSize=" + size>
<#else>
<#return pageUrl + "&pageNum=1&pageSize=" + size>



<#-- 分页信息 -->
<#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>
没有相关记录

<#else>
<#assign useFlag = 1>

<#else>




共 ${rowCount} 条记录 ${pageCount} 页
<#if pageCount gt 1 && pageSize!=maxPageSize>
全部显示
<#elseif pageSize==maxPageSize>
分页显示

<#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 (pageCount > 1)>
<#if (pageNum != 1)>
<#if (pageCount > 11)>
9

3
<#else>
<#if (pageCount > 11)>
9

3

<#list startPage..endPage as x>
<#if x=pageNum>
${x}
<#else>
${x}


<#if (pageCount != pageNum)>
4
<#if (pageCount > 11)>
:

<#else>
4
<#if (pageCount > 11)>
:







之后,我们来运行项目:
[img]http://dl.iteye.com/upload/attachment/473768/e57c007c-c8dd-3859-ba79-6e426f95e52c.jpg[/img]
可以通过点击全部显示和页面来查看分页效果。
本文系作者本人的实践探索,方案可能不是最佳实践,希望和大家交流沟通,源码随附件可以下载。另外关于本文涉及到的技术可以参看本博客中以往对iBatis的介绍:
框架系列:[url=http://sarin.iteye.com/category/87345]框架技术[/url],
最后,希望它对使用者和学习有用。根据大家的反馈意见全新修改。

你可能感兴趣的:(框架技术)