基于mybatis 自定义标签分页的实现
Posted TT_DUL
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了基于mybatis 自定义标签分页的实现相关的知识,希望对你有一定的参考价值。
ssm框架 ,mybatis 分页一直以来都是一个难题,博主也是参考大神的代码,整合到项目中的,废话不多说,看代码
0、最最首先是把page 基础类加进来了
import java.util.List;
import java.util.Map;
import org.codehaus.jackson.map.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.base.Joiner;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
public class Page
private static final Logger logger = LoggerFactory.getLogger(Page.class);
private static ObjectMapper mapper = new ObjectMapper();
public static String DEFAULT_PAGESIZE = "10";
private int pageNo; //当前页码
private int pageSize; //每页行数
private int totalRecord; //总记录数
private int totalPage; //总页数
private Map<String, String> params; //查询条件
private Map<String, List<String>> paramLists; //数组查询条件
private String searchUrl; //Url地址
private String pageNoDisp; //可以显示的页号(分隔符"|",总页数变更时更新)
private String tableName; //查询语句的主表名
private Page()
pageNo = 1;
pageSize = Integer.valueOf(DEFAULT_PAGESIZE);
totalRecord = 0;
totalPage = 0;
params = Maps.newHashMap();
paramLists = Maps.newHashMap();
searchUrl = "";
pageNoDisp = "";
public static Page newBuilder(int pageNo, int pageSize, String url)
Page page = new Page();
page.setPageNo(pageNo);
page.setPageSize(pageSize);
page.setSearchUrl(url);
return page;
/**
* 查询条件转JSON
*/
public String getParaJson()
Map<String, Object> map = Maps.newHashMap();
for (String key : params.keySet())
if ( params.get(key) != null )
map.put(key, params.get(key));
String json="";
try
json = mapper.writeValueAsString(map);
catch (Exception e)
logger.error("转换JSON失败", params, e);
return json;
/**
* 数组查询条件转JSON
*/
public String getParaListJson()
Map<String, Object> map = Maps.newHashMap();
for (String key : paramLists.keySet())
List<String> lists = paramLists.get(key);
if ( lists != null && lists.size()>0 )
map.put(key, lists);
String json="";
try
json = mapper.writeValueAsString(map);
catch (Exception e)
logger.error("转换JSON失败", params, e);
return json;
/**
* 总件数变化时,更新总页数并计算显示样式
*/
private void refreshPage()
//总页数计算
totalPage = totalRecord%pageSize==0 ? totalRecord/pageSize : (totalRecord/pageSize + 1);
//防止超出最末页(浏览途中数据被删除的情况)
if ( pageNo > totalPage && totalPage!=0)
pageNo = totalPage;
pageNoDisp = computeDisplayStyleAndPage();
/**
* 计算页号显示样式
* 这里实现以下的分页样式("[]"代表当前页号),可根据项目需求调整
* [1],2,3,4,5,6,7,8..12,13
* 1,2..5,6,[7],8,9..12,13
* 1,2..6,7,8,9,10,11,12,[13]
*/
private String computeDisplayStyleAndPage()
List<Integer> pageDisplays = Lists.newArrayList();
if ( totalPage <= 11 )
for (int i=1; i<=totalPage; i++)
pageDisplays.add(i);
else if ( pageNo < 7 )
for (int i=1; i<=8; i++)
pageDisplays.add(i);
pageDisplays.add(0);// 0 表示 省略部分(下同)
pageDisplays.add(totalPage-1);
pageDisplays.add(totalPage);
else if ( pageNo> totalPage-6 )
pageDisplays.add(1);
pageDisplays.add(2);
pageDisplays.add(0);
for (int i=totalPage-7; i<=totalPage; i++)
pageDisplays.add(i);
else
pageDisplays.add(1);
pageDisplays.add(2);
pageDisplays.add(0);
for (int i=pageNo-2; i<=pageNo+2; i++)
pageDisplays.add(i);
pageDisplays.add(0);
pageDisplays.add(totalPage-1);
pageDisplays.add(totalPage);
return Joiner.on("|").join(pageDisplays.toArray());
public int getPageNo()
return pageNo;
public void setPageNo(int pageNo)
this.pageNo = pageNo;
public int getPageSize()
return pageSize;
public void setPageSize(int pageSize)
this.pageSize = pageSize;
public int getTotalRecord()
return totalRecord;
public void setTotalRecord(int totalRecord)
this.totalRecord = totalRecord;
refreshPage();
public int getTotalPage()
return totalPage;
public void setTotalPage(int totalPage)
this.totalPage = totalPage;
public Map<String, String> getParams()
return params;
public void setParams(Map<String, String> params)
this.params = params;
public Map<String, List<String>> getParamLists()
return paramLists;
public void setParamLists(Map<String, List<String>> paramLists)
this.paramLists = paramLists;
public String getSearchUrl()
return searchUrl;
public void setSearchUrl(String searchUrl)
this.searchUrl = searchUrl;
public String getPageNoDisp()
return pageNoDisp;
public void setPageNoDisp(String pageNoDisp)
this.pageNoDisp = pageNoDisp;
public String getTableName()
return tableName;
public void setTableName(String tableName)
this.tableName = tableName;
1、首先当然是control 层了
@RequestMapping("/addressList")
public ModelAndView addressList(@RequestParam(required = false, defaultValue = "fhmange") String types ,
@RequestParam(required = false, defaultValue = "1") int pageNo,
@RequestParam(required = false, defaultValue = "25") int pageSize,
@ModelAttribute LogisticAddress logadd,HttpServletRequest request)
Page page = Page.newBuilder(pageNo, pageSize, "/addressControl/addressList?types="+types);
ModelAndView model = new ModelAndView();
Map<String, String> params = new HashMap<String, String>();
if(!StringUtils.isEmpty(logadd.getName()))
params.put("name", logadd.getName());
if(null !=logadd.getArea())
params.put("area", logadd.getArea()+"");
page.setParams(params);
List<LogisticAddress> findAddress = addressService.selectByExampleLimit(logadd,page);
model.setViewName("/address/addressList");
model.addObject("findAddress", findAddress);
model.addObject("page", page);
return model;
2、然后当然是到service 层了
@Service
public class AddressService
@Autowired
LogisticAddressMapper addressMapper;
public List<LogisticAddress> selectByExampleLimit(LogisticAddress logisticAddress,Page page)
Map<String, Object> map = Maps.newHashMap();
map.put("page", page);
return addressMapper.selectByExampleLimit(logisticAddress,map);
3、然后,mybatis 的mapper 文件是这样写的
public interface LogisticAddressMapper
List<LogisticAddress> selectByExampleLimit(@Param("logisticAddress")LogisticAddress logisticAddress,@Param("page")Map<String, Object> map);
4、最后呢,当然是最重要的了,就是在mybatis 拦截器中处理分页信息,取出分页的数据,在 mybatis-config.xml 中增加以下信息
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<plugins>
<plugin interceptor="com.vshop.sys.paging.PageInterceptor">
</plugin>
</plugins>
</configuration>
5、当然到这里,最最重要的就是 PageInterceptor 拦截器的实现了
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import org.apache.commons.jxpath.JXPathContext;
import org.apache.commons.jxpath.JXPathNotFoundException;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.executor.parameter.DefaultParameterHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.MappedStatement.Builder;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.mapping.SqlSource;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
@Intercepts(@Signature(type=Executor.class,method="query",args= MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class ))
public class PageInterceptor implements Interceptor
public Object intercept(Invocation invocation) throws Throwable
//当前环境 MappedStatement,BoundSql,及sql取得
MappedStatement mappedStatement=(MappedStatement)invocation.getArgs()[0];
Object parameter = invocation.getArgs()[1];
BoundSql boundSql = mappedStatement.getBoundSql(parameter);
String originalSql = boundSql.getSql().trim();
Object parameterObject = boundSql.getParameterObject();
//Page对象获取,“信使”到达拦截器!
Page page =null;
if(null !=searchPageWithXpath(boundSql.getParameterObject(),".","page","*/page"))
page = searchPageWithXpath(boundSql.getParameterObject(),".","page","*/page");
if(page!=null )
//Page对象存在的场合,开始分页处理
String countSql = getCountSql(originalSql);
Connection connection=mappedStatement.getConfiguration().getEnvironment().getDataSource().getConnection() ;
PreparedStatement countStmt = connection.prepareStatement(countSql);
BoundSql countBS = copyFromBoundSql(mappedStatement, boundSql, countSql);
DefaultParameterHandler parameterHandler = new DefaultParameterHandler(mappedStatement, parameterObject, countBS);
parameterHandler.setParameters(countStmt);
ResultSet rs = countStmt.executeQuery();
int totpage=0;
if (rs.next())
totpage = rs.getInt(1);
rs.close();
countStmt.close();
connection.close();
//分页计算
page.setTotalRecord(totpage);
//对原始Sql追加limit
int offset = (page.getPageNo() - 1) * page.getPageSize();
BoundSql newBoundSql=null;
StringBuffer sb = new StringBuffer();
sb.append(originalSql).append(" limit ").append(offset).append(",").append(page.getPageSize());
newBoundSql = copyFromBoundSql(mappedStatement, boundSql, sb.toString());
MappedStatement newMs = copyFromMappedStatement(mappedStatement,new BoundSqlSqlSource(newBoundSql));
invocation.getArgs()[0]= newMs;
return invocation.proceed();
/**
* 根据给定的xpath查询Page对象
*/
private Page searchPageWithXpath(Object o,String... xpaths)
JXPathContext context = JXPathContext.newContext(o);
Object result;
for(String xpath : xpaths)
try
result = context.selectNodes(xpath);
catch (JXPathNotFoundException e)
continue;
if ( result instanceof List && ((List) result).size()>0)
if(((List) result).get(0) instanceof Page )
return (Page)(((List) result).get(0));
return null;
/**
* 复制MappedStatement对象
*/
private MappedStatement copyFromMappedStatement(MappedStatement ms,SqlSource newSqlSource)
Builder builder = new Builder(ms.getConfiguration(),ms.getId(),newSqlSource,ms.getSqlCommandType());
builder.resource(ms.getResource());
builder.fetchSize(ms.getFetchSize());
builder.statementType(ms.getStatementType());
builder.keyGenerator(ms.getKeyGenerator());
if(null !=ms.getKeyProperties())
if(ms.getKeyProperties().length>0)
builder.keyProperty(ms.getKeyProperties()[0]);
builder.timeout(ms.getTimeout());
builder.parameterMap(ms.getParameterMap());
builder.resultMaps(ms.getResultMaps());
builder.resultSetType(ms.getResultSetType());
builder.cache(ms.getCache());
builder.flushCacheRequired(ms.isFlushCacheRequired());
builder.useCache(ms.isUseCache());
return builder.build();
/**
* 复制BoundSql对象
*/
private BoundSql copyFromBoundSql(MappedStatement ms, BoundSql boundSql, String sql)
BoundSql newBoundSql = new BoundSql(ms.getConfiguration(),sql, boundSql.getParameterMappings(), boundSql.getParameterObject());
for (ParameterMapping mapping : boundSql.getParameterMappings())
String prop = mapping.getProperty();
if (boundSql.hasAdditionalParameter(prop))
newBoundSql.setAdditionalParameter(prop, boundSql.getAdditionalParameter(prop));
return newBoundSql;
/**
* 根据原Sql语句获取对应的查询总记录数的Sql语句
*/
private String getCountSql(String sql)
return "SELECT COUNT(*) FROM (" + sql + ") aliasForPage";
public class BoundSqlSqlSource implements SqlSource
BoundSql boundSql;
public BoundSqlSqlSource(BoundSql boundSql)
this.boundSql = boundSql;
public BoundSql getBoundSql(Object parameterObject)
return boundSql;
public Object plugin(Object arg0)
return Plugin.wrap(arg0, this);
public void setProperties(Properties arg0)
6、最后当然是得用自定义标签显示在页面了
首先是在WEB-INFO 中建立tags 目录 ,然后在其中建立page.tag 文件
<%@ tag language="java" pageEncoding="UTF-8"%>
<%@ attribute name="page" type="com.vshop.sys.paging.Page" required="true"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%
int current = page.getPageNo();
int begin = 1;
int end = page.getTotalPage();
request.setAttribute("current", current);
request.setAttribute("begin", begin);
request.setAttribute("end", end);
request.setAttribute("pList", page.getPageNoDisp());
%>
<script type="text/javascript">
var paras = '<%=page.getParaJson()%>';
var paraJson = eval('(' + paras + ')');
//将提交参数转换为JSON
var paraLists = '<%=page.getParaListJson()%>';
var paraListJson = eval('(' + paraLists + ')');
function pageClick( pNo )
paraJson["pageNo"] = pNo;
var choosePageSize = $("#choosePageSize").find("option:selected").text();
paraJson["pageSize"] = choosePageSize;
var jsPost = function(action, values, valueLists)
var id = Math.random();
document.write('<form id="post' + id + '" name="post'+ id +'" action="$pageContext.request.contextPath' + action + '" method="post">');
for (var key in values)
document.write('<input type="hidden" name="' + key + '" value="' + values[key] + '" />');
for (var key2 in valueLists)
for (var index in valueLists[key2])
document.write('<input type="hidden" name="' + key2 + '" value="' + valueLists[key2][index] + '" />');
document.write('</form>');
document.getElementById('post' + id).submit();
//发送POST
jsPost("<%=page.getSearchUrl()%>", paraJson, paraListJson);
function ChoosePage()
var choosePage =document.getElementById("ChoosePage").value;
pageClick(choosePage);
//以下这个函数由小弟编写,可以简化到 html 代码中去
function choosePageSizeFunction()
var s2 = document.getElementById("choosePageSize");
for(var c=0;c<s2.options.length;c++)
if(s2.options[c].value=='$page.pageSize')
s2.options[c].selected=true;
if (window.addEventListener)
window.addEventListener('DOMContentLoaded', choosePageSizeFunction, false); //firefox
window.addEventListener('load', choosePageSizeFunction, false);
else if (window.attachEvent)
window.attachEvent('onload', choosePageSizeFunction); //IE
function chPageSize(obj)
/* alert(obj.value); */
pageClick(1);
</script>
<div class="row-fluid">
<div class="fyhpagediv" style="float: right;">
<% if (current!=1 && end!=0)%>
<span class="fyhfybk"><a href="javascript:void(0);" onclick="pageClick($current-1)">上一页</a>
<%else%>
<span class="fyhfybk"><a href="javascript:void(0);">上一页</a>
<% %>
<%
for(int i=0 ; i < page.getTotalPage(); i++)
if(i + 1 == current)
%>
<a href="#" class="pagebtn-red" ><%=i+1%></a>
<%
else
if(i == 0 || i == 1 || i == 2 || i == 3
|| i == current - 2 || i == current - 3
|| i == current || i == current + 1
|| i == page.getTotalPage() - 2 || i == page.getTotalPage() - 1)
%>
<a href="#" onclick="pageClick(<%=i+1%>)"><%=i+1%></a>
<%
else
if(i == 4 && current > 7)
%>
<a href="#">...</a>
<%
if(i == page.getTotalPage() - 3 && current < page.getTotalPage() - 4)
%>
<a href="#">...</a>
<%
%>
<% if (current<end && end!=0)%>
<a href="javascript:void(0);" onclick="pageClick($current+1)">下一页</a></span>
<%else%>
<a href="javascript:void(0);">下一页</a></span>
<% %>
</div>
<div class="fyhpagenum" style="float: right;">
<span>每页</span>
<select id="choosePageSize" class="fyhpageselect" onchange="chPageSize(this)"><option>10</option><option>25</option><option>50</option></select>
<span>条</span>
<span>共$page.totalRecord条</span>
</div>
</div>
7、最后当然是在页面引用了
在相应的页面中引入此tag 标签
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@taglib prefix="tags" tagdir="/WEB-INF/tags"%>
然后在要显示分页信息的地方,加入此行代码即可
<tags:page page="$page"/>
到最后让你的应用跑起来就可以看到好看的分页了
以上是关于基于mybatis 自定义标签分页的实现的主要内容,如果未能解决你的问题,请参考以下文章
Mybatis -- MyBatis核心配置文件深入: typeHandlers标签(自定义类型转换器)plugins标签(插件标签:扩展mybatis功能 分页助手)