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

  • 相关阅读:
    HDU 2544 最短路
    HDU 3367 Pseudoforest
    USACO 2001 OPEN
    HDU 3371 Connect the Cities
    HDU 1301 Jungle Roads
    HDU 1879 继续畅通工程
    HDU 1233 还是畅通工程
    HDU 1162 Eddy's picture
    HDU 5745 La Vie en rose
    HDU 5744 Keep On Movin
  • 原文地址:https://www.cnblogs.com/saixing/p/6730248.html
Copyright © 2011-2022 走看看