• SpringMVCDemo中,遇到的问题(四) 之分页功能


    1、背景:

      线上经常出现因为研发代码编写不规范,sql语句全表查询,数据过多,硬生生把内存塞爆,不断GC,整个服务宕掉的情况。

      引发这种场景的原因之一,归咎在使用mybatis编写sql语句时使用了万能查询语句。where 1=1之后,拼接的if条件都不符合条件。如下:

      如果下面sql语句中,if条件都匹配不上,最后会执行  select * from retailer where 1=1

     1     <!--sql片段-->
     2     <sql id="query_retailer_where">
     3         <if test="name!=null">and name like '%${name}%'</if>
     4         <if test="address!=null">and address like '%${address}%'</if>
     5         <if test="status!=null">and status like '%${status}%'</if>
     6         <if test="telphone!=null">and telphone = #{telphone}</if>
     7         <if test="createtime!=null">
     8             and createtime = DATE_FORMAT(#{createtime},'%Y-%m-%d %H:%i:%S')
     9         </if>
    10         <if test="starttime != null"> <![CDATA[ and createtime >= to_date(#{starttime},'yyyy-MM-dd HH:mm:ss')]]></if>
    11         <if test="endtime != null"> <![CDATA[ and createtime <= to_date(#{endtime},'yyyy-MM-dd HH:mm:ss')]]></if>
    12     </sql>
    13 
    14     <!--查询-->
    15     <select id="find" resultMap="resultMap" parameterType="java.util.Map">
    16         select * from retailer
    17         where 1=1
    18         <include refid="query_retailer_where"></include>
    19         <if test="startPage != null and pageSize !=null">
    20             order by createtime desc
    21 --             LIMIT #{startPage},#{pageSize}
    22         </if>
    23     </select>

     这类场景的解决办法,

     一种是使用<choose></choose>,如果匹配不到就查询不到,或查询个默认范围。

    <where>
        <choose>
            <when>
            </when>
            <otherwise>
                 AND 1=0
            </otherwise>  
       </choose>
    </where>

    另外一种就是使用分页查询,引入Limit,限制查询出的数据条数。

    2、使用思路:

        <!--查询-->
        <select id="find" resultMap="resultMap" parameterType="java.util.Map">
            select * from retailer
            where 1=1
            <include refid="query_retailer_where"></include>
            <if test="startPage != null and pageSize !=null">
                order by createtime desc
                LIMIT #{startPage},#{pageSize}
            </if>
        </select>

    由底向上的思路,从limit的使用,来考虑一下传值的逻辑。

    limit的语法是,select * from table where ... limit start,size;

      start:从第几条记录开始。

      size : 读取几条记录。

    首先需要知道,从第几条[下标]开始读,要往后读取多少条。

    即分页显示的话,需要传给sql语句两个参数,某一页面中第一条在表中的下标,以及页面中数据的条数。

    size好说,默认指定一个,或者从前台输入后取一个。

    start下标,

      一种方式是,前端js计算完,直接传值过来。不涉及sql语句的计算。

      另一种方式是,前端传过来要跳转到的页码,后台sql中加入(pageNum-1)*size的计算,即为对应页面的start下标值。

    3、实现:

    以第一种为例,实现不同页码的跳转。

     1)编写分页查询的PageEntity类,首次查询列表,需要给定个默认值

    /**
     * 分页类,包含三个属性
     * 开始页面、起始数据位置、每页要取的数据
     */
    public class PageEntity {
    
        //当前页
        private Integer currentPage;
        //起始页
        private Integer startPage;
        //页面的数据大小
        private Integer pageSize;
    
        public Integer getCurrentPage() {
            if(currentPage==null){
                currentPage = 1;
            }
            return currentPage;
        }
    
        public void setCurrentPage(Integer currentPage) {
            this.currentPage = currentPage;
        }
    
        public Integer getStartPage() {
            if (startPage==null){
                startPage=0;
            }
            return startPage;
        }
    
        public void setStartPage(Integer startPage) {
            this.startPage = startPage;
        }
    
        public Integer getPageSize() {
            if (pageSize==null){
                pageSize=5;
            }
            return pageSize;
        }
    
        public void setPageSize(Integer pageSize) {
            this.pageSize = pageSize;
        }
    }

     2)使用实际要分页的实体类,继承PageEntity。拥有分页的属性,DAO层(见2使用思路中的sql语句),Service层实际代码略

     3)controller层代码如下,主要对视图传过来的数据进行处理,并回传给视图层。

        //展示零售商
        @RequestMapping(value = "/list")
        public String list(RetailerEntity retailer, Model model){
            Map<String,Object> map = retailerToMap(retailer);
            List<RetailerEntity> retailerList = retailerService.find(map);
    
            //1.设置当前页码数
            model.addAttribute("currentPage",retailer.getCurrentPage());
            //2.获取开始的页码
            model.addAttribute("startPage",retailer.getStartPage());
            //3.获取总条数
            int countNumber = retailerService.count(map);
            model.addAttribute("countNumber",countNumber);
            //4.获取每页显示的数据条数,默认显示10条
            int pageSize  = retailer.getPageSize();
            model.addAttribute("pageSize",pageSize);
            //5.获取总页数
            int sumPageNumber = countNumber%pageSize==0?(countNumber/pageSize):((countNumber/pageSize)+1);
            model.addAttribute("sumPageNumber",sumPageNumber);
            model.addAttribute("list",retailerList);
    return "/retailer/retailerHome.jsp"; }

    4)页面及页面代码

    页面将定义的这几个属性,设置为hidden的input标签。每次展示前,从后台中获取。

    第一次打开显示时,默认显示第1页,第0条,共显示5条。

      <form id="listForm" action="list.action" method="post">
            姓名:<input type="text" name="name" style="120px"/> 
            手机:<input type="text" name="telphone" style="120px"/>
            地址:<input type="text" name="address" style="120px"/><br/><br/>
            状态:<select id="indexStatus" onchange="changeStatus()">
            <option value="-1" selected="selected">全部</option>
            <option value="1">启用</option>
            <option value="0">停用</option>
         </select>
         <input type="hidden" name="status" id="status" value="-1">
            创建日期:<input type="text" name="createtime"/>
         <input type="submit" value="搜索" style="background-color:#173e65;color:#ffffff;70px;"/> <br/>
         <!-- 显示错误信息 -->  
         <c:if test="${errorMsg}">   
             <font color="red">${errorMsg}</font><br/>
         </c:if> 
         <input type="hidden" name="startPage" id="startPage" value="${startPage}"/>
         <input type="hidden" name="currentPage" id="currentPage" value="${currentPage}"/>
         <input type="hidden" name="pageSize" id="pageSize" value="${pageSize}"/>
         <input type="hidden" name="sumPageNumber" id="sumPageNumber" value="${sumPageNumber}"/>
         <input type="hidden" name="countNumber" id="countNumber" value="${countNumber}"/>
      </form>

    在下方输入页码,点击go后。执行toLocationPage方法。

       <div style="margin-top: 10px;">
           <a onclick="toPrePage()">上一页</a><a onclick="toNextPage()">下一页</a>
           <input type="text" id="pageNumber" style="50px">
           <button onclick="toLocationPage()">go</button>
           <div id="pageInfo"></div>
       </div>

    js方法如下:

           function toLocationPage(){
               //获取要跳转到的页码
               var pageNumber = document.getElementById("pageNumber").value;
               //获取当前页码这个对象
               var currentPageObject = document.getElementById("currentPage");
               //取出当前页码的值
               var currentPage = currentPageObject.value;
               //进行非空校验
               if(pageNumber==null||pageNumber==""){
                   alert("请输入要跳转的页数!");
               }else{
                   pageNumber = parseInt(pageNumber);
                   //取出总页码数
                   var sumPage = parseInt(document.getElementById("sumPageNumber").value);
                   if(pageNumber<1){
                       alert("数据已到顶!");
                   }else if(pageNumber>sumPage){
                       alert("数据已到底!");
                   }else{
                       var pageSize = parseInt(document.getElementById("pageSize").value);
                       // 取出开始的数据下标,默认进入第一面时为0
                       var startPageObject =document.getElementById("startPage");
                       // 如果要跳转的页码大于当前页码,逻辑如下【注意:currentPage不同于currentPageObject.value,currentPage为1】
                       if(pageNumber>=currentPage){
                           //开始的下标+页码差*每页条数
                           startPageObject.value = parseInt(startPageObject.value)+pageSize*(pageNumber-currentPage);
                       }else if(pageNumber<currentPage){
                           startPageObject.value = parseInt(startPageObject.value)-pageSize*(currentPage-pageNumber);
                       }
                       // 变更当前页码对象的值为要跳转到的页面,相当于公共的变量值,以后后续使用
                       currentPageObject.value = pageNumber;
                       document.getElementById("listForm").submit();
                   }
               }
           }

     

     

  • 相关阅读:
    算法|LeetCode之找零钱[Java]
    算法|LeetCode之最大子段和[Java]
    算法|LeetCode之打家劫舍[Java]
    设计模式|观察者模式
    设计模式|原则—依赖倒置原则
    设计模式|原则—开闭原则
    数据库|MyBatis项目配置案例详解与Web下的增删改查实现[附项目源码]
    数据库|深入浅出MYSQL数据库—思维导图[附下载链接]
    算法|深度优先搜索(DFS)与广度优先搜索(BFS)的Java实现
    Java|单向链表的实现
  • 原文地址:https://www.cnblogs.com/kunpengv5/p/10025069.html
Copyright © 2020-2023  润新知