通用分页的详细讲解看这一篇就够了(内含源码)(上)

简介: 通用分页的详细讲解看这一篇就够了(内含源码)

一、为什么要使用通用分页以及使用场景

1.为什么使用通用分页

            在软件或者网站上,数据量是庞大的,不可能一下把全部的数据给你展示出来,因为渲染需要时间而且很耗内存,如果使用分页,每次就会处理一小块的数据,大大的提升了我们的性能以及用户体验度。但是在开发中不单单只是一个模块需要分页,有很多地方都需要分页,这时候我们就可以将重复的代码进行优化,变成——通用分页供开发人员使用。

2.使用场景

1.大型程序的执行:

大型程序需要占用大量的内存空间,但是内存空间是有限的,使用分页技术可以将程序的数据、代码、堆和栈等分为多个页面,只有需要时才将页面加载到内存中。

2.并发运行多个程序:

操作系统可以同时运行多个程序,分页技术可以让操作系统将内存中的每个程序区分开,保护每个程序的独立性和安全性。

3.身份验证和访问权限管理:

身份验证和访问权限管理需要划分内存空间,每个用户所占用的内存空间应该是独立的,这样才能有效地实现权限管理。

4.网络服务器:

作为网络服务器,需要同时处理多个客户端的请求,而每个客户端所占用的内存空间也是独立的,使用分页技术可以让操作系统为每个客户端分配独立的内存空间,保护服务器的稳定性和安全性。

5.计算机游戏:

计算机游戏通常具有图形和音效等较复杂的特性,需要占用大量的内存空间。使用分页技术可以提高游戏的运行速度和稳定性。

3.以往分页的实现及代码

往常都是这么写分页方法的,先封装一个分页工具类保存页码、一页的条目、总记录数以及是否分页,然后当成参数传递到Dao层的分页方法中,再将需要查询的关键词或是类别传递即可,下面看一下代码!!

分页工具类

package com.xw.util;
/**
 * 分页工具类
 *
 */
public class PageBean {
  private int page = 1;// 页码
  private int rows = 10;// 页大小
  private int total = 0;// 总记录数
  private boolean pagination = true;// 是否分页
  public PageBean() {
    super();
  }
  public int getPage() {
    return page;
  }
  public void setPage(int page) {
    this.page = page;
  }
  public int getRows() {
    return rows;
  }
  public void setRows(int rows) {
    this.rows = rows;
  }
  public int getTotal() {
    return total;
  }
  public void setTotal(int total) {
    this.total = total;
  }
  public void setTotal(String total) {
    this.total = Integer.parseInt(total);
  }
  public boolean isPagination() {
    return pagination;
  }
  public void setPagination(boolean pagination) {
    this.pagination = pagination;
  }
  /**
   * 获得起始记录的下标
   * 
   * @return
   */
  public int getStartIndex() {
    return (this.page - 1) * this.rows;
  }
  @Override
  public String toString() {
    return "PageBean [page=" + page + ", rows=" + rows + ", total=" + total + ", pagination=" + pagination + "]";
  }
}

Dao层分页方法

/**以往分页
   * @param b 关键词
   * @param p 分页工具类
   * @return 根据条件查询出来的数据
   */
  public List<Book> list(Book b, PageBean p) {
    // 获取连接对象
    Connection conn = null;// 连接对象
    PreparedStatement ps = null;// 执行对象
    ResultSet rs = null;// 结果集对象
    List<Book> list = new ArrayList<Book>();// 集合实体
    Book book = null;// 实体对象
    try {
      // 加载驱动
      conn = DBAccess.getConnection();
      // 编写sql语句
      String sql = "SELECT * FROM t_mvc_book where 1=1 ";
      // 当关键词不为空的时候,拼接模糊查询的sql语句
      String bname = b.getBname();
      if (StringUtils.isNotBlank(bname)) {
        sql += "  and bname like '%" + bname + "%'";
        //判断是否分页
        if(p!=null && p.isPagination()) {
          sql+="  LIMIT "+p.getStartIndex()+","+p.getRows();
        }
      }
      // 执行sql语句
      ps = conn.prepareStatement(sql);
      // 返回结果集
      rs = ps.executeQuery();
      // 遍历结果集
      while (rs.next()) {
        // 将结果集加入实体
        book = new Book(rs.getInt(1), rs.getString(2), rs.getFloat(3));
        // 将实体加入list集合
        list.add(book);
      }
    } catch (Exception e) {
      // 捕捉异常
      e.printStackTrace();
    } finally {
      // 关闭资源
      DBAccess.close(conn);
      DBAccess.close(rs);
      DBAccess.close(ps);
    }
    // 返回结果
    return list;
  }

