本期继0725继续对我们的学生列表进行升级
一.增加条件搜索框与各类按钮
在我们的jsp页面中进行编写
<div class="search">
<form action="">
<div>
<label for="stuId">学号</label>
<input type="text" id="stuId" name="stuId" placeholder="请输入学号" autocomplete="off">
</div>
<div>
<label for="name">姓名</label>
<input type="text" id="name" name="name" autocomplete="off">
</div>
<div>
<label for="sex">性别</label>
<select name="sex" id="sex">
<option value="">请选择</option>
<option value="男">男</option>
<option value="女">女</option>
</select>
</div>
<div class="layui-form-item">
<label>出生日期:</label>
<div class="layui-inline" id="birth-range">
<div class="layui-input-inline">
<input type="text" autocomplete="off" id="birth-start" class="layui-input"
placeholder="开始日期">
</div>
<div class="layui-form-mid">-</div>
<div class="layui-input-inline">
<input type="text" autocomplete="off" id="birth-end" class="layui-input"
placeholder="结束日期">
</div>
</div>
</div>
<div>
<label for="phone">手机号</label>
<input type="text" id="phone" name="phone" autocomplete="off">
</div>
</form>
</div>
<%--按钮区--%>
<div class="action">
<button id="search">查询</button>
<button id="add">新增</button>
<button id="edit">修改</button>
<button id="del">删除</button>
<button id="reset">重置</button>
</div>
二.引入layui进行美化
如何下载layui我在这里就不作过多赘述了
在jsp前端中引入layui的css与js
<link rel="stylesheet" href="assets/lib/layui/css/layui.css">
<script src="assets/lib/layui/layui.js"></script>
在我们自己的js文件中编写js代码用于激活layui样式
//渲染日期时间范围
layui.use(() => {
let laydate = layui.laydate;
laydate.render({
elem: "#birth-range",
range: ["#birth-start", "#birth-end"]
});
});
三.给“重置”增加功能
重置按钮的作用应该是点击后使搜索框内的数据全部清除
在js中编写代码
//重置按钮事件
$("#reset").click(function (){
$("#stuId").val("");
$("#name").val("");
$("#sex").val("");
$("#birth-start").val("");
$("#birth-end").val("");
$("#phone").val("");
params={
stuId:"",
name:"",
sex:"",
birthStart:"",
birthEnd:"",
phone:""
}
});
或者用对DOM对象操作,使用reset函数重置表单,【0】找到#serch-form下的第一个元素,这里是form元素,我们就可以调用DOM中form的原生函数reset进行重置
$('#reset').click(function(){
$('#serch-form')[0].reset();
})
四.增加搜索功能
1.完成js中搜索按钮的功能
//查询按钮事件
$("#search").click(function (){
let stuId = $("#stuId").val();
let name = $("#name").val();
let sex = $("#sex").val();
let birthStart = $("#birth-start").val();
let birthEnd = $("#birth-end").val();
let phone = $("#phone").val();
params = {
stuId,
name,
sex,
birthStart,
birthEnd,
phone
};
console.log(stuId, name, sex, birthStart, birthEnd, phone);
findAll(1,limits,params);
});
2.将搜索栏中输入的信息传给后端
为findAll函数新增加一个形参params用于接收条件,在ajax中使用
function findAll(page = 1, limit = 10, params = {}){
$.ajax({
url : ctx+ "/student/list",
method:"post",
data:{
page,
limit,
...params
},
...params代表将params展开使用
findAll接受到参数后,再以post请求发送给servlet
servlet新建对象存储params
//前端提交的查询条件
String stuId = req.getParameter("stuId");
String name = req.getParameter("name");
String sex = req.getParameter("sex");
String birthStart = req.getParameter("birthStart");
String birthEnd = req.getParameter("birthEnd");
String phone = req.getParameter("phone");
在模型软件包model下新建搜索模型软件包search,创建StudentSearchBean类用于存放拓展模块,继承Student;
package com.situ.model.search;
import com.situ.model.Student;
import lombok.Getter;
import lombok.Setter;
import java.time.LocalDate;
@Setter
@Getter
public class StudentSearchBean extends Student {
private LocalDate birthStart;
private LocalDate birthEnd;
}
在servlet下创建搜索模型对象
如果搜索框内传入数据不为空,就将数据存入模型对象condition
StudentSearchBean condition = new StudentSearchBean();
if(StringUtils.hasText(stuId)) condition.setStuId(stuId);
if (StringUtils.hasText(name)) condition.setName(name);
if (StringUtils.hasText(sex)) condition.setSex(sex);
if (StringUtils.hasText(phone)) condition.setPhone(phone);
if (StringUtils.hasText(birthStart)){
LocalDate bs = LocalDate.parse(birthStart, DateTimeFormatter.ofPattern("yyyy-MM-dd"));
condition.setBirthStart(bs);
}
if (StringUtils.hasText(birthEnd)){
LocalDate be = LocalDate.parse(birthEnd, DateTimeFormatter.ofPattern("yyyy-MM-dd"));
condition.setBirthEnd(be);
}
五.向下传递,获取数据库中信息
让srevice,dao的findAll方法都接收新的形参StudentSearchBean
List<Student> findAll(Pagination pagination, StudentSearchBean ssb);
修改之前的sql语句,使用字符串拼接上where条件
将得到的数据以数组的形式存到args中
package com.situ.dao.impl;
import com.situ.common.Global;
import com.situ.dao.StudentDao;
import com.situ.model.Student;
import com.situ.model.search.StudentSearchBean;
import com.situ.util.Pagination;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.util.StringUtils;
import java.util.ArrayList;
import java.util.List;
public class StudentDaoImpl implements StudentDao {
private final RowMapper<Student> rowMapper = new BeanPropertyRowMapper<Student>(Student.class);
@Override
public List<Student> findAll(Pagination pagination, StudentSearchBean ssb) {
JdbcTemplate jdbcTemplate = Global.getTemplate();
List<Object> args = new ArrayList<>();//查寻参数
String select = "select id, stu_id, name, sex, birthday, pinyin, phone, email, qq, wechat from t_student";
StringBuilder where = new StringBuilder();
where.append(" where 1=1");
if(StringUtils.hasText(ssb.getStuId())) {
where.append(" and stu_id = ?");
args.add(ssb.getStuId());
}
if(StringUtils.hasText(ssb.getName())) {
where.append(" and name like ?");
args.add("%"+ssb.getName()+"%");
}
if(StringUtils.hasText(ssb.getPinyin())) {
where.append(" and pinyin like ?");
args.add("%"+ssb.getPinyin()+"%");
}
if(StringUtils.hasText(ssb.getSex())) {
where.append(" and sex = ?");
args.add(ssb.getSex());
}
if(StringUtils.hasText(ssb.getPhone())) {
where.append(" and phone like ?");
args.add("%"+ssb.getPhone()+"%");
}
if(ssb.getBirthStart()!=null) {
where.append(" and birthday >= ?");
args.add(ssb.getBirthStart());
}
if(ssb.getBirthEnd()!=null) {
where.append(" and birthday < ?");
args.add(ssb.getBirthEnd());
}
//查询记录总数
String coustsql = "select count(0) from ("+(select+where.toString())+") t";
//记录总数
Long count = jdbcTemplate.queryForObject(coustsql, Long.class, args.toArray());
pagination.setTotal(count);//设置总记录数
//分页查询sql
String limitsql = select + where.toString() + " limit ?, ?";
args.add(pagination.getOffset());
args.add(pagination.getLimit());
//执行查询操作
List<Student> students = jdbcTemplate.query(limitsql, rowMapper ,args.toArray());
return students;
}
}
六.数据返回给前端显示
七.显示当前页数,总页数,以及总条数,查询对象高亮
注意需要及时清除上一次创建的对象
//总页数和总条数
$(".paginate>ul>li.jump-btn").after("<li>"+"共"+currentPage+"/"+pages+"页 "+pi.total+"条"+"</li>");
在信息插入之前使用.replace将目标对象替换即可
stu.name = stu.name.replace(params.name,"<span style='background-color: yellow'>"+params.name+"</span>")
$tr.append("<td>"+ stu.name +"</td>")
$tr.append("<td>"+ stu.pinyin +"</td>")
$tr.append("<td>"+ stu.sex +"</td>")
$tr.append("<td>"+ stu.birthday +"</td>")
stu.phone = stu.phone.replace(params.phone,"<span style='background-color: yellow'>"+params.phone+"</span>")
$tr.append("<td>"+ stu.phone +"</td>")
八.注意事项
1.RowMapper实例化:
private final RowMapper<Student> rowMapper = new BeanPropertyRowMapper<>(Student.class);
RowMapper<Student>
: 这是一个接口,定义了如何将数据库查询结果集(ResultSet
)中的一行数据映射(转换)成一个特定的Java对象(在这里是Student
对象)。<Student>
是泛型,指定了这个映射器将行数据转换成Student
类型的对象。BeanPropertyRowMapper<Student>(Student.class)
: 这是Spring JDBC提供的RowMapper
接口的一个具体实现类。- 作用: 它利用Java的反射机制(Reflection)来自动完成映射工作。
- 映射规则:
- 列名匹配属性名: 它会检查
ResultSet
中每一列的列名(column name)。 - 转换为驼峰命名: 它会将数据库列名从
snake_case
(如student_id
,first_name
)自动转换为 Java Bean 的camelCase
属性名(如studentId
,firstName
)。这个转换是它智能的地方。 - 查找Setter方法: 它会查找
Student
类中与转换后的属性名匹配的setter
方法(例如setStudentId(...)
,setFirstName(...)
)。 - 设置属性值: 找到对应的
setter
方法后,它会调用该方法,将ResultSet
中当前行对应列的值作为参数传入,从而设置Student
对象的属性。
- 列名匹配属性名: 它会检查
Student.class
: 这个参数告诉BeanPropertyRowMapper
,它需要创建和填充的是Student
类的实例。它会尝试调用Student
类的无参构造函数来创建对象。
private final
: 这表示rowMapper
是一个私有的、不可变的(一旦初始化后就不能再指向其他对象)实例变量。通常在DAO类中这样定义,避免每次查询都创建新的映射器实例,提高效率。
2.获取记录总数:
Long count = jdbcTemplate.queryForObject(coustsql, Long.class, args.toArray());
pagination.setTotal(count);//设置总记录数
jdbcTemplate
: 这是 Spring Framework 提供的核心类,用于简化 JDBC 操作(如创建连接、执行SQL、处理异常、关闭资源等),让开发者专注于SQL本身。queryForObject(String sql, Class<T> requiredType, Object... args)
: 这是JdbcTemplate
的一个核心方法。String sql
(coustsql
): 传入要执行的 SQL 查询语句。这里的coustsql
应该是一个SELECT COUNT(*) ...
语句,目的是获取满足条件的总记录数。例如:"SELECT COUNT(*) FROM students WHERE name LIKE ?"
。Class<T> requiredType
(Long.class
): 指定查询期望返回的单一结果的数据类型。因为COUNT(*)
返回的是一个整数,通常用Long
来接收(避免int
溢出),所以这里传入Long.class
。JdbcTemplate 会尝试将数据库返回的值转换成这个类型。Object... args
(args.toArray()
): 这是一个可变参数列表,用于传递 SQL 语句中的占位符(通常是?
)所对应的参数值。args
很可能是一个List
或Collection
,存储了查询条件(比如搜索关键词、状态等)。toArray()
将其转换为Object
数组,以便JdbcTemplate
能正确地将这些值绑定到 SQL 语句的?
占位符上,有效防止SQL注入。- 返回值
Long count
: 该方法执行coustsql
,并返回查询结果集中第一行第一列的值,且该值被转换为Long
类型。对于COUNT(*)
查询,这就是我们想要的总记录数。
pagination
: 这通常是一个自定义的分页信息对象(例如PageInfo
,Page
,Pagination
等),用来封装分页所需的各种信息。setTotal(count)
: 调用这个对象的setTotal
方法,将上一步查询到的总记录数count
设置进去。这个总数对于前端计算总页数、显示“共X条记录”等信息至关重要。
3.执行分页查询并映射结果:
List<Student> students = jdbcTemplate.query(limitsql, rowMapper, args.toArray());
jdbcTemplate.query(...)
: 这是JdbcTemplate
另一个核心的查询方法,专门用于返回多行结果。String sql
(limitsql
): 传入要执行的 SQL 查询语句。这里的limitsql
应该是一个包含分页逻辑的SELECT
语句,通常使用LIMIT
(MySQL, PostgreSQL) 或ROWNUM
(Oracle) 或OFFSET ... FETCH
(SQL Server, PostgreSQL) 等子句来限制返回的行数和起始位置。例如:"SELECT * FROM students WHERE name LIKE ? LIMIT ? OFFSET ?"
。它查询的是当前页需要显示的具体数据。RowMapper<T> rowMapper
(rowMapper
): 传入之前定义好的RowMapper
实例。JdbcTemplate
在遍历ResultSet
的每一行时,都会调用这个rowMapper
的mapRow()
方法(BeanPropertyRowMapper
内部实现了此方法),将每一行数据转换成一个Student
对象。Object... args
(args.toArray()
): 和queryForObject
一样,传入SQL占位符所需的参数值数组。注意,limitsql
中的占位符数量和顺序需要与args
中的值匹配(可能比coustsql
多两个,分别对应LIMIT
和OFFSET
的值)。- 返回值
List<Student> students
: 该方法执行limitsql
,使用rowMapper
将查询结果集中的每一行都转换成一个Student
对象,然后将所有转换后的对象放入一个List
中并返回。最终得到的就是当前分页页面上需要显示的所有Student
对象的集合。