Mybatis Physical Pagination
1. Requirements:
when we use the sql like "select * from targetTable", we get all records of the table,
but we usually just need one page of records(about 10 records).
so we need to change the sql sentences.
2. Solution(Mybatis Physical Pagination):
2.1 Mybatis help us to reduce the difficult of operating database, its
interceptor is a good tool to change the original SQL sentence.
Let's begin!
2.2 Page Interceptor
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Properties; import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.logging.Log;
import org.apache.ibatis.logging.LogFactory;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
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.reflection.MetaObject;
import org.apache.ibatis.reflection.factory.DefaultObjectFactory;
import org.apache.ibatis.reflection.factory.ObjectFactory;
import org.apache.ibatis.reflection.wrapper.DefaultObjectWrapperFactory;
import org.apache.ibatis.reflection.wrapper.ObjectWrapperFactory;
import org.apache.ibatis.scripting.defaults.DefaultParameterHandler;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.RowBounds; /**
* 通过拦截<code>StatementHandler</code>的<code>prepare</code>方法,重写sql语句实现物理分页。
*
*/
@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class})})
public class PageInterceptor implements Interceptor {
private static final Log logger = LogFactory.getLog(PageInterceptor.class);
private static final ObjectFactory DEFAULT_OBJECT_FACTORY = new DefaultObjectFactory();
private static final ObjectWrapperFactory DEFAULT_OBJECT_WRAPPER_FACTORY = new DefaultObjectWrapperFactory();
private static String defaultDialect = "oracle"; // 数据库类型(默认为mysql)
private static String defaultPageSqlId = ".*Page$"; // 需要拦截的ID(正则匹配)
private static String dialect = "oracle"; // 数据库类型(默认为mysql)
private static String pageSqlId = ""; // 需要拦截的ID(正则匹配) public Object intercept(Invocation invocation) throws Throwable {
StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
MetaObject metaStatementHandler = MetaObject.forObject(statementHandler, DEFAULT_OBJECT_FACTORY,
DEFAULT_OBJECT_WRAPPER_FACTORY);
// 分离代理对象链(由于目标类可能被多个拦截器拦截,从而形成多次代理,通过下面的两次循环可以分离出最原始的的目标类)
while (metaStatementHandler.hasGetter("h")) {
Object object = metaStatementHandler.getValue("h");
metaStatementHandler = MetaObject.forObject(object, DEFAULT_OBJECT_FACTORY, DEFAULT_OBJECT_WRAPPER_FACTORY);
}
// 分离最后一个代理对象的目标类
while (metaStatementHandler.hasGetter("target")) {
Object object = metaStatementHandler.getValue("target");
metaStatementHandler = MetaObject.forObject(object, DEFAULT_OBJECT_FACTORY, DEFAULT_OBJECT_WRAPPER_FACTORY);
}
Configuration configuration = (Configuration) metaStatementHandler.getValue("delegate.configuration");
dialect = configuration.getVariables().getProperty("dialect");
if (null == dialect || "".equals(dialect)) {
logger.warn("Property dialect is not setted,use default 'oracle' ");
dialect = defaultDialect;
}
pageSqlId = configuration.getVariables().getProperty("pageSqlId");
if (null == pageSqlId || "".equals(pageSqlId)) {
logger.warn("Property pageSqlId is not setted,use default '.*Page$' ");
pageSqlId = defaultPageSqlId;
}
MappedStatement mappedStatement = (MappedStatement) metaStatementHandler.getValue("delegate.mappedStatement");
// 只重写需要分页的sql语句。通过MappedStatement的ID匹配,默认重写以Page结尾的MappedStatement的sql
if (mappedStatement.getId().matches(pageSqlId)) {
BoundSql boundSql = (BoundSql) metaStatementHandler.getValue("delegate.boundSql");
Object parameterObject = boundSql.getParameterObject();
if (parameterObject == null) {
throw new NullPointerException("parameterObject is null!");
} else {
PageParameter page = (PageParameter) metaStatementHandler
.getValue("delegate.boundSql.parameterObject.page");//此处在mysql数据源时取到的对象page的pageno一直为1,可以通过改这里的逻辑取到pageNo
String sql = boundSql.getSql();
// 重写sql
String pageSql = buildPageSql(sql, page);
metaStatementHandler.setValue("delegate.boundSql.sql", pageSql);
// 采用物理分页后,就不需要mybatis的内存分页了,所以重置下面的两个参数
metaStatementHandler.setValue("delegate.rowBounds.offset", RowBounds.NO_ROW_OFFSET);
metaStatementHandler.setValue("delegate.rowBounds.limit", RowBounds.NO_ROW_LIMIT); }
}
// 将执行权交给下一个拦截器
return invocation.proceed();
}
/**
* 根据数据库类型,生成特定的分页sql
*
* @param sql
* @param page
* @return
*/
private String buildPageSql(String sql, PageParameter page) {
if (page != null) {
StringBuilder pageSql = new StringBuilder();
if ("mysql".equals(dialect)) {
pageSql = buildPageSqlForMysql(sql, page);
} else if ("oracle".equals(dialect)) {
pageSql = buildPageSqlForOracle(sql, page);
} else {
return sql;
}
return pageSql.toString();
} else {
return sql;
}
} /**
* mysql的分页语句
*
* @param sql
* @param page
* @return String
*/
public StringBuilder buildPageSqlForMysql(String sql, PageParameter page) {
StringBuilder pageSql = new StringBuilder(100);
String beginrow = String.valueOf((page.getPageNo() - 1) * page.getPageSize());
pageSql.append(sql);
pageSql.append(" limit " + beginrow + "," + page.getPageSize());
return pageSql;
} /**
* oracle的分页
*
* @param sql
* @param page
* @return String
*/
public StringBuilder buildPageSqlForOracle(String sql, PageParameter page) {
StringBuilder pageSql = new StringBuilder(100);
String beginrow = String.valueOf((page.getPageNo() - 1) * page.getPageSize());
String endrow = String.valueOf(page.getPageNo() * page.getPageSize()); pageSql.append("select * from ( select temp.*, rownum row_id from ( ");
pageSql.append(sql);
pageSql.append(" ) temp where rownum <= ").append(endrow);
pageSql.append(") where row_id > ").append(beginrow);
return pageSql;
} public Object plugin(Object target) {
// 当目标类是StatementHandler类型时,才包装目标类,否者直接返回目标本身,减少目标被代理的次数
if (target instanceof StatementHandler) {
return Plugin.wrap(target, this);
} else {
return target;
}
} public void setProperties(Properties properties) {
} }
2.3 Page Object
/**
* 分页参数类
*
*/
public class PageParameter { public static final int DEFAULT_PAGE_SIZE = 10; private int pageSize;
private int pageNo;
private int prePage;
private int nextPage;
private int totalPage;
private int totalCount; public PageParameter() {
this.pageNo = 1;
this.pageSize = DEFAULT_PAGE_SIZE;
}
/**
* get method for reflect
* @return
*/
public PageParameter getPage(){
return new PageParameter();
} /**
*
* @param currentPage
* @param pageSize
*/
public PageParameter(int pageNo, int pageSize) {
this.pageNo = pageNo;
this.pageSize = pageSize;
} 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 getPrePage() {
return prePage;
} public void setPrePage(int prePage) {
this.prePage = prePage;
} public int getNextPage() {
return nextPage;
} public void setNextPage(int nextPage) {
this.nextPage = nextPage;
} public int getTotalPage() {
return totalPage;
} public void setTotalPage(int totalPage) {
this.totalPage = totalPage;
} public int getTotalCount() {
return totalCount;
} public void setTotalCount(int totalCount) {
this.totalCount = totalCount;
} }
2.4 MVC Controller
import org.apache.commons.io.filefilter.FalseFileFilter;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam; import java.util.List; import javax.annotation.Resource;
import com.cdv.ppms.core.feature.orm.mybatis.PageParameter;
import com.cdv.ppms.web.model.ObjProgramWithBLOBs;
import com.cdv.ppms.web.service.ProgramService;
import com.cdv.ppms.web.service.impl.ProgramServiceImpl;
/**
* 节目管理
* @author rocky
*
*/
@Controller
@RequestMapping("/program/")
public class ProgramController {
@Resource
private ProgramService programService; @RequestMapping("uneditedProgram")
public String uneditedProgram(@RequestParam(required=false) Integer pageNo , Model model){
PageParameter page = new PageParameter();
if(pageNo!=null && pageNo>1){
page.setPageNo(pageNo);
}
List<ObjProgramWithBLOBs> programList = programService.selectProgramListPage(page);
int totalCount = programService.selectTotalCount();
page.setTotalCount(totalCount);
page.setTotalPage(totalCount%page.getPageSize()==0 ? totalCount/page.getPageSize() : totalCount/page.getPageSize()+1);
model.addAttribute("programList", programList);
model.addAttribute("page", page);
return "/program/program_unedited";
}
}
2.5 JSP page
<%
String path = request.getContextPath();
String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort()
+ path + "/"; %>
<div class="row">
<div class="gigantic pagination">
<a href="#" class="first" data-action="first">«</a>
<a href="#" class="previous" data-action="previous">‹</a>
<input type="text" readonly="readonly" data-max-page="40" />
<a href="#" class="next" data-action="next">›</a>
<a href="#" class="last" data-action="last">»</a>
</div>
</div> <form method="post" id="pageForm" name="pageForm"
action="<%=path%>/finished_task.action">
<input input="hidden" id="pageNo" name="pageNo" value="${page.pageNo }" />
</form> <link rel="stylesheet" href="<%=path %>/static/css/jqpagination.css"/>
<link rel="stylesheet" href="<%=path %>/static/css/demo.css"/>
<script src="<%=path %>/static/script/jquery.jqpagination.min.js"></script>
<script>
var path = '<%=path %>';
var pageNo = ${page.pageNo};
var totalPage = ${page.totalPage};
$('.pagination').jqPagination({
link_string : path+'/rest/program/uneditedProgram?pageNo={page_number}',
current_page: pageNo, //设置当前页 默认为1
max_page : totalPage, //设置最大页 默认为1
page_string : '当前第{current_page}页,共{max_page}页',
paged : function(page) {
console.log(page);
$("#pageNo").val(page);
$("#pageForm").submit();
}
});
</script>
2.6 mybatis config file
<plugins>
<!--Paging Interceptor -->
<plugin interceptor="com.cdv.ppms.core.feature.orm.mybatis.PageInterceptor"/>
</plugins>
2.7 jqPagination Plugin
https://github.com/beneverard/jqPagination
Mybatis Physical Pagination的更多相关文章
- Spring+Mybatis+jQuery.Pagination.js异步分页及JsonConfig的使用
在开发工作中经常用到异步分页,这里简单整理一下资料. 一.Controller方法 package com.lwj.controller; import javax.servlet.http.Http ...
- jDialects:一个从Hibernate抽取的支持70多种数据库方言的原生SQL分页工具
jDialects(https://git.oschina.net/drinkjava2/jdialects) 是一个收集了大多数已知数据库方言的Java小项目,通常可用来创建分页SQL和建表DDL语 ...
- Elasticsearch教程(九) elasticsearch 查询数据 | 分页查询
Elasticsearch 的查询很灵活,并且有Filter,有分组功能,还有ScriptFilter等等,所以很强大.下面上代码: 一个简单的查询,返回一个List<对象> .. ...
- SqlHelper发布——比你期望的还要多的多(例如比MyBatis-Pagehelper性能更高)
SqlHelper发布——比Mybatis-PageHelper性能更高 起源 前段时间开启了一个新的项目,在选择分页插件时,发现github上很流行的一个是pagehelper,在百度上搜索了一下, ...
- SqlHelper发布—比Pagehelper更好用的分页插件
SqlHelper发布-比PageHelper性能更高 起源 前段时间开启了一个新的项目,在选择分页插件时,发现github上很流行的一个是pagehelper,在百度上搜索了一下,使用量.由于项目紧 ...
- spring mvc+mybatis+sql server简单配置
context.xml配置文件 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns=&qu ...
- 记一次 IDEA mybatis.generator 自定义扩展插件
在使用 idea mybatis.generator 生成的代码,遇到 生成的代码很多重复的地方, 虽然代码是生成的,我们也不应该允许重复的代码出现,因为这些代码后期都要来手动维护. 对于生成时间戳注 ...
- 【SSM 5】Mybatis分页插件的使用
一.添加maven依赖项 <span style="font-family:KaiTi_GB2312;font-size:18px;"><dependency&g ...
- mybatis结合分页的使用及解析.
首先说明: 这里分页是使用了SSM框架+ jsp 来做的, 当然分页还有其他的很多做法, 比如easyUI自带的分页效果. 但是这些原理都是很相似的, 再次只做为学习总结之用. 一, 效果图这里的截图 ...
随机推荐
- WinDbg 命令集锦
//断点相关 bp + 地址 设置断点bl 显示已经设定的断点bu + 地址 设置断点,但是这种类型断点再下一次启动时被记录bc 清除断点对于断点范围,可以用*匹配,-表示一个范围,表达多个可用,号 ...
- A Year Of Books - 2016 Javaer书单
A Year Of Books - 2016 Javaer书单 (PS:欢迎留言推荐,很多来自白衣大哥的推荐) 1. OS & Networking <编码 : 隐匿在计算机软硬件背后的 ...
- 理解WebSocket
WebSocket的动机是什么? 目前的Web通信使用的是HTTP协议,HTTP协议是基于TCP协议的应用层协议,HTTP协议的工作模式是request/response模式.在一次通信中,必须首先由 ...
- 超微 X9DRL-iF 服务器主板简介 BIOS相关图解
超微 X9DRL-iF 服务器主板简介 BIOS相关图解 板载串口阵列相关简介 网烁信息805 发布时间:2012-6-15 21:10:09 浏览数:2745 随着Intel E5至强的 ...
- php 设计模式 - 单例
概述: 作为对象的创建模式,单例确保某一个内在系统中只存在一个实例,它不可以创建副本. 克隆函数(__clone )以及构造函数(__construct )必须声明为私用, 防止外部程序 创建一个新类 ...
- [转]非OpenVZ下利用谷歌TCP-BBR协议单边加速你的VPS
前段时间谷歌推出了新的 TCP-BBR 开源算法,可以起到单边加速 TCP 连接的效果,也就是不用客户端的配合,用来替代收费的锐速再合适不过,毕竟开源免费.TCP-BBR 的目的是要尽量跑满带宽,并且 ...
- CSS 块状元素和内联元素
在用CSS布局页面的时候,我们会将HTML标签分成两种,块状元素和内联元素(我们平常用到的div和p就是块状元素,链接标签a就是内联元素) 块状元素一般是其他元素的容器,可容纳内联元素和其他块状元素, ...
- JavaScript 变量生命周期
变量在它声明时初始化. 局部变量在函数调用时创建,当函数执行完毕后销毁,这和为什么要使用闭包也有一点关系,当然这是另外的话题了. 全局变量在页面(当前页面)关闭后销毁.
- vs2010设置
解决方案管理器文件自动定位:工具--选项--项目和解决方案--常规--在解决方案资源管理器中跟踪活动项(前打勾). VAssistX拼写错误的下划波浪线去掉:在VAssistX菜单栏->Visu ...
- 【网站开发】在新浪SAE上搭建一个博客
概述 在新浪SAE上搭建一个博客 1.访问新浪SAE站点 http://sae.sina.com.cn/ 2.注册新浪SAE 3.选择应用仓库 4.选择WordPress 5.安装WordPress ...