分页查询,就是将将过多的结果在有限的界面上分好多页来显示,这个是很多网站常用的功能,也是最基本的功能,今天简单总结一下。
分页以前听人们说都是一项技术,但是我觉的不尽然。我认为分页是将数据库的数据,利用一些特殊的sql语句来进行查询,显示理所应当显示的内容,更恰当的说可以是对SQL语句的灵活运用,对逻辑思维的简单使用。
一,一般人们将分页查询分为两类:逻辑分页,物理分页,我们先从理论上理解一下:
1,逻辑分页概述:就是用户第一次访问时,将数据库的所有记录全部查询出来,添加到一个大的集合中,然后存放在session对象,然后通过页码计算出当前页需要显示的数据内容,存储到一个小的list的集合中,并将之存储到request对象中,跳转到JSP页面,进行遍历显示。 当用户第二次访问时,只要不关闭浏览器,我们还会从session中获取数据,来进行显示。为什么叫逻辑分页呢?因为此种方法是在内存的session对象中进行计算分页显示的,而不是真正的将我们数据库进行分页的。
来看它的一些缺点吧:
a,如果需要查询的数据量过大,session将耗费大量的内存;
b,因为是在session中获取数据,如果第二次或者更多此的不关闭浏览器访问,会直接访问session,从而不能保证数据是最新的。
小结:这种分页很少使用。但是在数据量小,不会被修改的数据,使用逻辑分页会提高程序的执行效率。
2,物理分页概述:使用数据库自身所带的分页机制,例如,Oracle数据库的rownum,或者Mysql数据库中的limit等机制来完成分页操作。因为是对数据库实实在在的数据进行分页条件查询,所以叫物理分页。每一次物理分页都会去连接数据库。
优点:数据能够保证最新,由于根据分页条件会查询出少量的数据,所以不会占用太多的内存。
缺点:物理分页使用了数据库自身带的机制,所以这样的SQL语句不通用,导致不能进行数据库的移植。
小结:在实际中物理分页还是使用的较多的。
二,看一下逻辑分页查询的应用:
- <span style="font-size:18px;"> public class PageQueryUserServlet extends HttpServlet {
- @Override
- protected void doGet(HttpServletRequest request, HttpServletResponse response)
- throws ServletException, IOException {
- //获取页码
- int pageno = Integer.parseInt(request.getParameter("pageno")==null?"1":request.getParameter("pageno"));
- //从session中获取大List集合
- HttpSession session = request.getSession();
- List<User> bigList = (List<User>)session.getAttribute("bigList");
- //如果第一次访问
- if(bigList == null){
- //创建大List集合
- bigList = new ArrayList<User>();
- //如果大List集合不存在,则连接数据库
- Connection conn = null;
- PreparedStatement ps= null;
- ResultSet rs = null;
- try {
- conn = DBUtil.getConnection();
- String sql = "select usercode,username,orgtype from t_user order by regdate desc";
- ps = conn.prepareStatement(sql);
- //执行查询语句返回查询结果集
- rs = ps.executeQuery();
- //遍历结果集封装javabean对象并存储到大List集合中
- while(rs.next()){
- User user = new User();
- user.setUsercode(rs.getString("usercode"));
- user.setUsername(rs.getString("username"));
- user.setOrgtype(rs.getString("orgtype"));
- bigList.add(user);
- }
- //将大List集合存储到session中
- session.setAttribute("bigList", bigList);
- } catch (Exception e) {
- e.printStackTrace();
- } finally{
- DBUtil.close(conn, ps, rs);
- }
- }
- //如果从session中可以获取到大List集合,则通过页码计算得出小List集合
- List<User> smallList = new ArrayList<User>();
- //计算开始标识=页数大小*(页码-1)
- int beginIndex = Const.PAGE_SIZE * (pageno-1);
- //结束标识=页数大小*页码,如果超过了总数据条数,则表示为最后一页,写为总结条数即可
- int endIndex = Const.PAGE_SIZE * pageno > bigList.size() ? bigList.size() : Const.PAGE_SIZE * pageno;
- for(int i=beginIndex;i<endIndex;i++){
- smallList.add(bigList.get(i));
- }
- //将小List集合存储到request对象中
- request.setAttribute("userList", smallList);
- //转发
- }
- }</span>
三,好,物理分页和逻辑分页的计算方法差不多,只不过一个是session中一个是在数据库中,这里物理分页总结一下多条件查询分页显示的过程,这里也将分页对象进行封装了:
先看一下分页对象的编写:
- <span style="font-size:18px;"> /**
- * 分页对象
- * @author Administrator
- */
- public class Page<T> {
- /**
- * 页码
- */
- private int pageno;
- /**
- * 每页显示的记录条数
- */
- private int pagesize;
- /**
- * 数据集合(需要显示在网页中的数据)
- */
- private List<T> dataList;
- /**
- * 总记录条数
- */
- private int totalsize;
- public Page(String pageno) {
- this.pageno = (pageno == null ? 1 : Integer.parseInt(pageno));
- this.pagesize = Const.PAGE_SIZE;
- this.dataList = new ArrayList<T>();
- }
- public int getPageno(){
- return pageno;
- }
- public int getPagesize(){
- return pagesize;
- }
- public List<T> getDataList(){
- return dataList;
- }
- public void setTotalsize(int totalsize){
- this.totalsize = totalsize;
- }
- public int getTotalsize(){
- return totalsize;
- }
- public int getPagecount(){
- return totalsize%pagesize == 0 ? totalsize/pagesize : totalsize/pagesize + 1;
- }
- /**
- * 通过业务SQL语句获取分页SQL语句
- * @param sql 业务SQL
- * @return 分页SQL语句
- * 这是非常核心的,通过多次嵌套,嵌套出分页sql语句的编写
- */
- public String getSql(String sql){
- return "select t1.* from (select t.*,rownum as linenum from ("+sql+") t where rownum<=" + pageno*pagesize + ") t1 where t1.linenum>" + (pageno-1)*pagesize;
- }
- }
- </span>
有了这个分页对象,我就可以利用它了,看我们动态参数分页查询的过程,重点看注释步骤:
- <span style="font-size:18px;"> /**
- * 动态参数查询,难度最大的是SQL语句动态拼接。(因为查询提交内容不定,查询提交个数不定)
- * @author Administrator
- */
- public class PageQueryInvServlet extends HttpServlet {
- @Override
- protected void doPost(HttpServletRequest request, HttpServletResponse response)
- throws ServletException, IOException {
- //解决请求体的中文乱码问题
- //request.setCharacterEncoding("GB18030");
- //创建分页对象
- Page<Investor> page = new Page<Investor>(request.getParameter("pageno"));
- //获取查询提交的数据
- String invregnum = request.getParameter("invregnum");
- String invname = request.getParameter("invname");
- String startdate = request.getParameter("startdate");
- String enddate = request.getParameter("enddate");
- //拼接业务SQL,注意其中的技巧,where 1=1,另外这里使用StringBuilder提高拼接的效率
- StringBuilder sql = new StringBuilder("select i.invregnum,i.invname,i.regdate,u.username,i.cty from t_invest i join t_user u on i.usercode=u.usercode where 1=1");
- StringBuilder totalsizeSql = new StringBuilder("select count(*) as totalsize from t_invest i join t_user u on i.usercode=u.usercode where 1=1");
- //创建list集合用来绑定下标和内容,利用的list下标和值对应关系的特点
- List<String> paramList = new ArrayList<String>();
- //动态参数拼接动态SQL语句
- if(StringUtil.isNotEmpty(invregnum)){
- sql.append(" and i.invregnum = ?");
- totalsizeSql.append(" and i.invregnum = ?");
- paramList.add(invregnum);
- }
- if(StringUtil.isNotEmpty(invname)){
- sql.append(" and i.invname like ?");
- totalsizeSql.append(" and i.invname like ?");
- paramList.add("%" + invname + "%");
- }
- if(StringUtil.isNotEmpty(startdate)){
- sql.append(" and i.regdate >= ?");
- totalsizeSql.append(" and i.regdate >= ?");
- paramList.add(startdate);
- }
- if(StringUtil.isNotEmpty(enddate)){
- sql.append(" and i.regdate <= ?");
- totalsizeSql.append(" and i.regdate <= ?");
- paramList.add(enddate);
- }
- //调用获取分页SQL
- String pageSql = page.getSql(sql.toString());
- //连接数据库查询数据
- Connection conn = null;
- PreparedStatement ps = null;
- ResultSet rs = null;
- try {
- conn = DBUtil.getConnection();
- ps = conn.prepareStatement(pageSql);
- //给?赋值(重点),这里list的巧妙使用
- for(int i=0;i<paramList.size();i++){
- ps.setString(i+1, paramList.get(i));
- }
- //执行查询语句,返回查询结果集
- rs = ps.executeQuery();
- //遍历结果集,每遍历一次,封装Investor对象,将其添加到List集合中
- while(rs.next()){
- Investor inv = new Investor();
- inv.setInvregnum(rs.getString("invregnum"));
- inv.setInvname(rs.getString("invname"));
- inv.setRegdate(rs.getString("regdate"));
- inv.setUsername(rs.getString("username"));
- inv.setCty(rs.getString("cty"));
- page.getDataList().add(inv);
- }
- //查询总记录条数,并且设置到分页对象中
- ps = conn.prepareStatement(totalsizeSql.toString());
- //给?赋值
- for(int i=0;i<paramList.size();i++){
- ps.setString(i+1, paramList.get(i));
- }
- rs = ps.executeQuery();
- if(rs.next()){
- page.setTotalsize(rs.getInt("totalsize"));
- }
- } catch (Exception e) {
- e.printStackTrace();
- } finally{
- DBUtil.close(conn, ps, rs);
- }
- //将分页对象存储到request范围中
- request.setAttribute("pageObj", page);
- //转发
- }
- }
- </span>
分页查询将数据量分成几批显示到页面上。就像我们的书本,这不过这里的动态,可能因为查询条件的不同,页面的内容就不同,所以灵活应用,弄清楚查询条件,编写好分页查询语句,那么什么问题都解决了。