【SSH】——封装参数不确定的分页查询

【前言】

        在BS中,分页技术的应用相当频繁。说到分页,简单的分页就很好实现了,如果在分页的基础上再加上业务逻辑,这就使得分页的技术更加的灵活了。



【简单分页】


我们先看一种简单的分页,为了做到复用,我们抽出分页公用的东西,即分页实体PageModel。


/**
 * 封装分页信息
 * @author Administrator
 *
 */
public class PageModel<E> {

	//结果集
	private List<E> list;
	
	//查询记录数
	private int totalRecords;
	
	//每页多少条数据
	private int pageSize;
	
	//第几页
	private int pageNo;
	
	/**
	 * 总页数
	 * @return
	 */
	public int getTotalPages() {
		return (totalRecords + pageSize - 1) / pageSize;
	}
	
	/**
	 * 取得首页
	 * @return
	 */
	public int getTopPageNo() {
		return 1;
	}
	
	/**
	 * 上一页
	 * @return
	 */
	public int getPreviousPageNo() {
		if (pageNo <= 1) {
			return 1;
		}
		return pageNo - 1;
	}
	
	/**
	 * 下一页
	 * @return
	 */
	public int getNextPageNo() {
		if (pageNo >= getBottomPageNo()) {
			return getBottomPageNo();
		}
		return pageNo + 1;	
	}
	
	/**
	 * 取得尾页
	 * @return
	 */
	public int getBottomPageNo() {
		return getTotalPages();
	}
	
	...
	//get,set方法省略

}


在使用的时候,以查询用户集合为例

	/**
	 * 分页查询
	 * @param pageNo 第几页
	 * @param pageSize 每页多少条数据
	 * @return pageModel
	 */
	public PageModel<User> findUserList(int pageNo, int pageSize) {
		StringBuffer sbSql = new StringBuffer();
		//查询的sql语句
		sbSql.append("select user_id, user_name, password, contact_tel, email, create_date ")
			.append("from ")
			.append("( ")
			.append("select rownum rn, user_id, user_name, password, contact_tel, email, create_date ")
			.append("from ")
			.append("( ")
			.append("select user_id, user_name, password, contact_tel, email, create_date from t_user where user_id <> 'root' order by user_id ")
			.append(")  where rownum <= ? ")
			.append(")  where rn > ? ");
		Connection conn = null;
		PreparedStatement pstmt = null;
		//结果集
		ResultSet rs = null;
		//定义用户分页实体
		PageModel<User> pageModel = null;
		try {
			conn = DbUtil.getConnection();
			pstmt = conn.prepareStatement(sbSql.toString());
			//传入参数
			pstmt.setInt(1, pageNo * pageSize);
			pstmt.setInt(2, (pageNo - 1) * pageSize);
			rs = pstmt.executeQuery();
			List<User> userList = new ArrayList<User>();
			//遍历,赋值
			while (rs.next()) {
				User user = new User();
				user.setUserId(rs.getString("user_id"));
				user.setUserName(rs.getString("user_name"));
				user.setPassword(rs.getString("password"));
				user.setContactTel(rs.getString("contact_tel"));
				user.setEmail(rs.getString("email"));
				user.setCreateDate(rs.getTimestamp("create_date"));
				//添加到用户集合中
				userList.add(user);
			}
			//构建用户分页实体,用于前台页面显示。
			pageModel = new PageModel<User>();
			pageModel.setList(userList);
			pageModel.setTotalRecords(getTotalRecords(conn)); //总记录数
			pageModel.setPageSize(pageSize);
			pageModel.setPageNo(pageNo);
		}catch(SQLException e) {
			e.printStackTrace();
		}finally {
			DbUtil.close(rs);
			DbUtil.close(pstmt);
			DbUtil.close(conn);
		}
		return pageModel;
	}

    

       这里,我们只将pageNo(页号),pageSize(每页的条数)以及User(要分页的实体)作为参数传入就可以了。其他的属性,像TotalRecords(总记录数)等都可以计算得到。在sql语句中,我们限定了每次查询的数量,在前台显示的时候,直接将pageModel传到页面,然后获取其属性。


       在第一种方法中,我们的需求比较简单:查询用户名不是root的所有用户,默认排序方式是按用户id排序。但当排序方式、排序的范围、每页显示条数等都不确定的情况下,这种分页方式就比较粗糙了。


