思途JSP学习 0730
本期继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);@Overridepublic 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);//设置总记录数//分页查询sqlString 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
对象的集合。