数据库连接说明
public String DBDRIVER = "com.mysql.jdbc.Driver";
public String DBURL = "jdbc:mysql://www.jxtxzzw.com:3306/addresslist";
public String DBUSER = "jxtxzzw_dev";
public String DBPASS = "jxtxzzw_dev";
如果是 MySQL 8.0 及以上版本,需要修改驱动 com.mysql.jdbc.Driver
为 com.mysql.cj.jdbc.Driver
并安装对应驱动。
数据库用的是我的服务器上的数据库,数据库地址是 www.jxtxzzw.com:3306,数据库是
addresslist
。 用户名和密码都是jxtxzzw_dev
,连接允许从任意主机发起(已开放安全组入方向、已允许任意主机连入),其余数据库账号和数据表已禁用远程访问。 如果需要配置本地数据库,可自行修改数据库地址、用户名和密码。
登录注销功能
如果只做一个假的登录功能,即点击登录按钮时跳转到登录页面,登录成功和失败分别跳转不同的页面,那么其实我直接在浏览器输入正确页面的 URL 也是可以访问的。
所以,需要状态管理。
登录、注销使用的是 session。
if (result) {
// 使用request对象的getSession()获取session,如果session不存在则创建一个
HttpSession session = request.getSession();
// 将数据存储到session中
session.setAttribute("username", adminname);
response.sendRedirect("../AdminMain.jsp");
} else {
response.sendRedirect("../LoginFail.jsp");
}
之后在所有授权页面只需要检查 session。
所有页面增加这个字段。
<%
String username = null;
session = request.getSession(false);
if (session != null)
username = (String) session.getAttribute("username");
if (username != null) {
out.println("用户登录成功,欢迎你:" + username);
%>
一些 HTML 代码,例如显示可操作的项目……
<%
} else {
out.println("请先登录");
}
%>
注销只需要手动设置 session 无效。
<body>
<center>
<%
session = request.getSession();
//手工调用session.invalidate方法,摧毁session
session.invalidate();
%>
<h2>管理员注销成功!</h2>
<table width="80%">
<tr>
<td><a href="AdminLogin.jsp">重新登录</a></td>
<td><a href="index.jsp">回到主页</a></td>
</tr>
</table>
</center>
</body>
查看校内所有教师信息
select * from teacher
因此直接查询,然后用 rs.next()
遍历所有的结果,依次打印。
直接 jsp 里面写代码运行就行了。
模糊搜索功能
select * from teacher where teacher_name like %?%
模糊查询就是用 like
功能,但是由于这必须要一次前后台通讯,必须点了提交以后把数据发到后台才行,所以表单的 action 是 servlet 的一个服务,然后在那里做好查询,把数据保存到一个 QueryResult 页面,并返回。
<form id="teacherquery" name="teacherquery" method="post"
action="TeacherQueryResult.jsp">
String sql = "select * from teacher where teacher_name like '%" + teacher_name + "%'";
if (con != null) {
rs = dm.executeQuery(sql);
}
添加、修改、删除教师信息
逻辑比较方便,就是获取所有用户输入的东西,然后往后台发送。
前端的展示就是罗列一堆文本框就好了。
……
<tr>
<td align=right>所在系:</td>
<td><select id="teacher_dept_id" name="teacher_dept_id"
title="所在系">
</select></td>
</tr>
<tr>
<td align=right>职称:</td>
<td><input name="teacher_title" type="text" title="职称" /></td>
</tr>
……
修改的时候只是文本框默认是有值的,默认值就从后台返回一个 Query,把指定教师原有的信息填充进去。
需要注意的是,教师 ID 应该被设置为只读。
修改和删除期望的操作位置应该是在罗列教师列表的页面,每一行增加一个操作按钮。
但是这个页面同时也是普通用户可以访问的,这意味着,要么会出现代码复制的现象,要么就要在这个页面中判断当前用户是不是管理员(是不是有合法的 session),然后进行分支选择,决定是否显示修改和删除按钮。
boolean adminLoggedIn = false;
String username = null;
session = request.getSession(false);
if (session != null)
username = (String) session.getAttribute("username");
if (username != null)
adminLoggedIn = true;
if (adminLoggedIn) {
out.println("<td><a href=" + "TeacherModify.jsp?teacher_id=" + rs.getString("teacher_id")
+ ">修改</a>");
out.println("<a href=" + "TeacherDelete.jsp?teacher_id=" + rs.getString("teacher_id")
+ ">删除</a></td>");
}
只有登录状态会增加这两列。
删除状态比较简单,传递 ID,然后直接执行删除。
同理,新增、修改、删除页面,必须判断是不是管理员。
session = request.getSession(false);
if (session != null)
username = (String) session.getAttribute("username");
if (username != null) {
out.println("用户登录成功,欢迎你:" + username);
String sql = "delete from teacher where teacher_id='" + teacher_id + "'";
if (con != null) {
if (dm.executeUpdate(sql) > 0)
out.println("删除成功");
else
out.println("删除失败");
}
} else {
out.println("请先登录");
}
同时,返回功能的实现也与用户有关,如果是未登录状态,则点击返回按钮会返回到主页,而登录状态下的返回按钮会返回到管理员的管理界面。
学院和学院编号
ID 是传递给后台的,前台应该展示学院的名字,因此做一个转换即可,前端显示的是名字,即 name
是名字,而 value
保留 ID。
但是如果只是显示系编号就非常简单。
如果需要显示系的名字而不是编号,也行,就是多做一次查询,根据系的 ID 去查名字。
但是要显示学院,就有点麻烦,需要嵌套比较多的查询次数。
当然也可以用自然连接,或者笛卡尔积等方法。
String sql = "select * from teacher join department on teacher_dept_id = dept_id";
out.println("<td>" + rs.getString("dept_name") + "</td>");
下一个问题是怎么查学院。
String sql = "select * from (teacher join department as d1 on teacher_dept_id = dept_id) join department as d2 on d1.father_id = d2.dept_id";
out.println("<th>学院</th>");
out.println("<th>系</th>");
out.println("<td>" + rs.getString("d2.dept_name") + "</td>");
out.println("<td>" + rs.getString("d1.dept_name") + "</td>");
注册 XML
如果遇到了 servlet,需要注册 XML。
管理员修改密码
与修改教师信息一样,首先判断是不是管理员登录,然后提供一个新的输入框,允许用户输入新密码,然后传递到后台。管理员用户名只读,不允许修改,并同时可以传递到后台方便处理。
简化了问题,没有做“重复密码”输入和校验。
学院和系的处理
看 father_id
是不是 0。
提供下拉框
下拉框比较简单。
<select id="dept_name" name="dept_name">
<option value="100">这是 A 系</option>
<option value="233">这是 B 系</option>
</select>
联动的下拉框
期望的结果应该是联动的下拉框。
必须先选学院,然后第二级的下拉框会根据学院的内容切换成那个学院下面的所有系。
然而,由于其他输入框内容没有填写,因此不能做后台交互,尽量全部前台完成。
当然,如果做后台 API 请求,那更好,但是为了简化问题(其实是懒得在这次作业上花太多时间),怎么方便怎么来,尽量前端处理。
前端处理的话,要用到 JavaScript。
逻辑比较方便,首先获取所有的系的信息,然后把所有的学院(father_id
为 0)添加到下拉框。
if (rs.getString("father_id").equals("0"))
out.println("<option value = \"" + rs.getString("dept_id") + "\">"
+ rs.getString("dept_name") + "</option>");
然后传递信息只需要传递 value
还是方便的。
关键是第 2 级的下拉框内容。
可以有一个 onchange()
,当第 1 级下拉框的内容改变的时候,自动触发这个函数。
于是,这个函数要做的事情就非常简单,就是把属于同一个学院的内容添加到下拉框。
由于后端不能获取前端的实时信息,而这里也不存在交互的可能(其实是懒得做),而前端可以获取后端的信息,所以信息流的方向就很明确了。
在之前,我开了一个 ArrayList,把所有的系的信息保留了下来,记录系编号、系名称、所属学院编号。
while (rs.next()) {
ArrayList list = new ArrayList();
list.add(rs.getString("dept_id"));
list.add(rs.getString("dept_name"));
list.add(rs.getString("father_id"));
dept_list.add(list);
if (rs.getString("father_id").equals("0"))
out.println("<option value = \"" + rs.getString("dept_id") + "\">"
+ rs.getString("dept_name") + "</option>");
}
于是,onchange()
函数需要同时处理前端和后端的代码。
遍历所有的系,如果所属学院的 ID 是一样的,添加到下拉框。
<script language="javascript">
function fatherDeptOnChange() {
var father_dept_id = document.getElementById("dept_father").value;
var dept_select=document.getElementById("teacher_dept_id");
dept_select.options.length=0; // 清空下拉框
<%int len = 0;
if (dept_list != null)
len = dept_list.size();
for (int i = 0; i < len; i++) {
ArrayList list = (ArrayList) dept_list.get(i);
String dept_id = (String) list.get(0);
String dept_name = (String) list.get(1);
String father_id = (String) list.get(2);%>
if(father_dept_id==<%=father_id%>){
dept_select.options.add(new Option("<%=dept_name%>","<%=dept_id%>"));
}
<%}%>
}
</script>
上面是添加部分的代码,修改部分也是类似的。
下拉框的默认值
修改部分的比较简单。
同样,在记录所有的 ArrayList 的时候,把符合条件的系添加到下拉框,并对其中完全匹配的条目设置为默认勾选。
if (rs2.getString("father_id").equals("0")) {
out.println("<option value = \"" + rs2.getString("dept_id") + "\"");
if (rs2.getString("dept_id").equals(father_id)) {
out.println(" selected ");
}
out.println(">" + rs2.getString("dept_name") + "</option>");
}
<tr>
<td align=right>所在系:</td>
<td><select id="teacher_dept_id" name="teacher_dept_id"
title="所在系">
<%
rs2.beforeFirst();
while (rs2.next()) {
if (rs2.getString("father_id").equals(father_id)) {
out.println("<option value = \"" + rs2.getString("dept_id") + "\"");
if (rs2.getString("dept_id").equals(rs.getString(3))) {
out.println(" selected ");
}
out.println(">" + rs2.getString("dept_name") + "</option>");
}
}
%>
</select></td>
</tr>
对于添加教师的页面,由于一开始不会触发 onchange()
,所以要有别的方法来触发,我这里选择了使用 body
的 onload
。
<body onload="fatherDeptOnChange()">
其他需要交代的事情
<% %>
的嵌套关系。尤其是需要注意 if 条件,哪些是在后端做的,哪些是在前端做的,for 循环的嵌套、大括号的嵌套。