rc-pagination 同时使用 pageSize 和 rcdefaultappCurrent 时,无法

Pagination 分页
采用分页的形式分隔长列表,每次只加载一个页面。
当加载/渲染所有数据将花费很多时间时;
可切换页码浏览数据。
onChange={onChange} total={50} /&
defaultCurrent
默认的当前页数
页码改变的回调,参数是改变后的页码
showSizeChanger
是否可以改变 pageSize
onShowSizeChange
pageSize 变化的回调
showQuickJumper
是否可以快速跳转至某页
当为「small」时,是小尺寸分页
当添加该属性时,显示为简单分页
import { Pagination } from 'antd';
ReactDOM.render(
defaultCurrent={1} total={50} /&,
document.getElementById('components-pagination-demo-basic'));
基础分页。
import { Pagination } from 'antd';
function onShowSizeChange(current, pageSize) {
console.log(current, pageSize);
ReactDOM.render(
showSizeChanger onShowSizeChange={onShowSizeChange} defaultCurrent={3} total={500} /&,
document.getElementById('components-pagination-demo-changer'));
改变每页显示条目数。
import { Pagination } from 'antd';
ReactDOM.render(
size="small" defaultCurrent={2} total={50} /&,
document.getElementById('components-pagination-demo-mini'));
迷你版本。
import { Pagination } from 'antd';
import enUS from 'antd/lib/pagination/locale/en_US';
ReactDOM.render(
defaultCurrent={1} total={50} locale={enUS} /&,
document.getElementById('components-pagination-demo-locale'));
通过 locale 配置时区、语言等, 默认支持 en_US, zh_CN
import { Pagination } from 'antd';
ReactDOM.render(
defaultCurrent={1} total={500} /&,
document.getElementById('components-pagination-demo-more'));
更多分页。
import { Pagination } from 'antd';
ReactDOM.render(
showQuickJumper defaultCurrent={2} total={500} /&,
document.getElementById('components-pagination-demo-jump'));
快速跳转到某一页。
import { Pagination } from 'antd';
ReactDOM.render(
simple defaultCurrent={2} total={50} /&,
document.getElementById('components-pagination-demo-simple'));
简单地翻页。
import { Pagination } from 'antd';
let Container = React.createClass({
getInitialState() {
current: 3
onChange(page) {
console.log(page);
this.setState({
current: page
render() {
current={this.state.current} onChange={this.onChange} total={50} /&;
ReactDOM.render(
document.getElementById('components-pagination-demo-controlled'));
受控制的页码。Vue2.5 结合 Element UI 之 Table 和 Pagination 组件实现分页
2017年底了,总结了这一年多来的前端之路,Vue从入门到放弃,再二进宫,从 Vue1.0 持续跟踪到 Vue2.5。结合公司的一些实际项目,也封装了一些比较实用的组件。
由于现在公司管理平台主要运用Element UI,索性就结合组件Table 和 Pagination 封装了一个支持页面切换的Table组件,不啰嗦,直接上代码。(在之前发布的版本之上,已经优化了一波)
// Element UI
import Element from 'element-ui'
// 默认样式
import 'element-ui/lib/theme-chalk/index.css'
由于公司项目都是以 i 开头,所以,为了区分组件和页面,习惯于组件命名也以 i 开头。
首先把 Table、Pagination 组件加进来
&template&
&div class="table"&
&!--region 表格--&
&el-table id="iTable"&&/el-table&
&!--endregion--&
&!--region 分页--&
&el-pagination&&/el-pagination&
&!--endregion--&
&template&
养成写注释的好习惯,个人项目的注释量基本上不会低于 30%
&template&
&div class="table-page"&
&!--region table 表格--&
:list="list"
:total="total"
:otherHeight="otherHeight"
:options="options"
:pagination="pagination"
:columns="columns"
:operates="operates"
@handleSizeChange="handleSizeChange"
@handleIndexChange="handleIndexChange"
@handleSelectionChange="handleSelectionChange"
@handleFilter="handleFilter"
@handelAction="handelAction"&
&/i-table&
&!--endregion--&
&/template&
import iTable from '../../components/Table/Index'
export default {
components: {iTable},
otherHeight: 208,
columns: [
prop: 'id',
label: '编号',
align: 'center',
prop: 'title',
label: '标题',
align: 'center',
width: 400,
formatter: (row, column, cellValue) =& {
return `&span style="white-space:color:"&${row.title}&/span&`
prop: 'state',
label: '状态',
align: 'center',
width: '160',
render: (h, params) =& {
return h('el-tag', {
props: {type: params.row.state === 0 ? 'success' : params.row.state === 1 ? 'info' : 'danger'} // 组件的props
}, params.row.state === 0 ? '上架' : params.row.state === 1 ? '下架' : '审核中')
prop: 'author',
label: '作者',
align: 'center',
width: 120
prop: 'phone',
label: '联系方式',
align: 'center',
width: 160
prop: 'email',
label: '邮箱',
align: 'center',
width: 240
prop: 'createDate',
label: '发布时间',
align: 'center',
width: 180,
formatter: (row, column, cellValue) =& {
return this.$utils.Common.dateFormat(row.createDate, 'YYYY年MM月DD日 hh:mm')
], // 需要展示的列
operates: {
width: 200,
fixed: 'right',
label: '编辑',
type: 'warning',
show: true,
icon: 'el-icon-edit',
plain: true,
disabled: false,
method: (index, row) =& {
this.handleEdit(index, row)
label: '删除',
type: 'danger',
icon: 'el-icon-delete',
show: true,
plain: false,
disabled: false,
method: (index, row) =& {
this.handleDel(index, row)
}, // 操作按钮组
pagination: {
pageIndex: 1,
pageSize: 20
}, // 分页参数
options: {
stripe: true, // 是否为斑马纹 table
loading: false, // 是否添加表格loading加载动画
highlightCurrentRow: true, // 是否支持当前行高亮显示
mutiSelect: true // 是否支持列表项选中功能
} // table 的参数
mounted () {
methods: {
// 切换每页显示的数量
handleSizeChange (pagination) {
this.pagination = pagination
// 切换页码
handleIndexChange (pagination) {
this.pagination = pagination
handleSelectionChange (val) {
console.log('val:', val)
handleEdit (index, row) {
console.log(' index:', index)
console.log(' row:', row)
handleDel (index, row) {
console.log(' index:', index)
console.log(' row:', row)
除了 columns 参数和 operates 参数 之外,其它的参数应该还好理解,好的。那我们就详细的解释下这两个参数,那么我们就需要结合组件iTable.vue 来讲解了,接下来就给 iTable.vue 添加肌肉和血管,代码都贴了。
比较难理解的就是columns里面的 render 参数,使用了Vue的虚拟标签,为了就是能够在 table 表格的列中随心所欲的使用各种html标签 和 element UI 的其他组件。(你也可以直接写,看看 table 组件是否能识别,呵呵哒!)这个估计对于刚入门的小伙伴是一个比较难理解的地方,详细的大家可以先看下vue 的 ,解释的更清楚,如果有的小伙伴不理解,可以直接私信我~~~
&!--region 封装的分页 table--&
&!--region 封装的分页 table--&
&template&
&div class="table"&
id="iTable"
v-loading.iTable="options.loading"
:data="list"
:max-height="height"
:stripe="options.stripe"
ref="mutipleTable"
@selection-change="handleSelectionChange"&
&!--region 选择框--&
&el-table-column v-if="options.mutiSelect" type="selection" style="width: 55"&
&/el-table-column&
&!--endregion--&
&!--region 数据列--&
&template v-for="(column, index) in columns"&
&el-table-column :prop="column.prop"
:label="column.label"
:align="column.align"
:width="column.width"&
&template slot-scope="scope"&
&template v-if="!column.render"&
&template v-if="column.formatter"&
&span v-html="column.formatter(scope.row, column)"&&/span&
&/template&
&template v-else&
&span&{{scope.row[column.prop]}}&/span&
&/template&
&/template&
&template v-else&
&expand-dom :column="column" :row="scope.row" :render="column.render" :index="index"&&/expand-dom&
&/template&
&/template&
&/el-table-column&
&/template&
&!--endregion--&
&!--region 按钮操作组--&
&el-table-column ref="fixedColumn" label="操作" align="center" :width="operates.width" :fixed="operates.fixed"
v-if="operates.list.filter(_x=&_x.show === true).length & 0"&
&template slot-scope="scope"&
&div class="operate-group"&
&template v-for="(btn, key) in operates.list"&
&div class="item" v-if="btn.show"&
&el-button :type="btn.type" size="mini" :icon="btn.icon" :disabled="btn.disabled"
:plain="btn.plain" @click.native.prevent="btn.method(key,scope.row)"&{{ btn.label }}
&/el-button&
&/template&
&/template&
&/el-table-column&
&!--endregion--&
&/el-table&
&div style="height:12px"&&/div&
&!--region 分页--&
&el-pagination v-if="pagination" @size-change="handleSizeChange"
@current-change="handleIndexChange"
:page-size="tableCurrentPagination.pageSize"
:page-sizes="this.tableCurrentPagination.pageArray" :current-page="tableCurrentPagination.pageIndex"
layout="total,sizes, prev, pager, next,jumper"
:total="total"&&/el-pagination&
&!--endregion--&
&/template&
&!--endregion--&
const _pageArray = [20, 50, 100] // 每页展示条数的控制集合
export default {
type: Array,
default: [] // prop:表头绑定的地段,label:表头名称,align:每列数据展示形式(left, center, right),width:列宽
}, // 数据列表
columns: {
type: Array,
default: [] // 需要展示的列 === prop:列数据对应的属性,label:列名,align:对齐方式,width:列宽
operates: {
type: Object,
default: {} // width:按钮列宽,fixed:是否固定(left,right),按钮集合 === label: 文本,type :类型(primary / success / warning / danger / info / text),show:是否显示,icon:按钮图标,plain:是否朴素按钮,disabled:是否禁用,method:回调方法
type: Number,
default: 0
}, // 总数
pagination: {
type: Object,
default: null // 分页参数 === pageSize:每页展示的条数,pageIndex:当前页,pageArray: 每页展示条数的控制集合,默认 _page_array
otherHeight: {
type: Number,
default: 160
}, // 计算表格的高度
options: {
type: Object,
default: {
stripe: false, // 是否为斑马纹 table
loading: false, // 是否添加表格loading加载动画
highlightCurrentRow: false, // 是否支持当前行高亮显示
mutiSelect: false // 是否支持列表项选中功能
} // table 表格的控制参数
components: {
expandDom: {
functional: true,
row: Object,
render: Function,
index: Number,
type: Object,
default: null
render: (h, ctx) =& {
const params = {
row: ctx.props.row,
index: ctx.props.index
if (ctx.props.column) params.column = ctx.props.column
return ctx.props.render(h, params)
pageIndex: 1,
tableCurrentPagination: {},
multipleSelection: [] // 多行选中
created () {},
mounted () {
if (this.pagination && !this.pagination.pageSizes) {
this.pagination.pageArray = _pageArray // 每页展示条数控制
this.tableCurrentPagination = this.pagination || {
pageSize: this.total,
pageIndex: 1
} // 判断是否需要分页
computed: {
// 计算table高度
height () {
return this.$utils.Common.getWidthHeight().height - this.otherHeight
methods: {
// 切换每页显示的数量
handleSizeChange (size) {
if (this.pagination) {
this.tableCurrentPagination = {
pageIndex: 1,
pageSize: size
this.$emit('handleSizeChange', this.tableCurrentPagination)
// 切换页码
handleIndexChange (currnet) {
if (this.pagination) {
this.tableCurrentPagination.pageIndex = currnet
this.$emit('handleIndexChange', this.tableCurrentPagination)
// 多行选中
handleSelectionChange (val) {
this.multipleSelection = val
this.$emit('handleSelectionChange', val)
// 显示 筛选弹窗
showfilterDataDialog () {
this.$emit('handleFilter')
// 显示 表格操作弹窗
showActionTableDialog () {
this.$emit('handelAction')
&style lang="less" rel="stylesheet/less"&
@import "../../assets/styles/mixins";
height: 100%;
.el-pagination {
margin: 20
.el-table__header-wrapper, .el-table__fixed-header-wrapper {
.el-table-column--selection .cell {
padding: 0;
text-align:
.el-table__fixed-right {
bottom: 0 !
right: 6px !
z-index: 1004;
.operate-group {
flex-wrap:
margin-top: 4
margin-bottom: 4
flex: 0 0 50%;
.filter-data {
top: e("calc((100% - 100px) / 3)");
background-color: rgba(0, 0, 0, 0.7);
.table-action {
top: e("calc((100% - 100px) / 2)");
background-color: rgba(0, 0, 0, 0.7);
.fix-right {
height: 100
z-index: 1005;
writing-mode: vertical-
text-align:
line-height: 28
border-bottom-left-radius: 6
border-top-left-radius: 6
这就是全部 的代码了,原谅我的表达能力有限,如果能对你有点帮助,那就太好了!如果那里描述的不够详细或者有问题的地方,提出来,反正我也不改~~~,哈哈哈
完整的项目还是等我再优化下发粗来!
欢迎大家指点
打开新标签页发现好内容,掘金、GitHub、Dribbble、ProductHunt 等站点内容轻松获取。快来安装掘金浏览器插件获取高质量内容吧!分享选择栏目:
永远的学童
jQuery Pagination分页插件使用方法详解
本文《jQuery Pagination分页插件使用方法详解》提供学童君学习参考,知识和精力有限,无法一一勘误,若有所错误,可以在文末“反馈”按钮提出反馈。学童网(xuetn.com)尽量提供给您准确有价值的信息。
浏览《jQuery Pagination分页插件使用方法详解》小提示:
文章若附带资源/案例下载、效果/视频/动画演示,一般在文章底部资源列表里有提供
文章清晰的要点多数有加粗提示,多留意
涉及到代码地方,有清晰的代码样式。
文章内蓝色字体,多是本文的相关链接。
好文可以收藏到您个人的会员中心
要查阅跟本文相关的大量信息,可以访问专属栏目《》
本文实例为大家分享了jQuery Pagination分页插件的具体代码,供大家参考,具体内容如下
一、引用CSS和JS:
&link rel="nofollow" href="/Content/Plugins/jQuery.Pagination_v2.2/pagination.css" rel="external nofollow" rel="stylesheet"
type="text/css" /&
&script src="/Content/Plugins/jQuery.Pagination_v2.2/jquery.pagination.js" type="text/javascript"&&/script&
二、HTML:
&div id="Pagination" class="flickr" style="margin-top: 10 margin-left: 10"&
$(function () {
var total = parseInt("@(ViewBag.total)");
var page = parseInt("@(ViewBag.page)") - 1;
var pageSize = parseInt("@(ViewBag.pageSize)");
$("#Pagination").pagination(total, {
callback: function (page_id) {
window.location = "BoardList?page=" + page_id + "&pageSize=" + this.items_per_
}, //PageCallback() 为翻页调用次函数。
prev_text: " 上一页",
next_text: "下一页 ",
items_per_page: 10, //每页的数据个数
num_display_entries: 1, //两侧首尾分页条目数
current_page: page, //当前页码
num_edge_entries: 11 //连续分页主体部分分页条目数
四、后台代码:
public ActionResult BoardList()
PagerModel pager = new PagerModel();
if (Request["page"] == null)
pager.page = 1;
pager.rows = 10;
pager.sort = "Id";
pager.order = "desc";
pager.page = int.Parse(Request["page"]) + 1;
pager.rows = int.Parse(Request["pageSize"]);
pager.sort = "Id";
pager.order = "desc";
boardManageService.GetList(ref pager);
List&BoardModel& boardList = pager.result as List&BoardModel&;
ViewData["BoardModelList"] = boardL
ViewBag.page = pager.
ViewBag.total = pager.totalR
ViewBag.pageSize = pager.
return View();
#endregion
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持xuetn.com。
学童君~以上就是关于《jQuery Pagination分页插件使用方法详解》的全部内容,更深入了解可以关注更多此方面的信息,学童网(xuetn.com)给您提供此文的专有收藏链接,可以点击下方“收藏”按钮收藏佳文。
也可以选择如下操作:
获取和本文密切相关的大量同类型文章,可以访问《》栏目
关心跟此文相关大类栏目可以访问《》栏目,极大拓宽信息面。
如果有,文章相关的资源、代码包、视频、下载链接、演示效果可以在下方的资源列表里找到哦
知识点较多的文章可能会有上/下一页,上/下一篇,多留意。
当前观看编号:关闭
如果你要把《jQuery Pagination分页插件使用方法详解》相关的资源地址通过手机访问,或者要把其中某段信息保存到手机,那么你可以把它复制到上面的框中,通过手机扫一扫就可以啦。
Copyright (C) 2016.XueTN.com All rights reserved. our email[] - Collect from博客分类:
Mybatis 的物理分页是应用中的一个难点,特别是配合检索和排序功能叠加时更是如此。
我在最近的项目中开发了这个通用分页器,过程中参考了站内不少好文章,新年第一天,特此发文回馈网站。
特别鸣谢 paginator项目 ( ) ,阅读源码帮助很大。
项目框架是 SpringMVC+Mybatis, 需求是想采用自定义的分页标签,同时,要尽量少的影响业务程序开发的。如果你已经使用了JS框架( 如:Ext,EasyUi等)自带的分页机能,是属于前端分页,不在本文讨论范围。
【关于问题】
大多数分页器会使用在查询页面,要考虑以下问题:
1)分页时是要随时带有最近一次查询条件
2)不能影响现有的sql,类似aop的效果
3)mybatis提供了通用的拦截接口,要选择适当的拦截方式和时点
4)尽量少的影响现有service等接口
【关于依赖库】
Google Guava
作为基础工具包
Commons JXPath
用于对象查询
(1/23日版改善后,不再需要)
向前台传送Json格式数据转换用
【关于适用数据库】
现在只适用mysql
(如果需要用在其他数据库可参考 paginator的Dialect部分,改动都不大)
首先是Page类,比较简单,保存分页相关的所有信息,涉及到分页算法。虽然“其貌不扬”,但很重要。后面会看到这个page类对象会以“信使”的身份出现在全部与分页相关的地方。
* 封装分页数据
import java.util.L
import java.util.M
import org.codehaus.jackson.map.ObjectM
import org.slf4j.L
import org.slf4j.LoggerF
import com.google.common.base.J
import com.google.common.collect.L
import com.google.common.collect.M
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 pageS
//每页行数
private int totalR
//总记录数
private int totalP
private Map&String, String&
//查询条件
private Map&String, List&String&& paramL
//数组查询条件
private String searchU
private String pageNoD
//可以显示的页号(分隔符"|",总页数变更时更新)
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);
* 查询条件转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="";
json = mapper.writeValueAsString(map);
} catch (Exception e) {
logger.error("转换JSON失败", params, e);
* 数组查询条件转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="";
json = mapper.writeValueAsString(map);
} catch (Exception e) {
logger.error("转换JSON失败", params, e);
* 总件数变化时,更新总页数并计算显示样式
private void refreshPage(){
//总页数计算
totalPage = totalRecord%pageSize==0 ? totalRecord/pageSize : (totalRecord/pageSize + 1);
//防止超出最末页(浏览途中数据被删除的情况)
if ( pageNo & totalPage && totalPage!=0){
pageNo = totalP
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&=totalP 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&=totalP i++){
pageDisplays.add(i);
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 pageS
public void setPageSize(int pageSize) {
this.pageSize = pageS
public int getTotalRecord() {
return totalR
public void setTotalRecord(int totalRecord) {
this.totalRecord = totalR
refreshPage();
public int getTotalPage() {
return totalP
public void setTotalPage(int totalPage) {
this.totalPage = totalP
public Map&String, String& getParams() {
public void setParams(Map&String, String& params) {
this.params =
public Map&String, List&String&& getParamLists() {
return paramL
public void setParamLists(Map&String, List&String&& paramLists) {
this.paramLists = paramL
public String getSearchUrl() {
return searchU
public void setSearchUrl(String searchUrl) {
this.searchUrl = searchU
public String getPageNoDisp() {
return pageNoD
public void setPageNoDisp(String pageNoDisp) {
this.pageNoDisp = pageNoD
然后是最核心的拦截器了。涉及到了mybatis的核心功能,期间阅读大量mybatis源码几经修改重构,辛苦自不必说。
核心思想是将拦截到的select语句,改装成select count(*)语句,执行之得到,总数据数。再根据page中的当前页号算出limit值,拼接到select语句后。
为简化代码使用了Commons JXPath 包,做对象查询。
* 分页用拦截器
import java.sql.C
import java.sql.PreparedS
import java.sql.ResultS
import java.util.P
import org.apache.commons.jxpath.JXPathC
import org.apache.commons.jxpath.JXPathNotFoundE
import org.apache.ibatis.executor.E
import org.apache.ibatis.executor.parameter.DefaultParameterH
import org.apache.ibatis.mapping.BoundS
import org.apache.ibatis.mapping.MappedS
import org.apache.ibatis.mapping.MappedStatement.B
import org.apache.ibatis.mapping.ParameterM
import org.apache.ibatis.mapping.SqlS
import org.apache.ibatis.plugin.I
import org.apache.ibatis.plugin.I
import org.apache.ibatis.plugin.I
import org.apache.ibatis.plugin.P
import org.apache.ibatis.plugin.S
import org.apache.ibatis.session.ResultH
import org.apache.ibatis.session.RowB
@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 = 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();
StringBuffer sb = new StringBuffer();
sb.append(originalSql).append(" limit ").append(offset).append(",").append(page.getPageSize());
BoundSql 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);
for(String xpath : xpaths){
result = context.selectSingleNode(xpath);
} catch (JXPathNotFoundException e) {
if ( result instanceof Page ){
return (Page)
* 复制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());
builder.keyProperty(ms.getKeyProperty());
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 newBoundS
* 根据原Sql语句获取对应的查询总记录数的Sql语句
private String getCountSql(String sql) {
return "SELECT COUNT(*) FROM (" + sql + ") aliasForPage";
public class BoundSqlSqlSource implements SqlSource {
BoundSql boundS
public BoundSqlSqlSource(BoundSql boundSql) {
this.boundSql = boundS
public BoundSql getBoundSql(Object parameterObject) {
return boundS
public Object plugin(Object arg0) {
return Plugin.wrap(arg0, this);
public void setProperties(Properties arg0) {
到展示层终于可以轻松些了,使用了文件标签来简化前台开发。
采用临时表单提交,CSS使用了Bootstrap。
&%@tag pageEncoding="UTF-8"%&
&%@ attribute name="page" type="cn.com.intasect.ots.common.utils.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;
paraJson["pageSize"] = "&%=page.getPageSize()%&";
var jsPost = function(action, values, valueLists) {
var id = Math.random();
document.write('&form id="post' + id + '" name="post'+ id +'" action="' + 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);
&div class="page-pull-right"&
&% if (current!=1 && end!=0){%&
&button class="btn btn-default btn-sm" onclick="pageClick(1)"&首页&/button&
&button class="btn btn-default btn-sm" onclick="pageClick(${current-1})"&前页&/button&
&%}else{%&
&button class="btn btn-default btn-sm" &首页&/button&
&button class="btn btn-default btn-sm" &前页&/button&
&c:forTokens items="${pList}" delims="|" var="pNo"&
&c:choose&
&c:when test="${pNo == 0}"&
&label style="font-size: 10 width: 20 text-align:"&ooo&/label&
&c:when test="${pNo != current}"&
&button class="btn btn-default btn-sm" onclick="pageClick(${pNo})"&${pNo}&/button&
&c:otherwise&
&button class="btn btn-primary btn-sm" style="font-weight:"&${pNo}&/button&
&/c:otherwise&
&/c:choose&
&/c:forTokens&
&% if (current&end && end!=0){%&
&button class="btn btn-default btn-sm" onclick="pageClick(${current+1})"&后页&/button&
&button class="btn btn-default btn-sm" onclick="pageClick(${end})"&末页&/button&
&%}else{%&
&button class="btn btn-default btn-sm"&后页&/button&
&button class="btn btn-default btn-sm"&末页&/button&
注意“信使”在这里使出了浑身解数,7个主要的get方法全部用上了。
page.getPageNo()
//当前页号
page.getTotalPage()
page.getPageNoDisp()
//可以显示的页号
page.getParaJson()
//查询条件
page.getParaListJson()
//数组查询条件
page.getPageSize()
//每页行数
page.getSearchUrl()
//Url地址(作为action名称)
到这里三个核心模块完成了。然后是拦截器的注册。
【拦截器的注册】
需要在mybatis-config.xml 中加入拦截器的配置
&plugin interceptor="cn.com.dingding.common.utils.PageInterceptor"&
&/plugins&
【相关代码修改】
首先是后台代码的修改,Controller层由于涉及到查询条件,需要修改的内容较多。
1)入参需增加 pageNo,pageSize 两个参数
2)根据pageNo,pageSize 及你的相对url构造page对象。(
3)最重要的是将你的其他入参(查询条件)保存到page中
4)Service层的方法需要带着page这个对象(最终目的是传递到sql执行的入参,让拦截器识别出该sql需要分页,同时传递页号)
5)将page对象传回Mode中
@RequestMapping(value = "/user/users")
public String list(
@ModelAttribute("name") String name,
@ModelAttribute("levelId") String levelId,
@ModelAttribute("subjectId") String subjectId,
Model model) {
model.addAttribute("users",userService.selectByNameLevelSubject(
name, levelId, subjectId));
return USER_LIST_JSP;
@RequestMapping(value = "/user/users")
public String list(
@RequestParam(required = false, defaultValue = "1") int pageNo,
@RequestParam(required = false, defaultValue = "5") int pageSize,
@ModelAttribute("name") String name,
@ModelAttribute("levelId") String levelId,
@ModelAttribute("subjectId") String subjectId,
Model model) {
// 这里是“信使”诞生之地,一出生就加载了很多重要信息!
Page page = Page.newBuilder(pageNo, pageSize, "users");
page.getParams().put("name", name);
//这里再保存查询条件
page.getParams().put("levelId", levelId);
page.getParams().put("subjectId", subjectId);
model.addAttribute("users",userService.selectByNameLevelSubject(
name, levelId, subjectId, page));
model.addAttribute("page", page);
//这里将page返回前台
return USER_LIST_JSP;
注意pageSize的缺省值决定该分页的每页数据行数 ,实际项目更通用的方式是使用配置文件指定。
拦截器可以自动识别在Map或Bean中的Page对象。
如果使用Bean需要在里面增加一个page项目,Map则比较简单,以下是例子。
public List&UserDTO& selectByNameLevelSubject(String name, String levelId, String subjectId, Page page) {
Map&String, Object& map = Maps.newHashMap();
levelId = DEFAULT_SELECTED.equals(levelId)?null: levelId;
subjectId = DEFAULT_SELECTED.equals(subjectId)?null: subjectId;
if (name != null && name.isEmpty()){
map.put("name", name);
map.put("levelId", levelId);
map.put("subjectId", subjectId);
map.put("page", page);
//MAP的话加这一句就OK
return userMapper.selectByNameLevelSubject(map);
前台页面方面,由于使用了标签,在适当的位置加一句就够了。
&tags:page page="${page}"/&
“信使”page在这里进入标签,让分页按钮最终展现。
至此,无需修改一句sql,完成分页自动化。
【效果图】
现在回过头来看下最开始提出的几个问题:
1)分页时是要随时带有最近一次查询条件
回答:在改造Controller层时,通过将提交参数设置到 Page对象的 Map&String, String& params(单个基本型参数) 和 Map&String, List&String&& paramLists(数组基本型)解决。
顺便提一下,例子中没有涉及参数是Bean的情况,实际应用中应该比较常见。简单的方法是将Bean转换层Map后加入到params。
2)不能影响现有的sql,类似aop的效果
回答:利用Mybatis提供了 Interceptor 接口,拦截后改头换面去的件数并计算limit值,自然能神不知鬼不觉。
3)mybatis提供了通用的拦截接口,要选择适当的拦截方式和时点
回答:@Signature(method = "query", type = Executor.class, args = {
MappedStatement.class, Object.class, RowBounds.class,
ResultHandler.class }) 只拦截查询语句,其他增删改查不会影响。
4)尽量少的影响现有service等接口
回答:这个自认为本方案做的还不够好,主要是Controller层改造上,感觉代码量还比较大。如果有有识者知道更好的方案还请多指教。
【遗留问题】
1)一个“明显”的性能问题,是每次检索前都要去 select count(*)一次。在很多时候(数据变化不是特别敏感的场景)是不必要的。调整也不难,先Controller参数增加一个 totalRecord 总记录数 ,在稍加修改一下Page相关代码即可。
2)要排序怎么办?本文并未讨论排序,但是方法是类似的。以上面代码为基础,可以较容易地实现一个通用的排序标签。
===================================== 分割线 (1/8)=======================================
对于Controller层需要将入参传入Page对象的问题已经进行了改善,思路是自动从HttpServletRequest 类中提取入残,减低了分页代码的侵入性,详细参看文章
===================================== 分割线 (1/23)=======================================
再次改善,使用ThreadLocal类封装Page对象,让Service层等无需传Page对象,减小了侵入性。拦截器也省去了查找Page对象的动作,性能也同时改善。整体代码改动不大。
===================================== 分割线 (2/21)=======================================
今天比较闲,顺便聊下这个分页的最终版,当然从来只有不断变化的需求,没有完美的方案,这里所说的最终版其实是一个优化后的“零侵入”的方案。为避免代码混乱还是只介绍思路。在上一个版本(1/23版)基础上有两点改动:
一是增加一个配置文件,按Url 配置初始的每页行数。如下面这样(pagesize 指的是每页行数):
&pager url="/user/users" pagesize="10" /&
二是增加一个过滤器,并将剩下的位于Control类中 唯一侵入性的分页相关代码移入过滤器。发现当前的 Url
在配置文件中有匹配是就构造Page对象,并加入到Response中。
使用最终版后,对于开发者需要分页时,只要在配置文件中加一行,并在前端页面上加一个分页标签即可,其他代码,SQL等都不需要任何改动,可以说简化到了极限。
【技术列表】
总结下最终方案用到的技术:
Mybatis 提供的拦截器接口实现(实现分页sql自动 select count 及limit 拼接)
Servlet过滤器+ThreadLocal
(生成线程共享的Page对象)
(实现前端共通的分页效果)
临时表单提交 (减少页面体积)
【其他分页方案比较】
时下比较成熟的 JPA 的分页方案,(主要应用在 Hibernate + Spring Data 的场合),主要切入点在DAO层,而Controller等各层接口依然需要带着pageNumber,pageSize 这些的参数,另外框架开发者还要掌握一些必须的辅助类,如:
org.springframework.data.repository.PagingAndSortingRepository
可分页DAO基类
org.springframework.data.domain.Page
抽取结果封装类
org.springframework.data.domain.Pageable
分页信息类
比较来看 本方案 做到了分页与业务逻辑的完全解耦,开发者无需关注分页,全部通过配置实现。通过这个例子也可以反映出Mybatis在底层开发上有其独特的优势。
【备选方案】
最后再闲扯下,上面的最终案是基于 Url 配置的,其实也可以基于方法加自定义注解来做。这样配置文件省了,但是要增加一个注解解析类。注解中参数 为初始的每页行数。估计注解fans会喜欢,如下面的样子:
@RequestMapping(value = "/user/users")
@Pagination(size=10)
public String list(
同样与过滤器配合使用,只是注解本身多少还是有“侵入性”。在初始行数基本不会变更时,这个比较直观的方案也是不错的选择。大家自行决定吧。
浏览 206421
有jar包maven地址就好了,或者直接上个实例其中需要的jar包在pom.xml中的配置&dependency& &groupId&com.google.guava&/groupId& &artifactId&guava&/artifactId& &version&18.0&/version&&/dependency&&dependency& &groupId&commons-jxpath&/groupId& &artifactId&commons-jxpath&/artifactId& &version&1.3&/version&&/dependency&
Smart_咚咚 写道这种方式不能通过链接访问。。post提交 我看看能改不好吧& 没啥问题& 关键是好像把所有select都拦截了。。不过估计你不定什么时候才上奇怪 我看了下代码& 好像没这个问题啊& 我自己找找吧
这种方式不能通过链接访问。。post提交 我看看能改不好吧& 没啥问题& 关键是好像把所有select都拦截了。。不过估计你不定什么时候才上
不明白为什么要引入Guava库,一个2m多的jar包,而程序中也只是使用Guava创建集合,是不是有些大材小用了。
引用引用引用引用引用[u][i][b][b][b]引用引用[list]
[*][*][*][*][*][img][*][*][*][*][*][flash=200,200][url][img][list][*][*][*][*][*][*][*][*][*][*][*][/list][/img][/url][/flash][*][*][*][*][*][/img]
[*][*][*][*][/list][/b][/b][/b][/i][/u][*][*][/list][*][/list][/list][/list][/img][/url][/url]
lisciple 写道model.addAttribute("page", page);&&&&&&&&&&&& //这里将page返回前台& 请问你最后的版本中从threadlocal拿到page还是放在model里面返回吗?能不能把这行也消灭掉?page的生成及放入model都在过滤器中,对业务代码无侵入,没有消灭的必要了吧。过滤器中的request貌似不能返回请求吧? 这个request是过来的路,不是回去的路吧?您能具体说说嘛?
model.addAttribute("page", page);&&&&&&&&&&&& //这里将page返回前台& 请问你最后的版本中从threadlocal拿到page还是放在model里面返回吗?能不能把这行也消灭掉?page的生成及放入model都在过滤器中,对业务代码无侵入,没有消灭的必要了吧。
& 上一页 1
duanhengbin
浏览: 326230 次
来自: 成都
如果传值进来是int findChaSanFang(@Para ...
searchPageWithXpath,这个方法是做什么用的啊 ...
额滴神啊 搞个分页这复杂..
shreo_2007 写道有jar包maven地址就好了,或者 ...
Smart_咚咚 写道Smart_咚咚 写道这种方式不能通过链 ...
(window.slotbydup=window.slotbydup || []).push({
id: '4773203',
container: s,
size: '200,200',
display: 'inlay-fix'

我要回帖

更多关于 export default 使用 的文章

 

随机推荐