通用分页【上】

本文涉及的产品
服务治理 MSE Sentinel/OpenSergo,Agent数量 不受限
应用实时监控服务-可观测链路OpenTelemetry版,每月50GB免费额度
可观测监控 Prometheus 版,每月50GB免费额度
简介: JUnit是流行的、开源的Java单元测试框架,它提供了一种简单而强大的方式来测试Java应用程序中的单元代码。JUnit测试通常涉及创建和运行测试用例,而测试用例是一组独立的测试步骤,用于验证代码是否按照预期工作。JUnit测试通常分为以下四个步骤:定义测试用例:定义每个测试方法所需的输入参数以及期望的输出结果;编写测试代码:编写测试方法并使用断言(Assertion)来验证代码是否按照预期工作;运行测试用例:通常使用JUnit测试浏览器或者其他测试工具来运行测试用例;查看测试结果。


一、什么是通用分页?

通用分页是一种常用的分页方式,也被称为“前端分页”。它是指在获取数据的时候,将大量的数据分成若干页以便于用户查看和操作。通用分页通常是在前端完成的,也就是在用户界面上进行处理。通用分页的优点是可以减轻后台服务器压力,提高速度和用户体验。

二、什么情况下要用到分页?

在处理大量数据时,使用分页可以提高系统的性能和用户体验。一般来说,当数据量较大时,需要使用分页来减少一次性加载的数据量,避免对程序的性能产生负面影响,从而提高程序的响应速度和稳定性。同时,分页也可以更加方便用户查看和操作数据,避免用户因数据过多而感到信息冗杂和疲劳。因此,对于需要处理大量数据的应用程序(如电子商务网站、新闻网站等),分页是一种非常有效的数据处理方式

三、常用的查询方法

1、准备工作

编写一个类的常用方法

package com.tgq.util;
public class StringUtils {
  // 私有的构造方法,保护此类不能在外部实例化
  private StringUtils() {
  }
  /**
   * 如果字符串等于null或去空格后等于"",则返回true,否则返回false
   * 
   * @param s
   * @return
   */
  public static boolean isBlank(String s) {
    boolean b = false;
    if (null == s || s.trim().equals("")) {
      b = true;
    }
    return b;
  }
  /**
   * 如果字符串不等于null或去空格后不等于"",则返回true,否则返回false
   * 
   * @param s
   * @return
   */
  public static boolean isNotBlank(String s) {
    return !isBlank(s);
  }
}

image.gif

编写分页工具类

package com.tgq.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 + "]";
  }
}

image.gif

编写数据库帮助类

package com.tgq.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("数据库连接(关闭)成功");
  }
}

image.gif

编写config.properties

#mysql
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis_ssm?useUnicode=true&characterEncoding=UTF-8&useSSL=false
user=root
pwd=123456

image.gif

编写实体类

package com.tgq.entity;
public class Book {
  private int bid;
  private String bname;
  private float 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;
  }
}

image.gif

2、开始

编写dao类查询方法

我们基础的常用写法:

/**
   * 以前编写的查询方法
   * 
   * @return
   * @throws Exception
   */
  public List<Book> list1(Book book, PageBean bean) throws Exception {
    List<Book> list = new ArrayList<Book>();
    Connection conn = DBAccess.getConnection();
    String sql = "select * from t_mvc_book where 1=1";
    String bname = book.getBname();
    // 判断bname不为空的情况下才进行
    if (StringUtils.isNotBlank(bname)) {
      sql += " and bname LIKE '%" + bname + "%'";
    }
    PreparedStatement ps = conn.prepareStatement(sql);
    ResultSet rs = ps.executeQuery();
    while (rs.next()) {
      Book b = new Book();
      b.setBid(rs.getInt(1));
      b.setBname(rs.getString(2));
      b.setPrice(rs.getFloat(3));
      list.add(b);
    }
    return list;
  }

image.gif

输出结果:

image.gif编辑

四、反射完成通用查询

1、为什么要学并且使用反射的通用查询

反射是一种在运行时获取类型信息的机制,它可以动态地获取类、方法、属性等在代码编写时无法确定的信息,进而在运行时根据需要进行调用和操作。在Web应用程序中,反射可以帮助我们构建通用的查询器,从而在运行时根据用户传递的参数动态地生成查询条件。

使用反射构建通用查询的好处在于可以大幅度减少代码的重复性,提高代码的可维护性和可扩展性。在实际开发中,经常遇到需要根据前端传递的参数动态地生成查询条件的情况,此时使用反射可以避免大量的if-else语句或者手动编写多个查询方法的情况,并且可以实现代码复用,提高开发效率

