• 如何更准确过滤信息?看完本篇你就知道


    无论是使用Excel办公,还是浏览器搜寻关键字,都是需要通过条件过滤来实现。今天,手把手教你实现Java web项目——实现多条件过滤功能。

    分页查询

    需求分析:在列表页面中,显示指定条数的数据,通过翻页按钮完成首页/上一页/下一页/尾页的查询

    数据分析:

    通过观察,页面上需要显示下面的几个数据:
    当前页:currentPage
    页面大小:pageSize
    总页数:totalPage
    首页:1
    上一页:prevPage
    下一页:nextPage
    尾页:endPage
    总条数:totalCount
    结果集:result

     

    那么,我们应该如何方便快速的将这多个数据共享到页面上呢?答案是:封装

    我们应该将这几个参数封装到一个对象中,然后共享这个对象即可,所以,我们有了下面这个类

    @Getterpublic class PageResult {

        public static final  PageResult EMPTY_RESULT = new PageResult(Collections.EMPTY_LIST, 0, 1, 3);

        //1:两传

        private int currentPage;

        private int pageSize;

        

        //2:两查

        private List<?> result;

        private int totalCount;

        

        //3:三计算

        private int prevPage;

        private int nextPage;

        private int endPage;

        

        public PageResult(List<?> result, int totalCount, int currentPage, int pageSize){

            

            this.result = result;

            this.totalCount = totalCount;

            this.pageSize = pageSize;

            this.currentPage = currentPage;

            //计算

            this.endPage = totalCount % pageSize == 0 ?

                        totalCount / pageSize : totalCount / pageSize  + 1;

            this.prevPage = currentPage - 1 > 0 ? currentPage - 1 : 1;

            this.nextPage = currentPage + 1 > endPage ? endPage :currentPage + 1;

        }

    }

    在这个类中,我们提供了一个构造器来快速封装数据
    其中,endPage/prevPage/nextPage是通过上面的几个参数计算得来的

    在这些数据中,存在两个需要从数据库中查询得到的数据:总条数/结果集
    这两个数据我们需要下面两条SQL进行查询

    查询部门表中数据的总条数

    SELECT count(id) FROM department

    使用LIMIT关键字查询指定页面的数据

    SELECT  id, name, sn FROM department LIMIT #{start}, #{pageSize}

    #{start}:         使用(currentPage-1)*pageSize表达式计算出来的开始索引#{pageSize}: 每次查询的最大条数

    要执行这两条SQL,需要用户传递两个参数:currentPage和pageSize
    为了参数方便传递,我们将这两个参数封装到一个类中:QueryObject

    @Setter@Getterpublic class QueryObject {

            // 默认查询第一页的数据

        private int currentPage = 1;

            // 页面中默认显示10条数据

        private int pageSize = 5;

        public int getStart(){

            return (currentPage - 1)*pageSize;

        }

    }

    可以看出,查询结果集中的#{start}表达式,是访问查询对象中的getStart()方法来获取到计算得到的开始索引

    到此,我们都已经封装好了分页查询中最核心的两个类:
    QueryObject:封装用户传递过来的currentPage/pageSize
    PageResult:封装页面上显示需要的result/totalCount/currentPage/pageSize/totalPage/prevPage/pageSize

    有了这两个类,我们就可以在service中定义下面的方法,来处理分页查询的业务了:

     

        public PageResult query(QueryObject qo) {

            //查询表中数据的总条数

            int totalCount = dao.queryForCount(qo);

            //当查询到的总条数为0时,说明没有数据,此时就不应该再之后下面的查询

            //直接返回相应的默认值即可

            if (totalCount == 0) {

                return PageResult.EMPTY_RESULT;

            }

            List<Department> data = dao.queryForList(qo);

            PageResult result = new PageResult(data, totalCount, qo.getCurrentPage(), qo.getPageSize());

            return result;

        }

    该方法接收用户传递的数据(QueryObject),返回用户需要的数据(PageResult)
    通过调用dao中的两个方法执行两条SQL查询数据(总条数和结果集)

    <!--查询总条数--><select id="queryForCount" resultType="java.lang.Integer">

            SELECT count(id)

            FROM department

        </select><!--查询结果集-->

        <select id="queryForList" resultType="Department">

            SELECT

                id,

                name,

                sn

            FROM department

            LIMIT #{start}, #{pageSize}

        </select>

    然后将数据封装到PageResult对象中返回给表现层

    表现层获取到service中封装的PageResult对象后,共享到request作用域中
    然后请求转发到list.jsp页面

    protected void list(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

            //接收用户传递的currentPage和pageSize

            String currentPage = req.getParameter("currentPage");

            String pageSize = req.getParameter("pageSize");

            //将数据封装到QueryObject中,传递给service进行处理

            QueryObject qo = new QueryObject();

            if(!StringUtils.isNullOrEmpty(currentPage)){

                qo.setCurrentPage(Integer.valueOf(currentPage));

            }

            if(!StringUtils.isNullOrEmpty(pageSize)){

                qo.setPageSize(Integer.valueOf(pageSize));

            }

            PageResult result = service.query(qo);

            //共享获取到的PageResult对象

            req.setAttribute("result", result);

            req.setAttribute("qo", qo);

            //请求转发回到list.jsp页面

            req.getRequestDispatcher("/WEB-INF/views/department/list.jsp").forward(req, resp);

        }

    在list.jsp页面上,使用EL+JSTL获取数据并显示在对应的位置
    部门列表

    <c:forEach items="${result.data}" var="entity" varStatus="vs">

      <tr>

          <td>${vs.count}</td>

          <td>${entity.name}</td>

          <td>${entity.sn}</td>

          <td>

              <a class="btn btn-info btn-xs" href="/department?cmd=input&id=${entity.id}">

                 <span class="glyphicon glyphicon-pencil"></span>编辑

             </a>

             <a href="/department?cmd=delete&id=${entity.id}" class="btn btn-danger btn-xs">

               <span class="glyphicon glyphicon-trash"></span>删除

             </a>

         </td>

      </tr></c:forEach>

    分页条

    <div style="text-align: center;">

         <a href="javascript:;" onclick="goPage(1)">首页</a>

         <a href="javascript:;" onclick="goPage(${result.prevPage})">上一页</a>

         <a href="javascript:;" onclick="goPage(${result.nextPage})">下一页</a>

         <a href="javascript:;" onclick="goPage(${result.endPage})">尾页</a>

     

         当前页: ${result.currentPage} / ${result.endPage}

     

         跳转到第: <input id="currentPage" name="currentPage" style=" 80px; text-align: center;"

                      type="number" min="1" max="${result.endPage}" value="${result.currentPage}"/> 页

         <input type="submit" value="GO"/>

         每页显示:

         <select name="pageSize" onchange="goPage(1);">

             <option ${qo.pageSize==5?"selected='selected'":""}>5</option>

             <option ${qo.pageSize==10?"selected='selected'":""}>10</option>

             <option ${qo.pageSize==15?"selected='selected'":""}>15</option>

             <option ${qo.pageSize==20?"selected='selected'":""}>20</option>

         </select>

         条</div>

    效果如下

     

    说明:
    在点击翻页的时候,通过执行相应的JS代码提交表单来发起请求
    将需要查询的当前页的值设值给表单中的id为currentPage的输入框,然后提交表单
    目的主要是和后面的高级查询进行合并使用

    做到这里,我们部门的分页功能就完成了
    因为部门的字段比较少,所以,在这个模块中,没有设计高级查询的功能,这个功能我们在员工模块中再去实现
    接下来,我们来看看员工模块相应功能的实现

    在完成部门的CRUD和分页查询后发现,其他模块的这些功能基本相似
    不同之处主要在于字段不同而已,所以,在这里,我们主要对这些不同点进行说明,其他的按照前面的实现即可

    首先,来看看员工的表结构

     

    在该表中,前六个字段都是基本的字段,第七个(dept_id),这个字段是关联部门的外键列
    所以,待会儿我们在完成CRUD的过程中,需要注意维护该字段的值

    清楚表结构之后,我们来对员工的CRUD做一个简单的分析

    查询功能:

     

    可以看到,处理部门这一列显示的数据比较特殊之外,其他的都是基本的数据展示而已
    什么是特殊?什么是不特殊?
    员工除了部门的信息外,其他的数据都是直接来自于数据库,而部门在表中只存储了对应的编号,但是页面上需要显示部门的名称,那么这个问题我们是怎么解决的呢?

    好,首先解释一下,这个问题的解决方案在目前我们的web阶段还没有涉及过,所以,我在这只能点到为止

    我们的解决方案是:在执行该数据的查询的时候,使用多表查询,将员工及其所在部门的信息查询出来,SQL如下:

    SELECT

                e.id,

                e.name,

                e.password,

                e.email,

                e.age,

                e.admin,

                e.dept_id,

                d.id   d_id,

                d.name d_name,

                d.sn   d_sn

    FROM employee e LEFT JOIN department d on e.dept_id = d.idlimit #{start}, #{pageSize}

    这条SQL能够查询到如下的结果

     

    然后在resultMap中如下的配置,完成数据的封装,员工相关的数据封装到Employee对象中,部门相关的数据封装到Department对象中

    <resultMap id="BaseResultMap" type="Employee">

            <id column="id" jdbcType="BIGINT" property="id"/>

            <result column="name" jdbcType="VARCHAR" property="name"/>

            <result column="password" jdbcType="VARCHAR" property="password"/>

            <result column="email" jdbcType="VARCHAR" property="email"/>

            <result column="age" jdbcType="INTEGER" property="age"/>

            <result column="admin" jdbcType="BIT" property="admin"/>

            <!-- 一对多关系 -->

            <association property="dept" javaType="Department">

                <id column="d_id" property="id"/>

                <result column="d_name" property="name"/>

                <result column="d_sn" property="sn"/>

            </association>

        </resultMap>

    最后,在select元素中使用resultMap来完成映射

    <select id="queryForList" resultMap="BaseResultMap">

    完成这些操作之后,我们获取到的每个员工及其所在的部门信息就封装好了,那么,在JSP页面中使用EL表达式,就能够获取到当前员工所在部门的相关信息了

    <td>${entity.dept.name}</td>

    查询功能分析到此结束,其他功能和部门的一致

    新增功能:

    需求分析:
    保存用户的相关信息,包括用户所在部门的信息
    通过对员工表的观察发现,表中关联了部门的主键信息,来说明当前员工所在的部门
    所以,在保存员工的时候,需要为员工设置所在的部门.页面设计如下

     

    为员工设置所在的部门,用户只需要进行选择即可,然后将选中部门的id传递到服务端
    那么问题来了,如何将部门id通过下拉框传递到服务端呢?请看下面的分析

    protected void input(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

            List<Department> departments = departmentService.list();

            req.setAttribute("departments", departments);

            String id = req.getParameter("id");

            if (!StringUtils.isNullOrEmpty(id)) {

                Employee employee = service.get(Long.valueOf(id));

                req.setAttribute("employee", employee);

            }

            req.getRequestDispatcher("/WEB-INF/views/employee/input.jsp").forward(req, resp);

        }

    <select class="form-control" id="dept" name="deptId">

         <c:forEach var="d" items="${departments}">

             <option value="${d.id}" >${d.name}</option>

         </c:forEach>

     </select>

    首先,在跳转到该页面之间,我们先将所有的部门信息查询到
    然后在页面上循环遍历,生成对应的option元素,将部门的id作为option的value(提交的数据),将部门的name作为option的文本内容(显示的数据)
    最后,在用户选择对应选项后,提交表单,会将对应option元素的value提交到后台,完整数据如下:

     

    通过上图,可以清晰的看到,我们已经将完整的数据提交到后台
    后台获取到这些数据之后,就能够将其保存到数据库中
    那么,我们的保存功能就算完成了

    更新功能:

    更新和保存只有一个地方不同,就是需要数据回显
    而数据回显中,我们只对部门(下拉框)和超级管理员(复选框)做一个说明,其他的因为都是普通的input元素,使用value属性显示数据即可
    而下拉框和复选框需要单独进行处理,如下:

    <select class="form-control" id="dept" name="deptId">

        <c:forEach var="d" items="${departments}">

            <option value="${d.id}" ${employee.dept.id==d.id?"selected":""}>${d.name}</option>

        </c:forEach></select>

     

    <input type="checkbox" id="admin" name="admin" ${employee.admin?'checked':''}>

    这里,我们选择使用EL表达式的三元运算符进行判断,为下拉框添加selected属性,为复选框添加checked属性

    编辑的时候,数据能回显,接下来的操作和新增一致

    删除功能

    和部门的删除一致,这里就不再赘述

    到此,员工的CRUD结束

    高级查询

    功能需求:
    输入关键字和部门信息进去过滤查询,关键字是根据姓名和邮箱两个字段查询

    页面设计

     

    高级查询效果图

    当用户输入关键字"赵"和部门"总经办"后,在列表中显示的查询结果则为所有总经办姓赵的员工

    那么此时应该执行下面的SQL来查询相应的数据

    SELECT

      e.id, e.name, e.password, e.email, e.age, e.admin, e.dept_id, d.id d_id, d.name d_name, d.sn d_sn

    from

      employee e

    LEFT JOIN

      department d

    ON

      e.dept_id = d.id

    WHERE

      (e.name LIKE concat('%',? ,'%') OR e.email LIKE concat('%',? ,'%')) AND e.dept_id = ? LIMIT ?, ?

    在该SQL中,WHERE后面的条件是根据用户传递的高级查询相关的参数拼接而来

    这里,我们需要解决两个问题:
    1.这里的多个高级查询的参数如何传递?
    对于这个问题,我们应该能够比较快的想到解决方案---数据封装,如下:

    @Setter@Getterpublic class EmployeeQueryObject extends QueryObject {

        private String keyword;  //按照员工名称与邮箱模糊查询

        private Long deptId = -1L;  //按照部门id查询

        

        //当keyword为null或者空字符串的时候,都视为null处理

        public String getKeyword(){

            return StringUtils.isEmpty(keyword) ? null : keyword;

        }

    }

    2.参数拿到后,如何拼接到对应的SQL中?
    使用mybatis中的动态SQL中提供的标签,在mapper映射文件中进行SQL的拼接

    <sql id="base_where">

        <where>

            <if test="keyword != null">

                AND( e.name LIKE concat('%',#{keyword} ,'%') OR e.email LIKE concat('%',#{keyword} ,'%'))

            </if>

            <if test="deptId > 0">

                AND e.dept_id = #{deptId}

            </if>

        </where>

    </sql>

    以上两个问题解决后,我们就可以根据用户传递过来的参数,执行对应的过滤查询的SQL
    最后,和分页查询的逻辑一样,将数据封装到PageResult中,和分页相关的数据一同返回到页面
    效果如[高级查询效果图]所示

    好了,基本功能是完成了,我们再来看看下面的问题:
    首先,在表单中输入下面的参数,然后查询

     

    然后点击下一页进行翻页

     

    可以看到,在点击翻页之后,我们不是在上面的基础上查询下一页的数据,而是查询到了所有的数据,WHY?
    其实很简单,来看看我们的请求参数,一切就清楚了
    在我们点击翻页的时候,传递的参数只有currentPage,并没有将之前的高级查询的参数一起传递到后台,所以执行了下面的SQL查询到上面的结果

    SELECT

      e.id, e.name, e.password, e.email, e.age, e.admin, e.dept_id, d.id d_id, d.name d_name, d.sn d_sn

    from

      employee e

    LEFT JOIN

      department d

    on

      e.dept_id = d.id limit ?, ?

    所以,要想在之前高级查询的基础上,继续进行分页查询,那么我们只有一个办法,就是在翻页的时候将高级查询和分页的参数一起提交到后台,拼接执行相应的SQL才行

    解决方案:
    使用JS来完成数据的提交(JS在目前阶段还未进行系统学习,所以,这里大家重点应该是放在我们要做的事情上,而不是怎么做)

    <script type="text/javascript">

        function goPage(currentPage) {

            //为表单中的currentPage输入框设值

            document.getElementById("currentPage").value = currentPage

            //提交表单

            document.forms[0].submit();

        }

    </script>

     

     

    翻页条

    <a href="javascript:;" onclick="goPage(1)">首页</a>

    <a href="javascript:;" onclick="goPage(${result.prevPage})">上一页</a

    <a href="javascript:;" onclick="goPage(${result.nextPage})">下一页</a

    <a href="javascript:;" onclick="goPage(${result.endPage})">尾页</a>

    在点击翻页按钮时,调用定义好的goPage函数,完成其中的两件事即可
    此时,我们可以看到问题已然解决

    最后,页面跳转和页面大小的设置功能,我们不做要求,如果要实现也很简单,我这里就直接把相关代码贴出来

    跳转到第: <input id="currentPage" name="currentPage" style=" 80px; text-align: center;"

                 type="number" min="1" max="${result.endPage}" value="${result.currentPage}"/> 页<input type="submit" value="GO"/>

    每页显示:<select name="pageSize" onchange="goPage(1);">

        <option ${qo.pageSize==5?"selected='selected'":""}>5</option>

        <option ${qo.pageSize==10?"selected='selected'":""}>10</option>

        <option ${qo.pageSize==15?"selected='selected'":""}>15</option>

        <option ${qo.pageSize==20?"selected='selected'":""}>20</option></select>

    好了,高级查询的功能实现到此结束,我们来做一个小结
    高级查询和分页的功能,我们应该重点分析两个点
    1.用户需要看到什么数据?
    2.这些数据应该执行什么样的SQL才能查询到?
    如果将这两个问题分析清楚了,那么大家就能够知道这个过程中所封装的几个类的作用了
    QueryObject:封装查询对象中通用的属性
    EmployeeQueryObject:封装高级查询相关的条件参数
    PageResult:封装用户需要使用到的数据

    所以实现步骤大致如下:
    1.获取到用户传递的高级查询和分页的参数,封装到对象的查询对象中
    2.从查询对象中取出数据,然后拼接SQL
    3.将查询之后得到的结果,封装到PageResult对象中
    4.页面获取到PageResult中的数据进行显示

    多条件过滤确实给我们带来了很多便利,我们coder确实需要去掌握这样的方法,才能让自己的项目更佳完善。最后呢,希望各位coder能够从分享中得到自己想要的,亦或者是一些启发或者共鸣。

  • 相关阅读:
    强化学习的基本迭代方法
    基于文本描述的事务聚类
    学习强化学习之前需要掌握的3种技能
    其它 华硕 ASAU S4100U 系统安装 win10安装 重装系统 Invalid Partition Table 解决
    数据分析 一些基本的知识
    Python 取样式的内容 合并多个文件的样式 自定义样式
    电商 Python 生成补单公司需要的评论格式3
    SpringBlade 本地图片上传 生成缩略图
    SQL Server 字符串截取
    SpringBlade 本地图片上传
  • 原文地址:https://www.cnblogs.com/chenmo2580/p/11960676.html
Copyright © 2020-2023  润新知