打印结果

虽然我们的功能实现了,但是我们要对另一个模块的时候很多重复的代码还需要在写一次,是不是非常的麻烦,这时候我们的通用分页就上场了!!!

二、通用分页的讲解

0.前言

博主这里用的数据库是mysql,如果还不会mysql安装的话可以点击mysql安装教程,下面我以Book实体为例,开展我的通用分页查询讲解

package com.xw.entity;
/**book实体类
 * @author Java方文山
 *
 */
public class Book {
  private int bid;
  private String bname;
  private float price;
  public Book() {
    // TODO Auto-generated constructor stub
  }
  public Book(int bid, String bname, float price) {
    super();
    this.bid = bid;
    this.bname = bname;
    this.price = price;
  }
  @Override
  public String toString() {
    return "Book [bid=" + bid + ", bname=" + bname + ", price=" + price + "]";
  }
  public int getBid() {
    return bid;
  }
  public void setBid(int bid) {
    this.bid = bid;
  }
  public String getBname() {
    return bname;
  }
  public void setBname(String bname) {
    this.bname = bname;
  }
  public float getPrice() {
    return price;
  }
  public void setPrice(float price) {
    this.price = price;
  }
}

1.实现通用分页步骤分析

看完以上的代码,你想到的是怎么优化呢?

我来给大家分享一下我的优化方式,首先所有分页代码都大同小异,只有sql语句和遍历结果集的区域代码不一致,所以我们只需要将这里稍加改动,就可以变成我们的通用分页。首先我们通用的basedao里面要判断是否分页,因为有些模块不需要分页,如果不需要分页的话就进行“原sql”查询即可,需要分页,就将模糊查询的总记录数计算出来以及回显最终展示的数据即可。其次就是遍历结果集的时候,因为我们不知道将来传递进来的是什么对象,所以可以用到反射的机制,动态的获取属性。

2.实现通用分页

①编写BaseDao(通用分页)

package com.xw.dao;
import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import com.xw.entity.Book;
import com.xw.util.DBAccess;
import com.xw.util.PageBean;
import com.xw.util.StringUtils;
/**封装通用分页
 * @author Java方文山
 *
 */
public class BaseDao<T> {
  /**封装通用的分页方法
   * @param cl 需要分页的实体
   * @param sql sql语句
   * @param p 封装的分页工具类
   * @return 按照条件查询的结果
   */
  public List<T> executeQuery(Class cl,String sql,PageBean p) throws Exception{
    //获取连接对象
    Connection conn=null;//连接对象
    PreparedStatement ps=null;//执行对象
    ResultSet rs=null;//结果集对象
    List<T> list=new ArrayList<T>();//集合
    //判断是否分页
    if(p!=null && p.isPagination()) {
      //总记录数
      String count=getSQLcount(sql);
      conn=DBAccess.getConnection();
      ps=conn.prepareStatement(count);
      rs=ps.executeQuery();
      //将总记录数赋值给分页工具类
      if(rs.next()) {
        p.setTotal(rs.getObject("n").toString());
      }
      //初始下标(第几条开始)
      String pagesize=getSQpagesize(sql,p);
      conn=DBAccess.getConnection();
      ps=conn.prepareStatement(pagesize);
      rs=ps.executeQuery();
    }else {
      //加载驱动
      conn=DBAccess.getConnection();
      //执行sql语句
      ps=conn.prepareStatement(sql);
      //返回结果集
      rs=ps.executeQuery();
    }
    try {
      //遍历结果集
      while(rs.next()) {
        //创建类的实例
        T T=(T) cl.newInstance();
        //拿到传递进来的实体所有属性
        Field[] declaredFields = cl.getDeclaredFields();
        //遍历属性
        for (Field field : declaredFields) {
          //打开访问权限
          field.setAccessible(true);
          //等价于b.setBid(rs.getInt("bid"),设置T实例的属性
          field.set(T, rs.getObject(field.getName()));
        }
        //将实体添加到集合
        list.add(T);
      }
    } catch (Exception e) {
      //捕捉异常
      e.printStackTrace();
    }finally {
      //关闭资源
      DBAccess.close(conn);
      DBAccess.close(rs);
      DBAccess.close(ps);
    }
    //返回结果
    return list;
  }
  /**计算最终展示数据的sql
   * @param sql 传递过来的sql语句
   * @param p 分页工具类
   * @return 
   */
  private String getSQpagesize(String sql, PageBean p) {
    return sql+"  LIMIT "+p.getStartIndex()+","+p.getRows();
  }
  /**查看总记录数
   * @param sql 传递过来的sql语句
   * @return 数量
   */
  private String getSQLcount(String sql) {
    return "SELECT COUNT(1) as n from("+sql+") t";
  }
}

