• 【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的方式来完成分页,在拼接时,要考虑是否不易出错,程序健壮性如何等问题。

  • 相关阅读:
    bzoj 1069 凸包+旋转卡壳
    bzoj 3203 凸包+三分
    bzoj 3779 重组病毒 好题 LCT+dfn序+线段树分类讨论
    bzoj 3881 [Coci2015]Divljak fail树+树链的并
    bzoj 4034 [HAOI2015]树上操作 入栈出栈序+线段树 / 树剖 维护到根距离和
    bzoj 2819 Nim dfn序+树状数组维护区间异或值
    bzoj 4031 [HEOI2015]小Z的房间 Matrix-tree定理
    BZOJ3676: [Apio2014]回文串
    BZOJ2434: [Noi2011]阿狸的打字机
    BZOJ2553: [BeiJing2011]禁忌
  • 原文地址:https://www.cnblogs.com/saixing/p/6730248.html
Copyright © 2020-2023  润新知