【难度提升】


1、排序范围:分为两种——查询全部,查询部分。

2、排序条件:1:按最后更新时间排序,2:按主题发表时间排序,3:按回复数量排序

3、排序类型:升序,降序。




4、选择页码,展示相应的数据。



【分析】


1、pageModel 分页实体

     我们仍将抽取分页实体PageBean,与第一种方法不同的是,为了满足单击页码查询的需求,新增了两个属性beginPageIndex和endPageIndex。


/**
 * 分页中一页的信息
 * 
 * @author YANG
 * 
 */
public class PageBean {
	// 指定的或是页面参数
	private int currentPage;
	private int pageSize;

	// 查询数据库
	private List recordList;


	private int recordCount;

	// 需要计算
	private int pageCount;
	//开始
	private int beginPageIndex;
	//结束
	private int endPageIndex;
	
	//省去get,set
}


2、sql语句

因为查询条件数量和内容都不确定,因而sql语句绝对不能写死。


3、关于封装

1)sql语句可以进行封装。


如何解决:——抽象出Query类,将其where子句、orderby子句等进行封装。

/**
 * 辅助拼接hql
 * 
 * @author YANG
 * 
 */
public class QueryHelper {
	private String fromClause;
	private String whereClause = "";
	private String orderByClause = "";

	private List<Object> params = new ArrayList<Object>();

	/**
	 * 生成from
	 * 
	 * @param clazz
	 * @param alias
	 *            别名
	 */
	public QueryHelper(Class clazz, String alias) {
		fromClause = " FROM " + clazz.getSimpleName() + " " + alias;
	}

	/**
	 * 拼接where子句
	 * 
	 * @param condition
	 * @param param
	 */
	public QueryHelper addCondition(String condition, Object... param) {

		if ("".equals(whereClause)) {
			whereClause = " WHERE " + condition;
		} else {
			whereClause += " AND " + condition;
		}
		// 参数
		if (param != null) {
			for (Object p : param) {
				params.add(p);
			}
		}
		return this;
	}

	/**
	 * 第一个参数为true,拼接where子句
	 * 
	 * @param append
	 * @param condition
	 * @param param
	 */
	public QueryHelper addCondition(boolean append, String condition, Object... param) {

		if (append) {
			addCondition(condition, param);
		}
		return this;
	}

	/**
	 * orderBy子句
	 * 
	 * @param propertyName
	 * @param asc
	 *            true 升序
	 */
	public QueryHelper addOrderByProperty(String propertyName, boolean asc) {
		if ("".equals(orderByClause)) {
			orderByClause = " ORDER BY " + propertyName + (asc ? "ASC" : "DESC");
		} else {
			orderByClause += ", " + propertyName + (asc ? "ASC" : "DESC");
		}
		return this;
	}

	/**
	 * 第一个参数为true,拼接orderby子句
	 * 
	 * @param append
	 * @param propertyName
	 * @param asc
	 */
	public QueryHelper addOrderByProperty(boolean append, String propertyName,
			boolean asc) {
		if (append) {
			addOrderByProperty(propertyName, asc);
		}
		return this;
	}

	/**
	 * 获取生成的用于查询数据列表的hql语句
	 * 
	 * @return
	 */
	public String getListQueryHql() {
		return fromClause + whereClause + orderByClause;
	}
}





