简介:一套基于 jsp+servlet+jdbc 的学生请假信息管理系统课程设计,面向计算机相关专业大学生用于期末课设或毕业设计参考。系统按学生、辅导员、宿管员三类角色划分功能,涵盖学生个人信息查看、请假单填写与审核进度查询,辅导员端请假审核、晚归记录导出及学生/班级/课程信息管理,宿管员端则包含基础信息管理,整体业务流程完整、角色权限清晰。资源包共 117 个文件,以 JSP 页面为主,辅以 Java 源码、Class 编译文件、数据库 SQL、Jar 依赖库及少量 CSS/JS 资源,压缩包大小约 2.25MB;SQL 文件可供直接导入数据库,源码结构便于二次开发与功能扩展。已有 2404 人学习下载,适合需要快速上手 Servlet+JDBC 整合开发、理解请假审批流程或补充课程设计文档的读者使用。
1. 学生请假信息管理系统:为什么课程设计选 JSP+Servlet+JDBC 组合依然能打
学生请假信息管理系统,是 Java 课程设计里出现频率最高的题目之一。它的核心痛点很明确:手工请假流程繁琐,纸质单子容易丢,统计起来更是折磨。这个题目很适合用来串起 JSP 做页面展示、Servlet 做请求控制、JDBC 做数据库读写这三块技能。很多人会纠结要不要直接上 Spring Boot,但课程设计这个场景下,Servlet+JSP+JDBC 反而能把 Web 请求的底层逻辑看得更清楚。如果你正面临这门课设,想用最短时间做出一个能跑、能演示、能答辩的项目,这篇笔记会告诉你完整路径。
2. 技术选型拆解:Servlet 管控制、JSP 管展示,JDBC 管数据,为什么不直接上框架
2.1 课程设计场景下,传统三层比 Spring Boot 更合适
很多同学看到 Spring Boot 就眼热,觉得课设标题写上 Spring Boot 就上档次。但实际情况是,课程设计的评分重点往往是“你是否真的理解了 Web 开发的基本流程”。JSP+Servlet+JDBC 这个组合是 Java Web 的原始形态,一次请求从 JSP 页面发出,经过 Servlet 的 doGet/doPost 方法,再到 JDBC 代码操作数据库,没有任何框架帮你蒙住细节。老师问“Servlet 的生命周期是什么”“JDBC 连接怎么关闭”,你能直接答上来,这才是课设答辩的底气。
如果你已经会 Spring Boot,再回来写这三件套,反而要注意别把框架思维带进来。比如 Spring Boot 里你习惯用 @Autowired 注入 Service,但这里你要手动 new DAO 对象,手动在 finally 里关闭 Connection。这些动作虽然繁琐,但它还原了 Web 应用的原始面貌。从时间成本来看,这三个组件的学习曲线比 Spring Boot 全家桶短得多,对只修了 Java 和数据库课程的学生来说更友好。
2.2 项目目录结构和运行环境:Tomcat 9 + MySQL 5.7 的最小配置
先说我常用的课设环境组合:JDK 1.8,Tomcat 9.0,MySQL 5.7 或 8.0,Eclipse 或 IDEA 的 Dynamic Web Project 或者 Maven 项目。如果是纯手工课设,我建议直接用 IDEA 的 Java Enterprise 项目模板。核心的目录结构是下面这样的:
src/ ├── com/student/leave/ │ ├── dao/ # 数据访问层 │ │ ├── BaseDao.java │ │ ├── StudentDao.java │ │ └── LeaveDao.java │ ├── model/ # 实体类 │ │ ├── Student.java │ │ └── Leave.java │ ├── servlet/ # 控制器层 │ │ ├── LoginServlet.java │ │ ├── LeaveApplyServlet.java │ │ └── LeaveApproveServlet.java │ └── util/ │ └── DBUtil.java # JDBC 连接工具类 webapp/ ├── index.jsp ├── login.jsp ├── student/ │ ├── apply_leave.jsp │ └── my_leave.jsp ├── admin/ │ └── approve_leave.jsp └── WEB-INF/ ├── web.xml └── lib/ # mysql-connector-java.jar这个目录结构对应了 MVC 思想:model 是实体类,dao 负责数据操作,servlet 是控制器,jsp 是视图。注意 WEB-INF/lib 里要放 mysql-connector-java.jar,否则后面 JDBC 连接 MySQL 时会直接报 ClassNotFoundException。Tomcat 的 server.xml 里端口默认 8080,如果被占用可以改成 8081,这个改动在答辩前一定要确认,现场换端口容易慌。
2.3 一次请假申请的完整请求流转:从 JSP 表单到数据库的路径
理解了目录结构之后,最关键的是理清请求流转路径。我用最典型的“学生提交请假申请”来走一遍完整链路:
- 学生在
student/apply_leave.jsp表单里填写请假开始时间、结束时间、理由,点提交按钮。 - 表单的 action 指向 LeaveApplyServlet,method 为 POST。
- Tomcat 收到请求后,根据 web.xml 里的 url-pattern 或者 @WebServlet 注解找到对应的 Servlet 类。
- Servlet 的 doPost 方法调用
request.getParameter("startDate")等方法取出表单参数。 - 把参数封装成 Leave 实体对象,调用 LeaveDao 的 insert 方法。
- LeaveDao 内部用 JDBC 的 PreparedStatement 执行 INSERT SQL。
- Connection 来自 DBUtil 工具类,执行完成后在 finally 里关闭连接。
- 返回成功页面,通过
response.sendRedirect("my_leave.jsp")跳转。
这条链路每一步都有明确的职责。JSP 只负责收集和展示数据,不写业务逻辑;Servlet 只做参数接收和流程跳转,不写 SQL;DAO 只面对数据库,不关心页面长什么样。这样分层的好处是答辩时你可以清楚地说出“我用了 MVC 模式”。
注意:很多同学图省事把 SQL 写在 Servlet 里。课设验收时,老师如果让你现场改一个字段名,你会发现 Servlet 和 JSP 里到处都要改。把 SQL 收拢到 DAO 层,是成本最低的架构优化,没有之一。
3. 数据库设计及 JDBC 连接层:三张表、一个工具类、一套增删改查封装
3.1 核心表设计:学生表、请假表、审批人表,字段别贪多
学生请假信息管理系统的数据模型不复杂,但表结构设计得好不好,直接影响后续的 SQL 写起来舒不舒服。我通常只用三张表:学生表存登录和身份信息,请假表存申请内容与审批状态,审批人表存辅导员或管理员。
CREATE TABLE `student` ( id INT PRIMARY KEY AUTO_INCREMENT, student_no VARCHAR(20) UNIQUE NOT NULL, name VARCHAR(50) NOT NULL, class_name VARCHAR(50), password VARCHAR(50) NOT NULL ); CREATE TABLE `approver` ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(50) NOT NULL, role VARCHAR(20) ); CREATE TABLE `leave` ( id INT PRIMARY KEY AUTO_INCREMENT, student_id INT NOT NULL, start_date DATE NOT NULL, end_date DATE NOT NULL, days INT, reason VARCHAR(500), status TINYINT DEFAULT 0, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, approver_id INT, approve_comment VARCHAR(200), FOREIGN KEY (student_id) REFERENCES student(id), FOREIGN KEY (approver_id) REFERENCES approver(id) );字段设计上有几个习惯可以参考:days字段不建议在 Java 代码里手动算,直接插入前用 SimpleDateFormat 算好,或者查询时用 SQL 的DATEDIFF(end_date, start_date) + 1生成。status用 TINYINT 而不是 VARCHAR,因为数字状态在 SQL 里比较和统计都更高效。create_time设置 DEFAULT CURRENT_TIMESTAMP,插入时就不需要显式赋值。表名leave在 MySQL 里不是保留字,但为了保险我习惯写成反引号包裹的形式,避免某些版本数据库报语法错误。
3.2 JDBC 连接工具类:DriverManager 连接 MySQL 的完整写法
JDBC 连接 MySQL 是整套系统的基础,推荐写一个工具类 DBUtil,把驱动加载、连接获取、资源释放都收敛到一起,后面所有 DAO 都从这儿拿连接。
public class DBUtil { // useUnicode 和 characterEncoding=utf8 是中文不乱码的关键参数 private static final String URL = "jdbc:mysql://localhost:3306/leave_db" + "?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai"; private static final String USER = "root"; private static final String PASSWORD = "123456"; static { // MySQL 8.0 必须用 cj 版本的驱动类名 try { Class.forName("com.mysql.cj.jdbc.Driver"); } catch (ClassNotFoundException e) { e.printStackTrace(); } } public static Connection getConnection() throws SQLException { return DriverManager.getConnection(URL, USER, PASSWORD); } // 统一资源释放,避免连接泄漏 public static void close(Connection conn, PreparedStatement ps, ResultSet rs) { try { if (rs != null) rs.close(); } catch (SQLException e) { e.printStackTrace(); } try { if (ps != null) ps.close(); } catch (SQLException e) { e.printStackTrace(); } try { if (conn != null) conn.close(); } catch (SQLException e) { e.printStackTrace(); } } }这段代码里最容易被忽略的是 URL 参数。MySQL 8.0 之后驱动类名变成了com.mysql.cj.jdbc.Driver,而 MySQL 5.7 用的还是com.mysql.jdbc.Driver,写错直接 ClassNotFoundException。URL 里的serverTimezone=Asia/Shanghai不加的话,连接时会报时区错误;useSSL=false则是为了省去本地开发时的 SSL 证书告警。getConnection 每次新建物理连接,在课设这种低并发场景下完全够用,如果答辩被问到性能问题,就说“生产环境会用连接池,课设为了展示 JDBC 原理所以用 DriverManager”,这是加分回答。
3.3 BaseDao 封装:把增删改查写成通用方法,减少重复代码
每张表都写一套完整的 CRUD 很啰嗦,我习惯先封装一个 BaseDao,把通用的查询、更新逻辑抽出来,LeaveDao 和 StudentDao 里只需要写各自的 SQL 和参数即可。这里的关键是使用可变参数和 PreparedStatement,既能防 SQL 注入,又能让代码保持干净。
public class BaseDao { protected Connection conn; protected PreparedStatement ps; protected ResultSet rs; // 通用更新:参数按 SQL 中 ? 的顺序依次传入 protected int executeUpdate(String sql, Object... params) { int rows = 0; try { conn = DBUtil.getConnection(); ps = conn.prepareStatement(sql); for (int i = 0; i < params.length; i++) { ps.setObject(i + 1, params[i]); } rows = ps.executeUpdate(); } catch (SQLException e) { e.printStackTrace(); } finally { DBUtil.close(conn, ps, null); } return rows; } // 通用查询:返回 Map 列表,列名作为 key,兼容任意表结构 protected List<Map<String, Object>> executeQuery(String sql, Object... params) { List<Map<String, Object>> list = new ArrayList<>(); try { conn = DBUtil.getConnection(); ps = conn.prepareStatement(sql); for (int i = 0; i < params.length; i++) { ps.setObject(i + 1, params[i]); } rs = ps.executeQuery(); ResultSetMetaData md = rs.getMetaData(); while (rs.next()) { Map<String, Object> row = new HashMap<>(); for (int i = 1; i <= md.getColumnCount(); i++) { row.put(md.getColumnLabel(i), rs.getObject(i)); } list.add(row); } } catch (SQLException e) { e.printStackTrace(); } finally { DBUtil.close(conn, ps, rs); } return list; } }这两个方法的参数设计是核心。Object... params是可变参数,调用时直接executeUpdate(sql, studentId, startDate)就行,不用自己拼字符串,能有效防止 SQL 注入。executeQuery返回List<Map<String, Object>>,用列名做 key,这样不同表的查询结果都能用同一套代码处理,不用每个 DAO 都写一遍 ResultSet 遍历。这里特意用md.getColumnLabel(i)而不是getColumnName(i),是为了让 SQL 里的字段别名也能正常映射,后面 JSP 回显时省很多事。
4. Servlet 控制器与 JSP 视图交互:请假申请、审批流转和页面回显
4.1 请假申请 Servlet:doPost 里如何接收参数并组装实体
BaseDao 搭好了,这一章要落到的核心是 Servlet 和 JSP 的协同。先说学生提交请假这个最核心的流程。表单在apply_leave.jsp里,提交后发给LeaveApplyServlet,它的 doPost 方法代码如下:
@WebServlet("/applyLeave") public class LeaveApplyServlet extends HttpServlet { protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setCharacterEncoding("UTF-8"); HttpSession session = req.getSession(); Student student = (Student) session.getAttribute("student"); String startDate = req.getParameter("startDate"); String endDate = req.getParameter("endDate"); String reason = req.getParameter("reason"); Leave leave = new Leave(); leave.setStudentId(student.getId()); leave.setStartDate(Date.valueOf(startDate)); leave.setEndDate(Date.valueOf(endDate)); leave.setReason(reason); LeaveDao dao = new LeaveDao(); int result = dao.insertLeave(leave); if (result > 0) { resp.sendRedirect("student/my_leave.jsp"); } else { req.setAttribute("error", "申请失败,请重试"); req.getRequestDispatcher("student/apply_leave.jsp").forward(req, resp); } } }这里有三个细节值得讲。第一,req.setCharacterEncoding("UTF-8")必须放在所有getParameter之前,否则 POST 请求里的中文参数必然乱码。第二,Date.valueOf(startDate)是把页面传来的"2025-03-01"这类字符串转成 java.sql.Date 的标准写法,表单控件用<input type="date">时格式正好能匹配。第三,失败时用forward而不是sendRedirect,这样能把error属性带到 JSP 页面里回显给用户,而不是无声无息地跳走。提交成功后的sendRedirect则是必须的,它能打断刷新时的重复 POST 提交问题,这个后面避坑章还会展开。
4.2 审批与状态流转:UPDATE 语句实现待审批、通过、驳回
审批功能是管理系统里最能体现设计思路的部分。辅导员打开approve_leave.jsp页面,看到所有 status=0 的记录,每条记录后面有“通过”和“驳回”两个按钮,两个按钮共用同一个 ApproveServlet,用隐藏字段区分动作:
@WebServlet("/approveLeave") public class ApproveLeaveServlet extends HttpServlet { protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setCharacterEncoding("UTF-8"); String leaveId = req.getParameter("leaveId"); String action = req.getParameter("action"); String comment = req.getParameter("comment"); // 用数字状态机:0 待审批,1 通过,2 驳回 int status = "pass".equals(action) ? 1 : 2; int approverId = 1; // 实际从 session 里获取审批人 ID LeaveDao dao = new LeaveDao(); boolean ok = dao.updateStatus(Integer.parseInt(leaveId), status, approverId, comment); if (ok) { resp.sendRedirect("admin/approve_leave.jsp"); } else { req.setAttribute("error", "审批操作失败"); req.getRequestDispatcher("admin/approve_leave.jsp").forward(req, resp); } } }对应的 DAO 方法只需要一条 UPDATE:
public boolean updateStatus(int leaveId, int status, int approverId, String comment) { String sql = "UPDATE `leave` SET status=?, approver_id=?, approve_comment=? WHERE id=?"; int rows = executeUpdate(sql, status, approverId, comment, leaveId); return rows > 0; }这个简单的状态机设计,在答辩时可以讲出不少内容:状态用数字而非字符串,杜绝了拼写错误;每个状态转换都在同一处收口,后续如果加“已销假”状态,只需要改这里和页面枚举。如果你想让系统更真实,可以在 UPDATE 前加一层判断,比如“只有 status=0 的记录才能执行审批”,用一个WHERE id=? AND status=0就能防止重复审批覆盖前面审批人的意见。
4.3 JSP 页面回显与记录列表:JSTL+EL 表达式替代 Java 脚本段
很多课设的 JSP 页面里直接写<% for (...) { %>,能跑但可读性极差。推荐用 JSTL 的<c:forEach>配合 EL 表达式来展示请假列表,代码干净而且答辩时更有说服力。学生个人信息展示页面和请假记录列表都用同一套思路。
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <html> <body> <table> <tr><th>理由</th><th>开始日期</th><th>结束日期</th><th>天数</th><th>状态</th></tr> <c:forEach items="${leaveList}" var="leave"> <tr> <td>${leave.reason}</td> <td>${leave.startDate}</td> <td>${leave.endDate}</td> <td>${leave.days}</td> <td> <c:choose> <c:when test="${leave.status == 0}">待审批</c:when> <c:when test="${leave.status == 1}">已通过</c:when> <c:otherwise>已驳回</c:otherwise> </c:choose> </td> </tr> </c:forEach> </table> </body> </html>对应的 Servlet 查询端要在 doGet 里把数据放到 request 作用域:
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { HttpSession session = req.getSession(); Student student = (Student) session.getAttribute("student"); LeaveDao dao = new LeaveDao(); List<Map<String, Object>> list = dao.findByStudentId(student.getId()); req.setAttribute("leaveList", list); req.getRequestDispatcher("student/my_leave.jsp").forward(req, resp); }${leave.status}里的status能不能直接取出来,取决于 executeQuery 返回的 Map 的 key 是否和数据库列名一致。我在 BaseDao 里特意用getColumnLabel就是为了这个。如果你在 DAO 里返回的是 Leave 实体对象,EL 表达式同样生效,靠的是 getter 方法名,二者任选其一,但不要混用,否则 JSP 里要么字段全空,要么报属性找不到。
提示:如果用实体类,Leave 里必须有
getStatus()、getStartDate()这些方法;如果用 Map,注意列别名大小写。EL 表达式对这两者的查找规则不一样,这是新手最容易卡半小时的地方。
5. 课程设计避坑指南:乱码、驱动、路径、刷新重复提交、登录态,五个高频翻车点
做学生请假信息管理系统的课设,最折磨人的往往不是业务逻辑,而是环境配置和几个隐蔽的编码问题。这五个坑每一条都是历届课设被踩到烂的,按“现象 → 原因 → 解决”来说清楚。
5.1 POST 中文乱码:JSP 页面声明和 Servlet 编码不一致
现象:请假理由提交后,存进数据库成了问号,页面上回显也是乱码。
原因:JSP 页面明明写了pageEncoding="UTF-8",但 Servlet 里没调req.setCharacterEncoding("UTF-8"),Tomcat 默认按 ISO-8859-1 解码 POST 请求体。数据库连接 URL 里如果也没带characterEncoding=utf8,那即使 Servlet 侧对了,数据库存进去还是乱。
解决:三处必须一致。第一,Servlet 的 doPost 第一行加req.setCharacterEncoding("UTF-8");第二,JDBC URL 带useUnicode=true&characterEncoding=utf8;第三,数据库表和字段的字符集是 utf8mb4。这三处挨个检查完,乱码问题能根除,不要靠new String(bytes)那种粗暴方式去硬转,那只会把双重编码的字符搞得更乱。
5.2 JDBC 连接 MySQL 失败:驱动类名、URL 参数、jar 包版本三方都别错
现象:启动 Tomcat 后访问页面,控制台报ClassNotFoundException: com.mysql.jdbc.Driver,或者连接时抛时区相关异常。
原因:MySQL 5.7 和 8.0 的驱动类名不一样。8.0 之后改成了com.mysql.cj.jdbc.Driver,老教材里写的com.mysql.jdbc.Driver在 8.0 驱动下加载不了。URL 里缺少serverTimezone=Asia/Shanghai时,驱动会拒绝连接,因为服务器时区不确定。
解决:MySQL 8.0 就写Class.forName("com.mysql.cj.jdbc.Driver"),同时确保 WEB-INF/lib 下放的是mysql-connector-java-8.x.jar。项目里如果有旧版本的 jar 残留,一定要删干净再重新导入,否则两个驱动并存时还会报其他奇怪错误。驱动 jar 包版本和 MySQL 服务端版本要大致匹配,这是很多人忽略的点。
5.3 Tomcat 部署路径 404:contextPath 和 url-pattern 对不上
现象:表单 action 写的/applyLeave,提交后 404;或者按钮跳转到一个不存在的地址。
原因:IDEA 部署 Web 项目时,Tomcat 会给应用加一层上下文路径,比如http://localhost:8080/leave_system/。表单里写绝对路径/applyLeave,实际请求成了http://localhost:8080/applyLeave,落到了 ROOT 应用上,自然找不到你的 Servlet。
解决:表单的 action 写成${pageContext.request.contextPath}/applyLeave,这样会自动拼上项目上下文路径。Servlet 里跳转页面时则用resp.sendRedirect(req.getContextPath() + "/student/my_leave.jsp")。理解了 contextPath 这个概念之后,这个坑基本不会再踩,而且答辩时能顺手解释“为什么部署路径变了页面还能正常跳转”。
5.4 JSP 页面加载后自动刷新,导致请假记录重复提交
现象:为了“提交成功后页面自动刷新一次以显示最新记录”,在 apply_leave.jsp 里加了一行<meta http-equiv="refresh" content="0">,结果数据库里每提交一次就多出一条重复请假记录。
原因:meta refresh 在页面加载完成后会重新发出上一次的 POST 请求,Servlet 收到后再次执行了 insert。浏览器端的自动刷新和手动刷新一样,都会重放原请求,而 Servlet 没有做幂等处理。
解决:提交成功后的跳转改成resp.sendRedirect("my_leave.jsp"),不要留在原页面,更不要在提交页放 meta refresh。重定向之后地址栏变成查询页的 GET 地址,刷新只会重新查列表,不会再触发 insert。记住一个原则:所有插入或更新的请求处理完之后,一律走重定向,不要 forward 到同一个页面。
5.5 登录态掉失:session 与 Cookie 的隐蔽场景
现象:学生登录成功后进入首页,点任意功能链接,又被弹回 login.jsp,反复登录反复失效。
原因:session 靠客户端的 JSESSIONID Cookie 维持。如果浏览器启用了阻止第三方 Cookie 的隐私模式,或者你把session.setAttribute("student", student)写在了某个永远走不到的分支里,session 里就一直取不到值。另一个隐蔽场景是开发时多个窗口开着旧页面,Tomcat 重启之后 session 全部失效,被当成“登录过期”。
解决:登录成功后立刻调用session.setAttribute("student", student),然后在需要登录态的 Servlet 里统一做拦截校验,比如抽一个 BaseServlet,在它的 doGet/doPost 开头写if (session.getAttribute("student") == null) { resp.sendRedirect("login.jsp"); return; }。Tomcat 重启后 session 丢失是正常行为,不是代码 Bug,答辩时被问到要能说清楚 session 的生命周期。
6. 让系统更像个产品的三个小技巧:导出统计、动态 SQL、连接池替换
6.1 导出请假统计:用 CSV 响应头替代 POI,只加几行代码
答辩时如果能导出当月请假报表,演示效果会好很多。最省事的方案不是引 Apache POI,而是用 CSV,浏览器识别为 Excel 格式,不依赖额外 jar 包。在 Servlet 里设置响应头,然后逐行输出:
resp.setContentType("text/csv;charset=UTF-8"); resp.setHeader("Content-Disposition", "attachment;filename=leave_report.csv"); PrintWriter out = resp.getWriter(); out.println("学生姓名,班级,开始日期,结束日期,天数,状态");这是血泪经验:CSV 用 Excel 打开时中文容易乱码,解决方式是在输出第一行之前写一个 BOM 字符out.write(0xFEFF);,Excel 就能正确识别 UTF-8 编码,演示现场不至于尴尬。
6.2 动态 SQL 拼接:按班级、状态、时间范围过滤
实际使用中,“查看请假记录”往往需要多条件过滤,比如管理员想知道“软件2班最近一周有多少人请假”。这需要动态拼接 SQL,我一般用 StringBuilder 配合参数 List:
StringBuilder sql = new StringBuilder("SELECT * FROM leave l JOIN student s ON l.student_id=s.id WHERE 1=1 "); List<Object> params = new ArrayList<>(); if (className != null && !className.isEmpty()) { sql.append("AND s.class_name = ? "); params.add(className); } if (status >= 0) { sql.append("AND l.status = ? "); params.add(status); }WHERE 1=1是动态条件拼接的经典写法,每个后续条件都能直接用 AND 开头,不需要判断是不是第一个。关键点在于参数顺序必须和 SQL 里问号的出现顺序完全一致,这个顺序错了查询结果会非常诡异。这个技巧也可以作为“SQL 注入防御”的答辩素材。
6.3 连接池替换和验收自测,收尾前的最后两步
想再提升一点工程素养,可以把 DBUtil 的 DriverManager 换成 Druid 数据源,改动量只有 getConnection 那一小段,引入一个 jar、改一个静态代码块,20 行以内:
private static DruidDataSource dataSource = new DruidDataSource(); static { dataSource.setUrl("jdbc:mysql://localhost:3306/leave_db?serverTimezone=Asia/Shanghai"); dataSource.setUsername("root"); dataSource.setPassword("123456"); dataSource.setInitialSize(2); dataSource.setMaxActive(10); } public static Connection getConnection() throws SQLException { return dataSource.getConnection(); }交代码之前,我习惯做三个自测:清掉 Tomcat 的 work 目录再重新部署,防止旧 class 文件残留;用浏览器完整走一遍“登录 → 请假 → 审批 → 查看记录”主流程;把数据库服务停掉,确认页面上不会把异常堆栈直接抛给用户。这些步骤看起来琐碎,但每一条都在真实的答辩现场救过我。希望这份笔记能帮你在课程设计里少踩几个坑。
本文还有配套的精品资源,点击获取