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

 

 

原文地址:https://www.cnblogs.com/kunpengv5/p/10025069.html