2)Page分页实体中的其他属性值

      page中的参数可分为两类,一类是传入或给定的值,如pageSize、currentPage等;另一类是需要自动计算的属性,如beginPageIndex等。如果对需要回显页面的属性进行封装,既可避免向前台准备不必要的数据,也可防止遗漏。


如何解决:——在pageBean中利用构造函数完成


public PageBean(int currentPage, int pageSize, List recordList,
			int recordCount) {
		super();
		this.currentPage = currentPage;
		this.pageSize = pageSize;
		this.recordList = recordList;
		this.recordCount = recordCount;

		pageCount = (recordCount + pageSize - 1) / pageSize;
		// 计算beginPageIndex endPageIndex
		if (pageCount > 5) {
			// 总页数>5,显示分页
			// 当前页附近共5个 前两个+后两个+本页
			beginPageIndex=currentPage-2;
			endPageIndex=currentPage+2;
			// 前面页码不足2显示前5个
			if(beginPageIndex<1){
				beginPageIndex=1;
				endPageIndex=5;
			}
			// 后面页码不足2,显示后5个
			//TODO:bulijie
			if(endPageIndex>pageCount){
				beginPageIndex=pageCount-10+1;
				endPageIndex=pageCount;
			}
		} else {
			// 总页数<5
			beginPageIndex = 1;
			endPageIndex = pageCount;
		}

	}


……


具体业务中如何使用:

      1)确定排序条件,即给QueryHelper传参。

 new QueryHelper(Reply.class, "r")
		// 查询范围
		.addCondition("r.topic=?", topic)
		//排序条件,按发表时间postTime
		.addOrderByProperty( "r.postTime ", true)
		.preparePageBean(replyService, pageNum, pageSize);


2)实现类中具体实现方法

public PageBean getPageBeanByParam(int pageNum, int pageSize, QueryHelper queryHelper) {
		System.out.println("DaoSupportImpl.getPageBeanByParam()");
		
		//获取参数列表,即查询条件
		List<Object> params=queryHelper.getParams();
		
		// 查询列表
		Query query = getSession().createQuery(queryHelper.getListQueryHql());
		//设定参数
		if(params!=null){
			for (int i = 0; i < params.size(); i++) {
				query.setParameter(i,params.get(i));
			}
		}
		query.setFirstResult((pageNum-1)*pageSize);
		query.setMaxResults(pageSize);
			List list=query.list();
		// 总数量
			Query countQuery=getSession()
			.createQuery(queryHelper.getCountQueryHql());
			if(params!=null){
				for (int i = 0; i < params.size(); i++) {
					countQuery.setParameter(i,params.get(i));
				}
			}
			
		Long count = (Long)countQuery.uniqueResult();
		//pageBean的构造函数方法
		return new PageBean(pageNum, pageSize, list, count.intValue());
	}



 3)回显数据,最后页面的显示部分就很简单了,使用OGNL表达式或者EL表达式都可完成显示。

/**
	 * 查询分页信息,并放到栈顶
	 * @param service
	 * @param pageNum
	 * @param pageSize
	 */
	public void preparePageBean(DaoSupport<?> service,int pageNum,int pageSize){
		PageBean pageBean = service.getPageBeanByParam(pageNum, pageSize,
				this);
		ActionContext.getContext().getValueStack().push(pageBean);
	}


 【小结】

        首先,本文中的分页属于真分页。真分页的原则是显示什么查什么,而假分页是将所有的数据全部查出来,再前台控制数据的显示,这样性能会很差。在小编看来一般项目中,应用真分页的情况比较多一些。

        在项目中,像分页、连接数据库、sql的增删改查等操作都可以考虑抽出一个工具类来做,其他业务上需要使用,直接拿来用就可以。以分页为例,在业务需求增多,查询难度加大的情况下,可采用拼接sql的方式来完成分页,在拼接时,要考虑是否不易出错,程序健壮性如何等问题。

原文地址:https://www.cnblogs.com/saixing/p/6730248.html