通过使用反射,我们可以通过Java或者Python中的Class类或者type类型进行动态的方法调用、属性获取以及对象创建,从而为通用查询奠定了基础。在构建通用查询时,我们可以使用反射机制动态地获取到查询对象中的各种属性和方法,并根据用户传递的参数动态地构建查询条件,进而实现灵活、可定制化的查询功能

总之,反射是一种非常有用的编程工具,在构建通用查询等需要动态生成代码的场景中可以得到广泛的应用。通过反射机制,我们可以大大减少代码的重复性,提高代码的可读性、可维护性和可扩展性,从而提高开发效率和代码质量。

2、优化普通查询,利用反射编写

BookDao:操作的是书籍表 代码重复:必须要写的

Connection conn =  DBAccess.getConnection(); 
  PreparedStatement ps = conn.prepareStatement(sql); 
  ResultSet rs = ps.executeQuery();

image.gif

流程重复

    1. 创建表对应的实体类对象
    2. 将查询出来的结果集添加到实例化对象的属性中
    3. 已经被填充的实体对象,加入到集合中
    while (rs.next()) { 
                Book b = new Book(); 
                b.setBid(rs.getInt(1));
                b.setBname(rs.getString(2)); 
                b.setPrice(rs.getFloat(3)); 
                list.add(b);
           }

    image.gif

    首先我们需要编写一个BaseDao的帮助类泛型使用<T>,我们把BookDao里面普通的方法在BaseDao里面进行一个优化

    /**
       * 利用反射去优化
       *  通用查询
       * 
       * @param sql
       *            sql语句
       * @param cl
       *            Class对象
       * @param bean
       * @return
       * @throws Exception
       */
      public List<T> executeQuery(String sql, Class cl, PageBean bean) throws Exception {
        List<T> list = new ArrayList<T>();
        Connection conn = DBAccess.getConnection();
        PreparedStatement ps = conn.prepareStatement(sql);
        ResultSet rs = ps.executeQuery();
        while (rs.next()) {
          T t = (T) cl.newInstance();
          // 拿到Class对应所有的属性对象
          Field[] fields = cl.getDeclaredFields();
          // 循环
          for (Field f : fields) {
            // 打开权限
            f.setAccessible(true);
            f.set(t, rs.getObject(f.getName()));
            list.add(t);
          }
          // b.setBid(rs.getInt(1));
          // b.setBname(rs.getString(2));
          // b.setPrice(rs.getFloat(3));
        }
        return list;
      }

    image.gif

    我们的

    f.set(t, rs.getObject(f.getName()));

    image.gif

    // b.setBid(rs.getInt(1));
                // b.setBname(rs.getString(2));
                // b.setPrice(rs.getFloat(3));

    image.gif

    进行了一个代替

    BookDao里面编写一个方法进行一个测试

    public List<Book> list2(Book book, PageBean bean) throws Exception {
        String sql = "select * from t_mvc_book where 1=1";
        String bname = book.getBname();
        // 判断bname不为空的情况下才进行
        if (StringUtils.isNotBlank(bname)) {
          sql += " and bname LIKE '%" + bname + "%'";
        }
        return executeQuery(sql, Book.class, bean);
      }

    image.gif

    我们在BookDao里面编写main方法进行测试看能不能拿到数据

    public static void main(String[] args) throws Exception {
        BookDao bookDao = new BookDao();
        Book book = new Book();
        book.setBname("圣墟");
        PageBean bean = new PageBean();
        List<Book> list = bookDao.list2(book, bean);
        for (Book b : list) {
          System.out.println(b);
        }
      }

    image.gif

    输出结果:

    image.gif编辑

    五、junit单元测试

    1、什么是junit单元测试?

    JUnit是流行的、开源的Java单元测试框架,它提供了一种简单而强大的方式来测试Java应用程序中的单元代码。JUnit测试通常涉及创建和运行测试用例,而测试用例是一组独立的测试步骤,用于验证代码是否按照预期工作。

    JUnit测试通常分为以下四个步骤:

      1. 定义测试用例:定义每个测试方法所需的输入参数以及期望的输出结果;
      2. 编写测试代码:编写测试方法并使用断言(Assertion)来验证代码是否按照预期工作;
      3. 运行测试用例:通常使用JUnit测试浏览器或者其他测试工具来运行测试用例;
      4. 查看测试结果:如果测试失败,通常需要调试代码以找出问题所在,并修复代码。

      JUnit仅是一种测试框架,它不仅可以用于Java应用程序的单元测试,也可以用于自动化测试,并且可以与其他测试框架、工具和集成环境(如Eclipse、IntelliJ IDEA等)配合使用。在Java开发中,JUnit测试被广泛应用于各种类型的Java项目中,包括Web应用程序桌面应用程序数据库应用程序等。由于JUnit测试具有易于使用、易于理解、易于维护和可重复性等优点,因此在现代软件开发中成为了一个不可替代的测试手段。

      2、开始

      BookDao里面编写两个test方法测试junit

      public void test1() throws Exception {
          BookDao bookDao = new BookDao();
          Book book = new Book();
          book.setBname("圣墟");
          PageBean bean = new PageBean();
          List<Book> list = bookDao.list1(book, bean);
          for (Book b : list) {
            System.out.println(b);
          }
        }
        public void test2() throws Exception {
          BookDao bookDao = new BookDao();
          Book book = new Book();
          book.setBname("圣墟");
          PageBean bean = new PageBean();
          List<Book> list = bookDao.list2(book, bean);
          for (Book b : list) {
            System.out.println(b);
          }
        }

      image.gif

      1、我们要找到我们的项目文件右键==》Build Path===》Configure Build Path

      image.gif编辑

      2、如图中1、2、3

      image.gif编辑

      3、选择的是JUnit5,我们换成JUnit4 ===》Finish

      image.gif编辑

      4、在我们写的方法上面写一个@test

      @Test
        public void test1() throws Exception {
          BookDao bookDao = new BookDao();
          Book book = new Book();
          book.setBname("圣墟");
          PageBean bean = new PageBean();
          List<Book> list = bookDao.list1(book, bean);
          for (Book b : list) {
            System.out.println(b);
          }
        }
        @Test
        public void test2() throws Exception {
          BookDao bookDao = new BookDao();
          Book book = new Book();
          book.setBname("圣墟");
          PageBean bean = new PageBean();
          List<Book> list = bookDao.list2(book, bean);
          for (Book b : list) {
            System.out.println(b);
          }
        }

      image.gif

      然后我们就可以右键选择===》Run As===》JUnit Test

      image.gif编辑

      测试结果:

      image.gif编辑

      六、通用查询分页

      1、优化修改

      我们在basedao里面的通用查询方法里面进行一个判断,并且写两个方法

      // 如果bean是空
          if (bean != null && bean.isPagination()) {
            String countSQl = getCountSQL(sql);
            conn = DBAccess.getConnection();
            ps = conn.prepareStatement(countSQl);
            rs = ps.executeQuery();
            if (rs.next()) {
              // 设置
              bean.setTotal(rs.getObject("n").toString());
            }
            //////////
            String pageSQl = getPageSQL(sql, bean);
            conn = DBAccess.getConnection();
            ps = conn.prepareStatement(pageSQl);
            rs = ps.executeQuery();
          } else {
            conn = DBAccess.getConnection();
            ps = conn.prepareStatement(sql);
            rs = ps.executeQuery();
          }

      image.gif

      /**
         * 拼接出最终展示出的数据sql
         * 
         * @param sql
         *            原生的sql
         * @param bean
         * @return
         */
        private String getPageSQL(String sql, PageBean bean) {
          return sql + " limit " + bean.getStartIndex() + "," + bean.getRows();
        }
        /**
         * 拼接出查询符合条件的总记录数sql
         * 
         * @param sql
         *            原生的sql
         * @return
         */
        private String getCountSQL(String sql) {
          return "select count(1) as n from (" + sql + ") a";
        }

      image.gif

      我们还是利用这个方法进行一个测试,使用JUnit测试

      public List<Book> list2(Book book, PageBean bean) throws Exception {
          String sql = "select * from t_mvc_book where 1=1";
          String bname = book.getBname();
          // 判断bname不为空的情况下才进行
          if (StringUtils.isNotBlank(bname)) {
            sql += " and bname LIKE '%" + bname + "%'";
          }
          return executeQuery(sql, Book.class, bean);
        }

      image.gif

      @Test
        public void test3() throws Exception {
          BookDao bookDao = new BookDao();
          Book book = new Book();
          book.setBname("圣墟");
          PageBean bean = new PageBean();
          // 是否需要分页true false
          // bean.setPagination(false);
              //设置页码
          bean.setPage(2);
          List<Book> list = bookDao.list2(book, bean);
              //循环遍历输出
          for (Book b : list) {
            System.out.println(b);
          }
              //打印输出
          System.out.println(bean);
        }

      image.gif

      输出结果:

      image.gif编辑

      完整的BaseDao方法

      package com.tgq.util;
      import java.lang.reflect.Field;
      import java.sql.Connection;
      import java.sql.PreparedStatement;
      import java.sql.ResultSet;
      import java.util.ArrayList;
      import java.util.List;
      /**
       * 
       * @author tgq
       *
       * @param <T>
       */
      public class BaseDao<T> {
        /**
         * 利用反射去优化 通用查询
         * 
         * @param sql
         *            sql语句
         * @param cl
         *            Class对象
         * @param bean
         * @return
         * @throws Exception
         */
        public List<T> executeQuery(String sql, Class cl, PageBean bean) throws Exception {
          List<T> list = new ArrayList<T>();
          Connection conn = null;
          PreparedStatement ps = null;
          ResultSet rs = null;
          // 如果bean是空
          if (bean != null && bean.isPagination()) {
            String countSQl = getCountSQL(sql);
            conn = DBAccess.getConnection();
            ps = conn.prepareStatement(countSQl);
            rs = ps.executeQuery();
            if (rs.next()) {
              // 设置
              bean.setTotal(rs.getObject("n").toString());
            }
            //////////
            String pageSQl = getPageSQL(sql, bean);
            conn = DBAccess.getConnection();
            ps = conn.prepareStatement(pageSQl);
            rs = ps.executeQuery();
          } else {
            conn = DBAccess.getConnection();
            ps = conn.prepareStatement(sql);
            rs = ps.executeQuery();
          }
          while (rs.next()) {
            T t = (T) cl.newInstance();
            // 拿到Class对应所有的属性对象
            Field[] fields = cl.getDeclaredFields();
            // 循环
            for (Field f : fields) {
              // 打开权限
              f.setAccessible(true);
              f.set(t, rs.getObject(f.getName()));
              list.add(t);
            }
            // b.setBid(rs.getInt(1));
            // b.setBname(rs.getString(2));
            // b.setPrice(rs.getFloat(3));
            //
            // list.add(b);
          }
          return list;
        }
        /**
         * 拼接出最终展示出的数据sql
         * 
         * @param sql
         *            原生的sql
         * @param bean
         * @return
         */
        private String getPageSQL(String sql, PageBean bean) {
          return sql + " limit " + bean.getStartIndex() + "," + bean.getRows();
        }
        /**
         * 拼接出查询符合条件的总记录数sql
         * 
         * @param sql
         *            原生的sql
         * @return
         */
        private String getCountSQL(String sql) {
          return "select count(1) as n from (" + sql + ") a";
        }
      }

      image.gif

      【希望对你们有用!!!见谅!!!】

      相关实践学习
      如何快速连接云数据库RDS MySQL
      本场景介绍如何通过阿里云数据管理服务DMS快速连接云数据库RDS MySQL,然后进行数据表的CRUD操作。
      全面了解阿里云能为你做什么
      阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
      相关文章
      |
      SQL 前端开发 Java
      通用分页-后台
      通用分页-后台
      92 0
      |
      前端开发 数据管理 Java
      通用分页(下)
      通用分页(下)
      48 0
      |
      算法 JavaScript Java
      通用分页【下】(将分页封装成标签)
      调试()是指在软件开发过程中,通过识别、定位和解决程序错误或问题的过程。调试的目的是找出代码中的错误、异常或不正常的行为,并修复它们,以确保程序能够按照预期的方式运行。调试是一个重要的开发技巧,可以帮助开发人员理解程序的执行过程、找出错误的原因,并从中学习和改进。调试可以使用不同的工具和技术来辅助,例如打印输出、日志记录、调试器(debugger)等。调试是开发过程中不可或缺的一部分,可以帮助开发人员提高代码质量、加快解决问题的速度,并优化程序的性能和可靠性。
      |
      Java 数据库
      通用分页之详解】
      通用分页之详解】
      47 1
      |
      监控 前端开发 算法
      通用分页(前端)
      通用分页(前端)
      48 1
      |
      7月前
      |
      前端开发 关系型数据库 MySQL
      通用分页详解
      通用分页详解
      65 0
      通用分页(后台分页)
      通用分页(后台分页)
      61 0
      |
      前端开发
      通用分页02(前台分页)
      通用分页02(前台分页)
      64 0
      |
      存储 前端开发 Java
      详解通用分页(前端)
      详解通用分页(前端)
      62 0
      |
      SQL Java 关系型数据库
      通用分页详细讲解(后端)
      通用分页详细讲解(后端)
      73 0