大话分页(一)

分页是一项人性化的功能,也是查看大量显示数据的一种解决方案。然而就分页功能的实现来说,分页是多种多样的。那我们在项目中用到的时候,我该使用哪种方式进行分页呢?下面我将汇总一下分页查询的各种实现,并加以比对,当你使用时,做出较好的选择(本文讨论范畴只在真分页,下面谈到的分页也特指真分页)。


分页共性


凡是分页,无论使用什么方式实现,它都是以从第几条数据到第几条数据这样的思路实现的,都需要提供两个参数:

PageNo:当前页号;

PaseSize:每页显示的数据。


三种数据的分页方式的比较


MySql数据库特色分页

最简单的方法就是利用mysql数据库的limit函数进行分页:

limit[offset],rows可以从Mysql数据库中第M条记录开始检索N条记录的语句为:

select * from 表名 limit M,N

从表Sys_option(主键为sys_id)中从第10条记录开始检索20条记录,语句如下:

select * from sys_option order by sys_id limit 10,20


Oracle数据库常用分页

Oracle中经常使用三层嵌套查询:

select * from (select T.*,rownum rn from (select * from table order by id) T where rownum <PageNo*PageSize) where rn >=(PageNo-1)*PageSize

PageNo为当前页数;

PageSize为每页显示数据数目。

查询的是从第(PageNo-1)*PageSize条数据到第PageNo*PageSize条数据。


例子:从t_student表中取出第20到40条数据

select * from (select A.*,rownum rn from (select * from t_student order by id) A where rownum <41) where rn >=20


问为什么Oracle要使用三层嵌套查询实现分页呢?

其实这跟Oracle数据库的特性有关,rownum的赋值默认从1开始。如果不适用嵌套查询,我们会写出下面的语句:

select rownum,id from (select * from t_student order by id) where rownum >=m and rownum < n; 

当执行 rownum>m 且 m>1 时,由于rownum默认为1,这也就是一个不成立的条件,所以会查不出数据,所以采用三层嵌套语句,将rownum变成一个临时表的字段,这时候就没有oracle数据特性的限制了。


SqlServer数据常用分页

查询第一页的五条数据比较好查询,那么怎样查询第二页的数据呢(也就是第6到第10条记录)?

第二页的记录就是紧跟这第一页显示的记录之后的5条记录,也就是通过对userID字段进行降序排列时,它们是除了第一页数据之后的5条记录,也就是它们的userID不在第一页的UserID之中,在SQL语句有一个not in这个正好可以排上用场。 首先我们按照对UserID进行降序排序,查询出前面第一页使用的数据的UserID,SQL语句及执行结果如下:

select top 5 * from t_User where userID not in (select 5 userID from t_User order by userID asc) order by userID asc


userID是从1开始,所以userID在1至5的记录在第一页显示,userID为6至10的记录在第二页显示,userID为11至15的记录在第三页显示……依此类推,如果每页显示5条记录,那么第n页显示的数据记录的公式应该是:

select top 5 * from t_User where userID not in (select top(n-1)*5 userID from t_User order by userID asc) order by userID asc


Hibernate框架的分页

根据上面我们可以看出:每种数据都有自己特色的内容可以完成分页功能。Hibernate框架再此基础上进行封装,只需要Query接口中setMaxResultsset和FirstResult方法即可完成分页。采用Hibernate的好处就是:如果你使用的mysql数据库,那么Hibernate就会按照Mysql的规则生成相应的分页语句;如果使用Oracle数据库,那么它也会相应的生成对应的Oracle分页语句。也就是说,Hibernate框架实现分页,与具体数据库是无关的,有利于更换数据库。

下面看一个Hibernate实现分页的工具类的主要方法:

public class AbstractPageManager extends HibernateDaoSupport {

	/**
	 * 根据HQL语句,获得查找总记录数的HQL语句 如: 
	 * select ... from Organization o where o.parent is null 
	 * 经过转换,可以得到: 
	 * select count(*) from Organization o where o.parent is null
	 * 
	 * @param hql
	 * @return
	 */
	private String getCountQuery(String hql) {
		// 取得from的位置
		int index = hql.indexOf("from");

		// 返回:查询记录条数 的SQL语句
		if (index != -1) {
			return "select count(*) " + hql.substring(index);
		}
		throw new SystemException("无效的HQL查询语句");
	}
	
	/**
	 * 根据HQL语句进行分页查询
	 * 
	 * @param hql HQL语句
	 * @param params HQL语句带的多个参数
	 * @param offSet 从第几个记录开始查询
	 * @param pageSize 每页显示多少行
	 * @return
	 */
	public PageModel searchPaginate(String hql, Object[] params, int offSet,
			int pageSize) {
		// 记录条数
		String strCount = getCountQuery(hql);

		// 查询条数
		Query query = getSession().createQuery(strCount);
		// 将HQL语句带的多个参数 赋值给Query
		if (params != null && params.length > 0) {
			for (int i = 0; i < params.length; i++) {
				query.setParameter(i, params[i]);
			}
		}

		// 获取查询条数
		int intCount = ((Long) query.uniqueResult()).intValue();

		// 查询Organization记录
		query = getSession().createQuery(hql);
		// 将HQL语句带的多个参数 赋值给Query
		if (params != null && params.length > 0) {
			for (int i = 0; i < params.length; i++) {
				query.setParameter(i, params[i]);
			}
		}
		/*
		 * offSet 设置从第几个记录开始查询
		 * pageSize 设置每页显示多少行
		 */
		query.setFirstResult(offSet);
		query.setMaxResults(pageSize);

		//组装PageModel
		PageModel pageModel = new PageModel();
		pageModel.setDatas(query.list());
		pageModel.setTotal(intCount);
		
		return pageModel;
	}
}


由上可知:MySql、Oracle和SqlServer三种数据库有通性的地方,同时也有自己特色的内容。如果使用数据库特色的语句,是不利于数据库的移植的。Hibernate框架在此基础上进行封装,只需要通过setMaxResultsset()和FirstResult()方法设置offSet和pageSize,就能根据数据库生成特色语句,具体实现有兴趣的同学可以自行研究Hibernate源码,我这里就不往上贴了。

未完待续。。


原文地址:https://www.cnblogs.com/pangblog/p/3339414.html