生活中,当你闲暇之余浏览资讯的时候,当你搜索资料但繁杂信息夹杂时候,你就会想,如何更为准确的定位需求信息。今天就为你带来:

分页查询

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

数据分析:

通过观察,页面上需要显示下面的几个数据:
当前页: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="width: 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="width: 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中的数据进行显示

读到这里,你肯定对多条件过滤功能有所了解呢?或者说知道该如何去实现这个功能了?如果有不明白的地方也可以通过留言或者私信。

教你用Java web实现多条件过滤功能的更多相关文章

  1. java web开发 图片上传功能

    基本思路在于,配置路径,然后用java I/O的api将图片上传到该目录下. String photoPath =    ServletActionContext.getServletContext( ...

  2. Java Web项目实现写日志功能

    第一步:导入log4j-1.2.16的jar包 第二步:在servlet包里编写写日志的servlet,代码如下: public class InitServlet extends HttpServl ...

  3. 【Java Web开发学习】Spring4条件化的bean

    [Java Web开发学习]Spring4条件化的bean 转载:https://www.cnblogs.com/yangchongxing/p/9071960.html Spring4引入了@Con ...

  4. 【原创】三分钟教你学会MVC框架——基于java web开发(2)

    没想到我的上一篇博客有这么多人看,还有几位看完之后给我留言加油,不胜感激,备受鼓励,啥都别说了,继续系列文章之第二篇.(如果没看过我第一篇博客的朋友,可以到我的主页上先浏览完再看这篇文章,以免上下文对 ...

  5. 【java项目实战】一步步教你使用MyEclipse搭建java Web项目开发环境(一)

    首先.在開始搭建MyEclipse的开发环境之前.还有三步工具的安装须要完毕,仅仅要在安装配置成功之后才干够进入以下的java Web项目开发环境的搭建. 1.安装工具 第一步,下载并安装JDK,到官 ...

  6. java web后台开发SSM框架(Spring+SpringMVC+MyBaitis)搭建与优化

    一.ssm框架搭建 1.1创建项目 新建项目后规划好各层的包. 1.2导入包 搭建SSM框架所需包百度云链接:http://pan.baidu.com/s/1cvKjL0 1.3整合spring与my ...

  7. java WEB开发入门

    WEB开发入门 1 进入web JAVASE:标准- standard   JAVA桌面程序 GUI    SOCKET JAVAEE:企业-浏览器控制  web 2 软件结构 C/S :client ...

  8. Java Web编程技术学习要点及方向

    学习编程技术要点及方向亮点: 传统学习编程技术落后,应跟著潮流,要对业务聚焦处理.要Jar, 不要War:以小为主,以简为宝,集堆而成.去繁取简 Spring Boot,明日之春(future of ...

  9. 零基础如何系统学习Java Web

    零基础如何系统学习Java Web?   我来给你说一说 你要下决心,我要转行做开发,这样你才能学成. 你要会打字,我公司原来有一个程序员,打字都是两个手一指禅,身为程序员你一指禅怎么写出的代码,半个 ...

随机推荐

  1. InitializingBean,spring 初始化bean

    springframework的提供接口,InitializingBean接口为bean提供了初始化方法的方式,它只包括afterPropertiesSet方法,凡是继承该接口的类,在初始化bean的 ...

  2. 使用grep过滤make的输出内容

    make的输出内容其实分为两种,有些是到标准输出,有些是到标准错误,由于标准输出和标准错误默认都是屏幕,所以平时区分不出来, 实际上一般是error和warning信息到标准错误,其余的到标准输出. ...

  3. 设计模式C++描述----18.中介者(Mediator)模式

    一. 举例 比如,现在中图和日本在关于钓鱼岛问题上存在争端.这时,联合国就会站出来,做为调解者,其实也没什么好调解的,钓鱼岛本来就是中国的,这是不争的事实!联合国也就是个传话者.发言人. 结构图如下: ...

  4. java迭代器 常用

    19 //使用迭代器遍历ArrayList集合 20 Iterator<String> listIt = list.iterator(); 21 while(listIt.hasNext( ...

  5. Spring Cloud gateway 网关服务 一

    之前我们介绍了 zuul网关服务,今天聊聊spring cloud gateway 作为spring cloud的亲儿子网关服务.很多的想法都是参照zuul,为了考虑zuul 迁移到gateway 提 ...

  6. Spring AOP 知识整理

    通过一个多月的 Spring AOP 的学习,掌握了 Spring AOP 的基本概念.AOP 是面向切面的编程(Aspect-Oriented Programming),是基于 OOP(面向对象的编 ...

  7. Kali Linux——迈向网络攻防

    自从进入大三的课程后,在已学的高数.线代.数论.概率论.信息论.通信等知识的技术上,开始了网络信息安全.网      络攻防的学习.俗话说得好,磨刀不误砍柴工,开始网络攻防之旅也势必要一个好的工具.然 ...

  8. MinIO 搭建

    MinIO 搭建 MinIO 是一个基于 Apache License v2.0 开源协议的对象存储服务.它兼容亚马逊 S3 云存储服务接口,非常适合于存储大容量非结构化的数据,例如图片.视频.日志文 ...

  9. Xshell和Xftp 安装及使用

    Xshell Xshell 是一个强大的安全终端模拟软件,它支持SSH1, SSH2, 以及Microsoft Windows 平台的TELNET 协议.Xshell 通过互联网到远程主机的安全连接以 ...

  10. 吐血推荐珍藏的Visual Studio Code插件

    作为一名Java工程师,由于工作需要,最近一个月一直在写NodeJS,这种经历可以说是一部辛酸史了.好在有神器Visual Studio Code陪伴,让我的这段经历没有更加困难.眼看这段经历要告一段 ...