第一个做法,就是直接使用我们的sql语句进行分页,也就是在mapper里面加上分页的语句就好了。

<select id="" parameterType="" resultType="" resultMap="">
Select ROWNUM,ID,NAME FROM(Select ROWNUM as ROWNO, ID,NAME from CHANGED_CONTENT
<where>
<![CDATA[ROWNUM <= #{endRow}]]>
</where>
)
<where>
<![CDATA[ROWNO > #{startRow}]]>
</where>
</select>

然后我们在使用这个dao的时候,传入我们的分页的参数,就可以实现我们的分页需求了。其实很简单。


第二个做法,是一个更为通用的做法,那就是利用mybatis的拦截器,拦截每一个sql,在需要分页的sql加上分页的语句,就可以实现我们的分页功能。

我们可以查看 org.mybatis.spring.SqlSessionFactoryBean,其实他有一个plugin属性,我们可以配置实现一个插件来实现我们的需求。

首先说明一下实现的流程

例如我们有一条sql

String sql =select * from tableXXX where a=xxxx

我们的 拦截器拦截这条sql,然后判断是否是需要分页的,然后将这条sql转化为分页的sql

<span style="font-family: 微软雅黑; background-color: rgb(255, 255, 255);">    String countSql = "select count(1) from (" + sql    + ") tmp_count"  </span>
这样我们就可以得到分页的总页数了。

然后, 拼上我们传入的分页数据,哪一页,每一页几条数据:

select * from ( select row_.*, rownum rownum_ from (   sql        ) row_ ) where rownum_ <=  endString   and rownum_ >  offsetPlaceholder

这样我们就可以得到我们本页的返回了。

具体的代码如下:

首先是实现了ibatis intercepter的pageplugin
public class PagePlugin implements Interceptor {

    private static Logger log = Logger.getLogger("page plugin");
private static Dialect dialectObject = null; // 数据库方言
private static String pageSqlId = ""; // mybaits的数据库xml映射文件中需要拦截的ID(正则匹配) public Object intercept(Invocation ivk) throws Throwable {
if (ivk.getTarget() instanceof RoutingStatementHandler) {
log.info("com.dragon.dao.pulgin.mybatis.plugin.PagePlugin.intercept() enter*****************");
RoutingStatementHandler statementHandler = (RoutingStatementHandler) ivk
.getTarget();
BaseStatementHandler delegate = (BaseStatementHandler) ReflectHelper
.getValueByFieldName(statementHandler, "delegate");
MappedStatement mappedStatement = (MappedStatement) ReflectHelper
.getValueByFieldName(delegate, "mappedStatement");
/**
* 方法1:通过ID来区分是否需要分页..*query.* 方法2:传入的参数是否有page参数,如果有,则分页,
*/
if (mappedStatement.getId().matches(pageSqlId)) { // 拦截需要分页的SQL
BoundSql boundSql = delegate.getBoundSql();
Object parameterObject = boundSql.getParameterObject();// 分页SQL<select>中parameterType属性对应的实体参数,即Mapper接口中执行分页方法的参数,该参数不得为空
if (parameterObject == null) {
//throw new NullPointerException("boundSql.getParameterObject() is null!");
return ivk.proceed();
} else { PageView pageView = null;
if (parameterObject instanceof PageView) { // 参数就是Pages实体
pageView = (PageView) parameterObject;
} else if (parameterObject instanceof Map) {
for (Entry entry : (Set<Entry>) ((Map) parameterObject).entrySet()) {
if (entry.getValue() instanceof PageView) {
pageView = (PageView) entry.getValue();
break;
}
}
} else { // 参数为某个实体,该实体拥有Pages属性
pageView = ReflectHelper.getValueByFieldType(
parameterObject, PageView.class);
if (pageView == null) {
return ivk.proceed();
}
} String sql = boundSql.getSql();
PreparedStatement countStmt = null;
ResultSet rs = null;
try {
Connection connection = (Connection) ivk.getArgs()[0];
String countSql = "select count(1) from (" + sql
+ ") tmp_count"; // 记录统计
countStmt = connection.prepareStatement(countSql);
ReflectHelper.setValueByFieldName(boundSql, "sql",
countSql);
DefaultParameterHandler parameterHandler = new DefaultParameterHandler(
mappedStatement, parameterObject, boundSql);
parameterHandler.setParameters(countStmt);
rs = countStmt.executeQuery();
Long count = 0L;
if (rs.next()) {
count = ((Number) rs.getObject(1)).longValue();
}
pageView.setRowCount(count);
} finally {
try {
rs.close();
} catch (Exception e) {
}
try {
countStmt.close();
} catch (Exception e) {
}
}
String pageSql = generatePagesSql(sql, pageView);
ReflectHelper.setValueByFieldName(boundSql, "sql", pageSql); // 将分页sql语句反射回BoundSql.
}
}
}
return ivk.proceed();
} /**
* 根据数据库方言,生成特定的分页sql
*
* @param sql
* @param page
* @return
*/
private String generatePagesSql(String sql, PageView page) {
if (page != null && dialectObject != null) {
//pageNow默认是从1,而已数据库是从0开始计算的.所以(page.getPageNow()-1)
int pageNow = page.getPageNow();
return dialectObject.getLimitString(sql, (pageNow <= 0 ? 0 : pageNow - 1)
* page.getPageSize(), page.getPageSize());
}
return sql;
} public Object plugin(Object target) {
return Plugin.wrap(target, this);
} public void setProperties(Properties p) {
String dialect = ""; // 数据库方言
dialect = p.getProperty("dialect");
if (StringUtils.isBlank(dialect)) {
try {
throw new PropertyException("dialect property is not found!");
} catch (PropertyException e) {
log.error(e);
}
} else {
try {
dialectObject = (Dialect) Class.forName(dialect)
.getDeclaredConstructor().newInstance();
} catch (Exception e) {
throw new RuntimeException(dialect + ", init fail!\n" + e);
}
}
pageSqlId = p.getProperty("pageSqlId");//根据id来区分是否需要分页
if (StringUtils.isBlank(pageSqlId)) {
try {
throw new PropertyException("pageSqlId property is not found!");
} catch (PropertyException e) {
log.error(e);
}
}
}
}

然后是实现了dialect的oracledialect 数据库方言让我们区分不同的数据库不同的sql语句

public class OracleDialect extends Dialect {

    public boolean supportsLimit() {
return true;
} public boolean supportsLimitOffset() {
return true;
} public String getLimitString(String sql, int offset, String offsetPlaceholder, int limit, String limitPlaceholder) {
sql = sql.trim();
boolean isForUpdate = false;
if (sql.toLowerCase().endsWith(" for update")) {
sql = sql.substring(0, sql.length() - 11);
isForUpdate = true;
} StringBuffer pagingSelect = new StringBuffer(sql.length() + 100);
if (offset > 0) {
pagingSelect.append("select * from ( select row_.*, rownum rownum_ from ( ");
} else {
pagingSelect.append("select * from ( ");
}
pagingSelect.append(sql);
if (offset > 0) {
// int end = offset+limit;
String endString = offsetPlaceholder + "+" + limitPlaceholder;
pagingSelect.append(" ) row_ ) where rownum_ <= " + endString + " and rownum_ > " + offsetPlaceholder);
} else {
pagingSelect.append(" ) where rownum <= " + limitPlaceholder);
} if (isForUpdate) {
pagingSelect.append(" for update");
} return pagingSelect.toString();
} }

配置mybatis.xml

我们需要在sqlSessionFactory中注册我们的插件,发挥效果
 <bean id="pagePlugin" class="com.xxxxx.pulgin.mybatis.plugin.PagePlugin">
<property name="properties">
<props>
<prop key="dialect">com.dragon.dao.pulgin.jdbc.dialet.OracleDialect</prop>
<prop key="pageSqlId">.*query.*</prop>
</props>
</property>
</bean>

 <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="configLocation" value="classpath:spring/mybatis.xml" />
<property name="plugins">
<array>
<ref bean="pagePlugin" /> </array>
</property>
<property name="mapperLocations">
<list>
<!-- 自动匹配Mapper映射文件 -->
<value>classpath:com/xxxxx/mapper/*-mapper.xml</value>
</list>
</property>
</bean>

至此,我们的分页效果就做出来了。

charles at P.P 2016-7-14

如何做系列(1)- mybatis 如何实现分页?的更多相关文章

  1. Mybatis Generator实现分页功能

    Mybatis Generator实现分页功能 分类: IBATIS2013-07-17 17:03 882人阅读 评论(1) 收藏 举报 mybatisibatisgeneratorpage分页 众 ...

  2. Mybatis第三方PageHelper分页插件原理

    ​ 欢迎关注公号:BiggerBoy,看更多文章 原文链接:https://mp.weixin.qq.com/s?__biz=MzUxNTQyOTIxNA==&mid=2247485158&a ...

  3. Mybatis中的分页

    Mybatis中有哪些分页方式? 数组分页:查询出全部数据,然后再list中截取需要的部分.(逻辑分页) 优点:效率高     缺点:占用内存比较高 sql分页:只从数据库中查询当前页的数据.(物理分 ...

  4. SpringBoot+Mybatis配置Pagehelper分页插件实现自动分页

    SpringBoot+Mybatis配置Pagehelper分页插件实现自动分页 **SpringBoot+Mybatis使用Pagehelper分页插件自动分页,非常好用,不用在自己去计算和组装了. ...

  5. SpringBoot+Mybatis+PageHelper实现分页

    SpringBoot+Mybatis+PageHelper实现分页 mybatis自己没有分页功能,我们可以通过PageHelper工具来实现分页,非常简单方便 第一步:添加依赖 <depend ...

  6. SpringBoot集成Mybatis并具有分页功能PageHelper

    SpringBoot集成Mybatis并具有分页功能PageHelper   环境:IDEA编译工具   第一步:生成测试的数据库表和数据   SET FOREIGN_KEY_CHECKS=0;   ...

  7. SpringBoot系列-整合Mybatis(注解方式)

    目录 一.常用注解说明 二.实战 三.测试 四.注意事项 上一篇文章<SpringBoot系列-整合Mybatis(XML配置方式)>介绍了XML配置方式整合的过程,本文介绍下Spring ...

  8. Mybatis的PageHelper分页插件的PageInfo的属性参数,成员变量的解释,以及页面模板

    作者:个人微信公众号:程序猿的月光宝盒 //当前页 private int pageNum; //每页的数量 private int pageSize; //当前页的数量 private int si ...

  9. 小白的springboot之路(十五)、mybatis的PageHelper分页插件使用

    0.前言 用mybatis,那么分页必不可少,基本都是用PageHelper这个分页插件,好用方便: 1.实现 1.1.添加依赖: <!-- 3.集成 mybatis pagehelper--& ...

随机推荐

  1. delphi 流程单打印

    1.添加声明 f_count1: double; 2.得到拆分页数量 // Modified by 884 2018-04-20 14:50:18 AM0057 with aqTpCount do b ...

  2. CSIC_716_20191107【深拷贝、文件的编码解码、文件的打开模式】

    深拷贝和浅拷贝 列表的拷贝,用copy方法浅拷贝,新列表和被拷贝列表的id是不一样的. list1 = [1, 'ss', (5, 6), ['p', 'w','M'], {'key1': 'valu ...

  3. C/C++实现单向循环链表(尾指针,带头尾节点)

    C语言实现单向循环链表,主要功能为空链表创建,链表初始化(头插法,尾插法),链表元素读取,按位置插入,(有序链表)按值插入,按位置删除,按值删除,清空链表,销毁链表. 单向循环链表和单向链表的区别:( ...

  4. mysql索引原理深度解析

    mysql索引原理深度解析 一.总结 一句话总结: mysql索引是b+树,因为b+树在范围查找.节点查找等方面优化 hash索引,完全平衡二叉树,b树等 1.数据库中最常见的慢查询优化方式是什么? ...

  5. 怎样使用github?(转)

    怎样使用github?(转) 转自: 怎样使用 GitHub? - 知乎https://www.zhihu.com/question/20070065 珊姗是个小太阳 ❤努力做最胖//bang的健身博 ...

  6. maven项目依赖其他jar包的时候,idea运行没问题,java -jar 报错:java.lang.SecurityException: Invalid signature file digest

    当项目依赖其他jar包的时候,打出的jar包执行出错,抛出这个异常. 原因:因为依赖jar包中的META-INF中有多余的.SF文件与当前jar包冲突, 解决方案 一 在打包前删除依赖jar包的.SF ...

  7. Vue入坑——vue-cli(脚手架)目录结构认识

    转载:https://my.oschina.net/u/3802541/blog/1809182 一.目录结构 |-- build                            // 项目构建 ...

  8. Android Support 包的作用、用法

    1, Android Support V4, V7, V13是什么?本质上就是三个java library. 2,  为什么要有support库?如果在低版本Android平台上开发一个应用程序,而应 ...

  9. The Python Standard Library

    The Python Standard Library¶ While The Python Language Reference describes the exact syntax and sema ...

  10. F - GCD - Extreme (II) UVA - 11426

    Given the value of N, you will have to find the value of G. The definition of G is given below: