自定义通用分页标签一行代码完成分页效果(内含源码)(上)

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS SQL Server,基础系列 2核4GB
简介: 自定义通用分页标签一行代码完成分页效果(内含源码)

前言(分页的思想)

首先简单分析一下,我们第一次发送请求肯定是页面初始化的时候这时候页数和显示的条目都是默认的,查询条件也是查询全部。第二次按照“圣墟”进行查询的时候,只有sql语句发生了变化。第三次按照“圣墟”进行查询但是这一次我在查询过后点击下一页,这时候多了两个参数,关键词和页码。

以上可知,分页只改变了sql语句和页码值,其他查询条件不变,所以我们只需要在页码和sql语句做文章即可。(这只是基本思想,可能还有类别等等参数)

一、创建数据库连接以及实体对象

我们操作的数据肯定是动态的,这里博主用的数据库是MySQL。

创建数据库帮助类(用于连接数据库)

package com.xw.util;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
/**
 * 提供了一组获得或关闭数据库对象的方法
 * 
 */
public class DBAccess {
  private static String driver;
  private static String url;
  private static String user;
  private static String password;
  static {// 静态块执行一次,加载 驱动一次
    try {
      InputStream is = DBAccess.class
          .getResourceAsStream("config.properties");
      Properties properties = new Properties();
      properties.load(is);
      driver = properties.getProperty("driver");
      url = properties.getProperty("url");
      user = properties.getProperty("user");
      password = properties.getProperty("pwd");
      Class.forName(driver);
    } catch (Exception e) {
      e.printStackTrace();
      throw new RuntimeException(e);
    }
  }
  /**
   * 获得数据连接对象
   * 
   * @return
   */
  public static Connection getConnection() {
    try {
      Connection conn = DriverManager.getConnection(url, user, password);
      return conn;
    } catch (SQLException e) {
      e.printStackTrace();
      throw new RuntimeException(e);
    }
  }
  public static void close(ResultSet rs) {
    if (null != rs) {
      try {
        rs.close();
      } catch (SQLException e) {
        e.printStackTrace();
        throw new RuntimeException(e);
      }
    }
  }
  public static void close(Statement stmt) {
    if (null != stmt) {
      try {
        stmt.close();
      } catch (SQLException e) {
        e.printStackTrace();
        throw new RuntimeException(e);
      }
    }
  }
  public static void close(Connection conn) {
    if (null != conn) {
      try {
        conn.close();
      } catch (SQLException e) {
        e.printStackTrace();
        throw new RuntimeException(e);
      }
    }
  }
  public static void close(Connection conn, Statement stmt, ResultSet rs) {
    close(rs);
    close(stmt);
    close(conn);
  }
  public static boolean isOracle() {
    return "oracle.jdbc.driver.OracleDriver".equals(driver);
  }
  public static boolean isSQLServer() {
    return "com.microsoft.sqlserver.jdbc.SQLServerDriver".equals(driver);
  }
  public static boolean isMysql() {
    return "com.mysql.cj.jdbc.Driver".equals(driver);
  }
  public static void main(String[] args) {
    Connection conn = DBAccess.getConnection();
    System.out.println(conn);
    DBAccess.close(conn);
    System.out.println("isOracle:" + isOracle());
    System.out.println("isSQLServer:" + isSQLServer());
    System.out.println("isMysql:" + isMysql());
    System.out.println("数据库连接(关闭)成功");
  }
}

我这里并没有直接将我们的数据库账户以及密码填写在数据库帮助类中,原因有以下几点

  1. 安全性:通过将账户密码存储在配置文件中,可以将敏感信息与代码分离开来,避免直接将敏感数据硬编码在源代码中。这有助于减少泄露敏感信息的风险,特别是在团队环境中,可以限制敏感信息的可见性和访问权限。
  2. 灵活性:将账户密码存储在配置文件中可以提供更大的灵活性。通过修改配置文件,可以在不修改源代码的情况下更改数据库的连接信息,包括账户、密码、主机地址和其他相关配置。这样可以方便地进行配置管理和部署,而不必重新编译或修改代码。
  3. 维护性:将账户密码保存在配置文件中可以提高代码的可维护性。如果需要更改数据库连接信息,只需修改配置文件而不必修改源代码。这样可以减少潜在的错误和冲突,并且更易于管理和维护。
  4. 可配置性:配置文件具有可读性和易于修改的特点,允许用户根据特定需求对账户密码进行自定义配置。这样可以根据不同的环境或部署需求,为不同的数据库设置不同的账户密码,而无需修改源代码并重新编译。

配置文件(用于访问数据库)

#oracle9i
#driver=oracle.jdbc.driver.OracleDriver
#url=jdbc:oracle:thin:@localhost:1521:orcl
#user=scott
#pwd=******
#sql2005
#driver=com.microsoft.sqlserver.jdbc.SQLServerDriver
#url=jdbc:sqlserver://localhost:1433;DatabaseName=test1
#user=sa
#pwd=******
#sql2000
#driver=com.microsoft.jdbc.sqlserver.SQLServerDriver
#url=jdbc:microsoft:sqlserver://localhost:1433;databaseName=unit6DB
#user=sa
#pwd=******
#mysql
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis_ssm?useUnicode=true&characterEncoding=UTF-8&useSSL=false
user=root
pwd=******

填写自己的数据库url路径以及账户密码即可

我们在数据库帮助类测试一下调用getConnection方法,查看是否连接成功

出现第一行代码说明连接成功 ,可以继续我们接下来的操作

编写实体类(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;
  }
}

二、创建pagebean分页工具类以及优化Dao层

这里我将页码、条目、总记录数、url请求路径、上一次请求携带的参数、以及是否分页,六个字段作为PageBean分页工具类的属性,方便我们使用以及制作自定义标签,并且初始化PageBean分页工具类的值(值从request获取),提供计算起始记录的下标、获取最大页数、以及上一页下一页的方法。

注意:看过我分享的通用分页博客的话,可不要误导了哦,这个分页工具类是优化版主要是为了更好的实现自定义分页标签。

PageBean分页工具类

package com.xw.util;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
/**分页工具类
 * @author Java方文山
 *
 */
public class PageBean {
  private int page = 1;// 页码
  private int rows = 10;// 页大小
  private int total = 0;// 总记录数
  private boolean pagination = true;// 是否分页
  private String url;// 保存上一次的请求路径
  private Map<String, String[]> parameterMap;// 保存上一次请求路径携带的参数
  // 初始化数据获取request获取的值并赋值给Pagebean工具类
  public void setPagebean(HttpServletRequest req) {
    // 初始化当前页码page
    this.setPage(req.getParameter("page") != null ? Integer.valueOf(req.getParameter("page")) : 1);
    // 初始化展示页大小total
    this.setRows(req.getParameter("rows") != null ? Integer.valueOf(req.getParameter("rows")) : 10);
    // 初始化是否分页pagination
    // 只有填写了false字符串,才代表不分页所有才需要这样做
    this.setPagination(req.getParameter("pagination") != null ? !"false".equals(pagination) : true);
    // 保存上一次的查询请求url
    this.setUrl(req.getRequestURI().toString());
    // 保存上一次查询的条件parameterMap
    this.setParameterMap(req.getParameterMap());
  }
  public PageBean() {
    super();
  }
  public String getUrl() {
    return url;
  }
  public void setUrl(String url) {
    this.url = url;
  }
  public Map<String, String[]> getParameterMap() {
    return parameterMap;
  }
  public void setParameterMap(Map<String, String[]> parameterMap) {
    this.parameterMap = parameterMap;
  }
  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;
  }
  /**
   * @return 获取最大页数
   */
  public int getMaxPage() {
    return this.total % this.rows == 0 ? this.total / this.rows : (this.total / this.rows) + 1;
  }
  /**
   * 上一页
   * 
   * @return 页码
   */
  public int getPrevPage() {
    // 如果当前页数大于1就允许-1如果当前页数小于1那么就还是当前页数
    return this.page > 1 ? this.page - 1 : this.page;
  }
  /**
   * 下一页
   * 
   * @return 页码
   */
  public int getNextPage() {
    // 如果当前页数小于最大页数就允许+1如果当前页数+1后大于最大页数那么就还是当前页数
    return this.page < this.getMaxPage() ? this.page + 1 : this.page;
  }
  @Override
  public String toString() {
    return "PageBean [page=" + page + ", rows=" + rows + ", total=" + total + ", pagination=" + pagination + "]";
  }
}