②BookDao层继承BaseDao

package com.xw.dao;
import static org.junit.Assert.fail;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;
import org.junit.Test;
import com.xw.entity.Book;
import com.xw.util.DBAccess;
import com.xw.util.PageBean;
import com.xw.util.StringUtils;
/**
 * book的dao类
 * 
 * @author Java方文山
 *
 */
public class BookDao extends BaseDao<Book> {
  public List<Book> BaseDaoTest(Book b, PageBean p) throws Exception {
    // 编写sql语句
    String sql = "SELECT * FROM t_mvc_book where 1=1 ";
    // 当关键词不为空的时候,拼接模糊查询的sql语句
    String bname = b.getBname();
    if (StringUtils.isNotBlank(bname)) {
      sql += "  and bname like '%" + bname + "%'";
    }
    //调用basedao封装的方法
    return executeQuery(b.getClass(), sql, p);
  }
  @Test
  public void test2() throws Exception {
    System.out.println("通用分页");
    //调用BookDao
    BookDao bookDao = new BookDao();
    //模拟关键词
    Book b = new Book();
    b.setBname("圣墟");
    //调用分页工具类
    PageBean p = new PageBean();
    //记录查询记录数
    int count = 0;
        //允许分页
    p.setPagination(true);
    List<Book> list = bookDao.BaseDaoTest(b, p);
    for (Book book : list) {
      System.out.println(book);
      count++;
    }
    System.out.println("总记录数目: " + count);
  }
}

打印结果:

至此,我们的通用分页就写完了,我们将来只需要写sql语句和参数传递即可,是不是很方便!!

相关文章
|
前端开发 Java 测试技术
java通用分页(后端)
1.通用分页是什么? Java通用分页是指在Java编程语言中实现的一种通用分页功能。它通常用于在Java Web应用中展示大量数据或查询结果,并将其分页显示给用户。
279 1
|
10月前
|
Java Spring 容器
解决Spring的UnsatisfiedDependencyException异常的方法
在Spring开发中,UnsatisfiedDependencyException异常意味着依赖注入失败,影响应用稳定性。该异常由Spring容器在无法满足bean依赖时抛出,常见原因包括bean定义错误、循环依赖、多个候选bean等。解决方法包括:检查bean定义和注入的正确性、解决循环依赖、确认依赖包的兼容性、使用@Qualifier或@Primary注解。通过日志、调试工具和异常对比来定位问题。持续学习Spring框架有助于更好地解决此类异常。
5751 1
|
10月前
|
SQL 监控 关系型数据库
MySQL Metadata Locking(MDL)机制的实现与获取机制分析
MySQL Metadata Locking(MDL)机制的实现与获取机制分析 为了满足数据库在并发请求下的事务隔离性和一致性要求,同时针对MySQL插件式多种存储引擎都能发挥作用,MySQL在Server层实现了 Metadata Locking(MDL)机制。这种机制可以灵活自定义锁的对象、锁的类型以及不同锁类型的优先级,甚至可以做到在系统不同状态时动态调整不同锁类型的兼容性。本篇文章将详细介绍MDL系统中的常用数据结构及含义,从实现角度讨论MDL的获取机制与死锁检测,以及在实践中如何监控MDL状态。
360 2
|
8月前
|
关系型数据库 MySQL 数据库
|
10月前
|
测试技术 持续交付 开发工具
一文掌握:Gitlab的完整使用手册
一文掌握:Gitlab的完整使用手册
|
SQL druid Java
解决 ‘The last packet successfully received from the server was xxx milliseconds ago‘ 问题
解决 ‘The last packet successfully received from the server was xxx milliseconds ago‘ 问题
2293 0
|
JSON 负载均衡 Java
SpringCloud Feign 远程调用(史上最详细讲解)
SpringCloud Feign 远程调用(史上最详细讲解)
10081 0
SpringCloud Feign 远程调用(史上最详细讲解)
|
存储 缓存 NoSQL
Spring Boot 中的 @CacheEvict 注解
Spring Boot 中的 @CacheEvict 注解
|
存储 运维 监控
智能排班系统 【数据库设计】
智能排班系统 【数据库设计】
637 0
lombok的@Builder原理
只有聪明人才能看见的摘要~( ̄▽ ̄~)~
186 0