教你用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="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中的数据进行显示
读到这里,你肯定对多条件过滤功能有所了解呢?或者说知道该如何去实现这个功能了?如果有不明白的地方也可以通过留言或者私信。