因为刚刚分析了每次只有sql语句与页码不同,所以我们将Dao层进行优化,这里就不细讲了,直接上代码,感兴趣的可以查看我写的通用分页

编写一个BaseDao<T>存放通用的分页方法

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<T>

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);
  }
}

看到这里是不是非常的吃惊,怎么会这么简短,将来无论是什么StudentDao、GoodsDao继承BaseDao<T>都是这么几行代码,大大的提升了我们的效率。

这里我们的准备工作就做完了,下面进行我们的重头戏——自定义分页标签

相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
前端开发 Java 测试技术
通用分页【上】
JUnit是流行的、开源的Java单元测试框架,它提供了一种简单而强大的方式来测试Java应用程序中的单元代码。JUnit测试通常涉及创建和运行测试用例,而测试用例是一组独立的测试步骤,用于验证代码是否按照预期工作。JUnit测试通常分为以下四个步骤:定义测试用例:定义每个测试方法所需的输入参数以及期望的输出结果;编写测试代码:编写测试方法并使用断言(Assertion)来验证代码是否按照预期工作;运行测试用例:通常使用JUnit测试浏览器或者其他测试工具来运行测试用例;查看测试结果。
|
SQL 关系型数据库 MySQL
通用分页的详细讲解看这一篇就够了(内含源码)(上)
通用分页的详细讲解看这一篇就够了(内含源码)
119 0
|
16天前
|
SQL Java 数据库连接
【MyBatisPlus·最新教程】包含多个改造案例,常用注解、条件构造器、代码生成、静态工具、类型处理器、分页插件、自动填充字段
MyBatis-Plus是一个MyBatis的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。本文讲解了最新版MP的使用教程,包含多个改造案例,常用注解、条件构造器、代码生成、静态工具、类型处理器、分页插件、自动填充字段等核心功能。
【MyBatisPlus·最新教程】包含多个改造案例,常用注解、条件构造器、代码生成、静态工具、类型处理器、分页插件、自动填充字段
|
5月前
|
前端开发 JavaScript 数据库
分页,快捷链接表单,分页表单,常用软件开发之分页,快捷链接分页表单添加显示,可以做的分页统计,一个简单的添加页面,数据条理清晰呈现如何设计,如何修改成自己想要的,要会编写接口文档,一边写接口
分页,快捷链接表单,分页表单,常用软件开发之分页,快捷链接分页表单添加显示,可以做的分页统计,一个简单的添加页面,数据条理清晰呈现如何设计,如何修改成自己想要的,要会编写接口文档,一边写接口
|
算法 JavaScript Java
通用分页【下】(将分页封装成标签)
调试()是指在软件开发过程中,通过识别、定位和解决程序错误或问题的过程。调试的目的是找出代码中的错误、异常或不正常的行为,并修复它们,以确保程序能够按照预期的方式运行。调试是一个重要的开发技巧,可以帮助开发人员理解程序的执行过程、找出错误的原因,并从中学习和改进。调试可以使用不同的工具和技术来辅助,例如打印输出、日志记录、调试器(debugger)等。调试是开发过程中不可或缺的一部分,可以帮助开发人员提高代码质量、加快解决问题的速度,并优化程序的性能和可靠性。
|
前端开发 数据管理 Java
通用分页(下)
通用分页(下)
43 0
|
监控 前端开发 算法
通用分页(前端)
通用分页(前端)
46 1
|
Java 数据库
通用分页之详解】
通用分页之详解】
40 1
|
7月前
|
前端开发 关系型数据库 MySQL
通用分页详解
通用分页详解
59 0
|
前端开发
通用分页02(前台分页)
通用分页02(前台分页)
60 0