• 通用分页(分页代码的封装)


    本期的数据库使用的是mysql,那么该如何实现分页效果呢?


    目录

    功能特点

    PageBean类的代码编写

    封装分页功能

    流程图

    DBUtil

    封装前的分页代码

    重构-提取公用部分(分页代码的封装)


    功能特点:

    为了提高查询性能及节约网络流量,每次只查询指定的记录数,而不是全部,在数据量比较大时很有用

     当点击下一页或指定页面的数据时,将带着所有的查询条件,再次执行查询

    PageBean类的代码编写:

    首先编写分页信息实体(PageBean类)

    该类用于存储和传递分页参数,主要内容如下:

    页码:从页面传递过来

    每页行数,从页面传递过来

    总记录数,从数据库中统计得到

    是否分页,如果为false,则查询所有记录

    查询参数,点击上一页或者下一页时需要及携带用户输入的所有查询参数

    另外提供上页,下页,总页数等计算

     

    1. public class PageBean {
    2. // 页码
    3. private int page = 1;
    4. //每页显示的记录数
    5. private int rows = 10;
    6. // 总记录数
    7. private int total = 0;
    8. //是否分页
    9. private boolean pagination = true;
    10. // 记录查询的url,以便于点击分页时再次使用
    11. private String url;
    12. //存放请求参数,用于生成隐藏域中的元素
    13. private Map parameterMap;
    14. /**
    15. * 根据传入的Request初始化分页对象
    16. * @param request
    17. */
    18. public void setRequest(HttpServletRequest request) {
    19. if(!StringUtils.isNullOrEmpty(request.getParameter("page"))) {
    20. this.page = Integer.valueOf(request.getParameter("page"));
    21. }
    22. if(!StringUtils.isNullOrEmpty(request.getParameter("rows"))) {
    23. this.rows = Integer.valueOf(request.getParameter("rows"));
    24. }
    25. if(!StringUtils.isNullOrEmpty(request.getParameter("pagination"))) {
    26. this.pagination = Boolean.valueOf(request.getParameter("pagination"));
    27. }
    28. this.url = request.getRequestURI();
    29. this.parameterMap = request.getParameterMap();
    30. request.setAttribute("pageBean", this);
    31. }
    32. public int getPage() {
    33. return page;
    34. }
    35. public void setPage(int page) {
    36. this.page = page;
    37. }
    38. public int getRows() {
    39. return rows;
    40. }
    41. public void setRows(int rows) {
    42. this.rows = rows;
    43. }
    44. public int getTotal() {
    45. return total;
    46. }
    47. public void setTotal(int total) {
    48. this.total = total;
    49. }
    50. public boolean isPagination() {
    51. return pagination;
    52. }
    53. public void setPagination(boolean pagination) {
    54. this.pagination = pagination;
    55. }
    56. public String getUrl() {
    57. return url;
    58. }
    59. public void setUrl(String url) {
    60. this.url = url;
    61. }
    62. public Map getParameterMap() {
    63. return parameterMap;
    64. }
    65. public void setParameterMap(Map parameterMap) {
    66. this.parameterMap = parameterMap;
    67. }
    68. //计算起始页码
    69. public int getStartIndex() {
    70. return (this.page - 1) * this.rows;
    71. }
    72. //获取总页数
    73. public int getTotalPage() {
    74. if (this.getTotal() % this.rows == 0) {
    75. return this.getTotal() / this.rows;
    76. } else {
    77. return this.getTotal() / this.rows + 1;//102%10=2,增加一页放
    78. }
    79. }
    80. //上一页
    81. public int getPreviousPage() {
    82. return this.page - 1 > 0 ? this.page - 1 : 1;
    83. }
    84. //下一页
    85. public int getNextPage() {
    86. return this.page + 1 > getTotalPage() ? getTotalPage() : this.page + 1;
    87. }
    88. }

    封装分页功能:

    流程图

    tip:在编写后台代码之前,可以先画一个流程图,来理清分页功能的业务逻辑。

    1.查询满足条件的总记录数

    2.查询满足条件的当前页的数据

    3.上两个步骤的查询条件要一致

     

    DBUtil

    1.为简化数据库操作,新建一个DBUtil工具类

    1. public class DBUtil {
    2. private static String DRIVER_NAME = "com.mysql.jdbc.Driver";
    3. private static String DB_URL = "jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8&useSSL=false";
    4. private static String DB_USER = "root";
    5. private static String DB_PASSWORD = "123456";
    6. private DBUtil() {
    7. }
    8. static {
    9. try {
    10. Class.forName(DRIVER_NAME);
    11. } catch (ClassNotFoundException e) {
    12. e.printStackTrace();
    13. }
    14. }
    15. public static Connection getConection() throws SQLException {
    16. Connection connection = DriverManager.getConnection(DB_URL, DB_USER, DB_PASSWORD);
    17. return connection;
    18. }
    19. public static void closeDB(ResultSet rs, Statement ps, Connection con) {
    20. try {
    21. if (rs != null && !rs.isClosed()) {
    22. rs.close();
    23. }
    24. if (ps != null && !ps.isClosed()) {
    25. ps.close();
    26. }
    27. if (con != null && !con.isClosed()) {
    28. con.close();
    29. }
    30. } catch (SQLException e) {
    31. e.printStackTrace();
    32. }
    33. }
    34. public static void closeDB(ResultSet rs, Statement ps) {
    35. try {
    36. if (rs != null && !rs.isClosed()) {
    37. rs.close();
    38. }
    39. if (ps != null && !ps.isClosed()) {
    40. ps.close();
    41. }
    42. } catch (SQLException e) {
    43. e.printStackTrace();
    44. }
    45. }
    46. }

    2.学生实体类(根据数据库中字段创建)

    1. public class Student {
    2. private Integer sid;
    3. private String sname;
    4. private Double score;
    5. private String clazz;
    6. public Integer getSid() {
    7. return sid;
    8. }
    9. public void setSid(Integer sid) {
    10. this.sid = sid;
    11. }
    12. public String getSname() {
    13. return sname;
    14. }
    15. public void setSname(String sname) {
    16. this.sname = sname;
    17. }
    18. public Double getScore() {
    19. return score;
    20. }
    21. public void setScore(Double score) {
    22. this.score = score;
    23. }
    24. public String getClazz() {
    25. return clazz;
    26. }
    27. public void setClazz(String clazz) {
    28. this.clazz = clazz;
    29. }
    30. @Override
    31. public String toString() {
    32. return "Student [sid=" + sid + ", sname=" + sname + ", score=" + score + ", clazz=" + clazz + "]";
    33. }
    34. }

    封装前的分页代码

    3.实体将学生数据分页的代码(未封装)

    1. public class StudentDao {
    2. public List getStudents(String sname, PageBean pageBean) {
    3. String sql = "select * from t_student t ";
    4. if (!Objects.isNull(sname) && sname.length() > 0) {
    5. sql += " where t.sname like ?";
    6. }
    7. List students = new ArrayList<>();
    8. Connection con = null;
    9. PreparedStatement ps = null;
    10. ResultSet rs = null;
    11. /*
    12. * 如果不需要分页,则直接查询查询,并返回查询结果。
    13. */
    14. if (pageBean == null || !pageBean.isPagination()) {
    15. //3.
    16. try {
    17. con = DBUtil.getConection();
    18. ps = con.prepareStatement(sql);
    19. //设置查询参数
    20. if(sname != null) {
    21. ps.setObject(1, sname+"%");
    22. }
    23. rs = ps.executeQuery();
    24. //1. 差异化部分
    25. while(rs.next()) {
    26. Student stu = new Student();
    27. stu.setSid(rs.getInt("sid"));
    28. stu.setSname(rs.getString("sname"));
    29. stu.setAge(rs.getInt("age"));
    30. stu.setRemark(rs.getString("remark"));
    31. students.add(stu);
    32. }
    33. } catch (Exception e) {
    34. e.printStackTrace();
    35. //2.
    36. } finally {
    37. DBUtil.closeDB(rs, ps, con);
    38. }
    39. return students;
    40. } else {
    41. /*
    42. * 如果需要分页,则分两步操作:
    43. * 1. 查询总记录数
    44. * 2. 查询当前页记录
    45. */
    46. //1. 生成统计总记录数的SQL, 查询总记录数
    47. try {
    48. String countSql = "select count(*) from (" + sql + ") tmp";
    49. con = DBUtil.getConection();
    50. ps = con.prepareStatement(countSql);
    51. //设置查询参数
    52. if(sname != null) {
    53. ps.setObject(1, sname+"%");
    54. }
    55. rs = ps.executeQuery();
    56. while(rs.next()) {
    57. pageBean.setTotal(rs.getInt(1));
    58. }
    59. /*
    60. * 如果统计的总记录数为0,则表示没有符合条件的记录,直接返回一个空结果集即可。
    61. */
    62. if(pageBean.getTotal() == 0) {
    63. return students;
    64. }
    65. } catch (Exception e) {
    66. e.printStackTrace();
    67. } finally {
    68. DBUtil.closeDB(rs, ps);
    69. }
    70. /*
    71. * 生成分页sql,查询当前页的数据
    72. */
    73. try {
    74. String pagingSql = sql + " limit " + pageBean.getStartRow() + ", " + pageBean.getRows();
    75. ps = con.prepareStatement(pagingSql);
    76. //设置查询参数
    77. if(sname != null) {
    78. ps.setObject(1, sname+"%");
    79. }
    80. rs = ps.executeQuery();
    81. //差异化
    82. while(rs.next()) {
    83. Student stu = new Student();
    84. stu.setSid(rs.getInt("sid"));
    85. stu.setSname(rs.getString("sname"));
    86. stu.setAge(rs.getInt("age"));
    87. stu.setRemark(rs.getString("remark"));
    88. students.add(stu);
    89. }
    90. } catch (Exception e) {
    91. e.printStackTrace();
    92. } finally {
    93. DBUtil.closeDB(rs, ps, con);
    94. }
    95. return students;
    96. }
    97. }
    98. //测试
    99. public static void main(String[] args) {
    100. StudentDao dao = new StudentDao();
    101. List students = dao.getStudents("张", new PageBean());
    102. students.forEach(s -> System.out.println(s));
    103. }
    104. }

    通过观察上述学生数据分页功能的代码实现,可以从中提取不会改变的代码逻辑,封装成一个分页功能类,这样在各种不同数据对象需要分页时直接调用BaseDao类即可

    重构-提取公用部分(分页代码的封装)

    为了进行公共方法的抽取,需要找出上面实习中的可通用部分,和差异化部分

    • 只要是分页,就会统计总记录数,而总记录数的统计是在业务sql外封装了一个select count(*)是有规律可循的,可以通用
    • 只要是分页,则封装分页sql也是有规律可循的(在业务sql后加limit子句即可),可以通用
    • 因为每个查询对应的业务实体(即模型)不同,所以ORM映射部分不能通用

    公用方法封装思路

    • 将可通用的部分封装到模板中

    • 差异化部分(即不可通用部分),可以定义一个处理接口,以便于通过参数传入个性化的实现部分

    具体实现:

    通用分页查询模板类BaseDao(已封装)

    1. public final class BaseDao {
    2. private BaseDao() {}
    3. //回调函数接口
    4. public static interface ICovent {
    5. List convent(ResultSet rs) throws SQLException;
    6. }
    7. public static List query(String sql,
    8. Object[] params,
    9. PageBean pageBean,
    10. ICovent covent
    11. ) {
    12. List students = new ArrayList<>();
    13. Connection con = null;
    14. PreparedStatement ps = null;
    15. ResultSet rs = null;
    16. if(pageBean == null || !pageBean.isPagination()) {
    17. try {
    18. con = DBUtil.getConection();
    19. ps = con.prepareStatement(sql);
    20. //设置查询参数
    21. int i = 1;
    22. for(Object param: params) {
    23. ps.setObject(i, param);
    24. i++;
    25. }
    26. rs = ps.executeQuery();
    27. //通过使用者传入的回调函数,执行转换
    28. students = covent.convent(rs);
    29. } catch (SQLException e) {
    30. e.printStackTrace();
    31. } finally {
    32. DBUtil.closeDB(rs, ps, con);
    33. }
    34. } else {
    35. //需要分页的情况
    36. String countSql = "select COUNT(*) from (" + sql + ") tmp";
    37. try {
    38. con = DBUtil.getConection();
    39. ps = con.prepareStatement(countSql);
    40. //设置查询参数
    41. int i = 1;
    42. for(Object param: params) {
    43. ps.setObject(i, param);
    44. i++;
    45. }
    46. rs = ps.executeQuery();
    47. if(rs.next()) {
    48. Integer total = rs.getInt(1);
    49. pageBean.setTotal(total);
    50. }
    51. //判断总记录数是否大于0,如果大于零表示有记录,需求查询当前记录
    52. //如果没有就直接返回
    53. if(pageBean.getTotal() <= 0) {
    54. return students;
    55. }
    56. //查询当前业的数据
    57. String pagingSql = sql + " limit " + pageBean.getStartIndex() + ", " + pageBean.getRows();
    58. ps = con.prepareStatement(pagingSql);
    59. //设置查询参数
    60. int j = 1;
    61. for(Object param: params) {
    62. ps.setObject(i, param);
    63. j++;
    64. }
    65. rs = ps.executeQuery();
    66. //变化部分
    67. students = covent.convent(rs);
    68. } catch (SQLException e) {
    69. e.printStackTrace();
    70. } finally {
    71. DBUtil.closeDB(rs, ps, con);
    72. }
    73. }
    74. return students;
    75. }
    76. }

    调用测试:

    1. public class StudentDao {
    2. public List<Student> getStudents(String sname, PageBean pageBean) {
    3. //构造sql语句 变化部分
    4. String sql = "select * from t_student t ";
    5. List<Object> param = new ArrayList<>();
    6. if (!Objects.isNull(sname) && sname.length() > 0) {
    7. sql += " where t.sname like ?";
    8. param.add(sname);
    9. }
    10. List<Student> list = BaseDao.query(sql, param.toArray(), pageBean, new ICovent<Student>() {
    11. @Override
    12. public List<Student> convent(ResultSet rs) throws SQLException {
    13. List<Student> students = new ArrayList<>();
    14. while(rs.next()) {
    15. Student stu = new Student();
    16. stu.setSid(rs.getInt("sid"));
    17. stu.setSname(rs.getString("sname"));
    18. stu.setClazz(rs.getString("class"));
    19. students.add(stu);
    20. }
    21. return students;
    22. }
    23. });
    24. return list;
    25. }
    26. public static void main(String[] args) {
    27. StudentDao dao = new StudentDao();
    28. List<Student> students = dao.getStudents(null, new PageBean());
    29. students.forEach(t-> System.out.println(t));
    30. }
    31. }

     


     感谢收看~

  • 相关阅读:
    L4.linux命令每日一练 -- 第二章 文件和目录操作命令 -- pwd和cd命令
    通俗易懂VAE变分自编码器
    Ansible playbook中 block用法
    灵性图书馆:好书推荐-《零极限》
    DeepMind发布新模型Mirasol3B:更高效处理音频、视频数据
    基于uniapp的商城外卖小程序
    【C++】继承 ② ( 继承语法 | 三种继承 - 共有继承 / 保护继承 / 私有继承 | 继承与父类私有成员 | 子类访问父类私有成员 | 代码示例 )
    安卓期末大作业——图书信息管理系统
    Logback的使用
    15 Spring Security OAuth2登录(三方登录)
  • 原文地址:https://blog.csdn.net/m0_64043477/article/details/125884611