简介:本资源是一套完整的Java Web毕业设计项目,面向计算机专业本科生及Java初学者,聚焦学校信息管理场景,提供从理论到落地的全流程实践材料。压缩包含论文全文(共32页,涵盖需求分析、技术选型、数据库设计、模块实现与系统测试等7章)、可运行的JSP+Servlet+MySQL源码、配套数据库文件及部署说明,覆盖登录、学生成绩录入、科目管理、学生信息维护等核心功能模块。资源共9.87MB,以RAR格式封装,主要包含.java、.jsp、.sql、.docx等类型文件,分别对应业务逻辑、前端页面、数据定义与文档说明,结构清晰便于学习与二次开发。已有4633人学习下载,适合课程设计参考、毕设开题借鉴或JavaWeb技术栈综合实训,尤其利于理解B/S架构下MVC分层实现与MySQL表关系设计。
1. 这不是又一个“学生管理系统”Demo:它是一套能直接跑通、改完就能交毕设的JavaWeb全栈落地方案
你手头正卡在毕业设计开题答辩前最后一周,导师说“系统要有登录、学生管理、成绩录入、科目维护四个核心模块,数据库得自己建,前后端得跑起来,不能是纯静态页面”——这时候搜到“学校信息管理系统 Java 毕业设计源码”,点进去发现一堆截图+模糊PDF+没注释的JSP文件,运行报错堆栈里全是ClassNotFoundException: com.mysql.jdbc.Driver、HTTP Status 404 – /login.jsp、java.lang.NullPointerException at StudentDaoImpl.java:42……这种资源,比没下载还糟心。
这份基于Java开发的学校信息管理系统,不是教学演示项目,而是按真实毕设交付标准打磨过的可执行体:含完整论文(Word可编辑)、MyEclipse工程源码(非Eclipse/IDEA迁移版)、MySQL 5.7兼容建库脚本(含初始测试数据)、Tomcat 7.0~8.5双环境适配说明、所有DAO层SQL语句带字段注释、JSP页面统一采用UTF-8编码且无scriptlet硬编码、登录校验含密码MD5加盐(盐值写在配置文件而非代码里)。它解决的不是“能不能跑”,而是“改三处就能过答辩、调两行就能换学校Logo、删一个if就能去掉权限分级”的落地问题。适合正在赶毕设 deadline 的计算机/软件工程本科生,也适合需要快速搭建教务原型的实训课教师——你要的不是技术原理,是今天下午拉下来、配好数据库、启动Tomcat、打开浏览器就能填学生姓名和成绩的确定性。
2. 技术栈选型不是拍脑袋:为什么用JSP+Servlet+MySQL而不是Spring Boot?
2.1 毕设场景下的技术合理性:轻量、可控、易答辩
高校毕设评审最常问三个问题:“这个技术你真懂吗?”“为什么不用更流行的框架?”“出问题你能自己修吗?”——Spring Boot虽新,但自动装配、starter依赖、内嵌Tomcat这些黑匣子,在答辩现场被问到“Bean生命周期怎么触发的”“@Transactional底层怎么代理的”,90%本科生答不全。而本系统采用JSP+Servlet+MySQL原生组合,每个环节都暴露在眼皮底下:
web.xml里明明白白写着<servlet>映射路径;StudentServlet.java中doPost()方法里request.getParameter("name")直接取参,没有@RequestParam注解遮挡;StudentDaoImpl.java里Connection conn = DriverManager.getConnection(url, user, pwd)一行代码把数据库连接逻辑摊开;- 所有SQL写在DAO方法里,没用MyBatis的XML或注解,避免“SQL写在哪?怎么查执行计划?”的追问。
这种“笨办法”反而成了答辩优势:你能指着代码说“这里我改了密码校验逻辑,把明文比对换成MD5加盐”,而不是背诵Spring Security的Filter链顺序。
2.2 MyEclipse+Tomcat组合的现实约束:兼容性比时髦更重要
当前主流IDE(IntelliJ IDEA、Eclipse 2023)默认支持Servlet 4.0+、JDK 11+,但高校机房、教师评审电脑普遍装着Windows 7 + JDK 1.7 + Tomcat 7.0.62——这不是怀旧,是真实硬件限制。本系统工程文件.project和.classpath明确指定:
org.eclipse.jdt.core.javabuilder绑定JDK 1.7;org.eclipse.wst.common.project.facet.core.builder启用jst.web:2.5(对应Servlet 2.5规范);org.eclipse.wst.jsdt.core.jsNature禁用(JSP项目不需要JS编译器);org.eclipse.m2e.core.maven2Builder未启用(零Maven依赖,所有jar包放在WEB-INF/lib下)。
这意味着你用MyEclipse 10(2012年发布)打开即用,无需升级IDE、降级JDK、手动排除冲突jar——省下的两小时,够你重画三张ER图。
2.3 MySQL 5.7的边界设定:为什么不用8.0的窗口函数?
系统数据库设计刻意避开MySQL 8.0特性(如CTE递归查询、JSON_TABLE、角色权限管理),全部使用5.7稳定语法:
- 表引擎统一为
InnoDB(支持事务,非MyISAM); - 字符集强制
utf8mb4(防微信昵称emoji乱码),但排序规则用utf8mb4_general_ci而非utf8mb4_0900_as_cs(后者需8.0+); - 所有时间字段用
DATETIME类型,不用TIMESTAMP自动更新(避免时区转换陷阱); - 外键约束显式声明(
CONSTRAINT fk_student_class FOREIGN KEY (class_id) REFERENCES class(id)),而非靠应用层维护。
这样做的结果是:你在阿里云轻量应用服务器(预装MySQL 5.7.32)上导入SQL脚本,source school_system.sql后立刻可用,不用查“MySQL 8.0 strict mode怎么关”。
2.4 B/S架构的朴素实现:没有Ajax轮询,只有表单提交+JSP跳转
系统交互完全遵循传统B/S范式:
- 登录页
login.jsp提交到LoginServlet,校验通过后response.sendRedirect("main.jsp"); - 成绩录入页
score_input.jsp用<form action="ScoreServlet" method="post">提交,Servlet处理完重定向回score_list.jsp; - 所有列表页(如
student_list.jsp)用<c:forEach>遍历request.getAttribute("studentList"),不走AJAX异步加载。
这种“落后”设计恰恰规避了毕设常见翻车点: - 不用调试Chrome Network面板看XHR请求是否401;
- 不用纠结jQuery版本与Bootstrap JS的兼容性;
- 不会因
fetch().then().catch()里没写console.error(e)导致错误静默。
答辩时老师点开F12看到Network里只有几个GET/POST,会点头说“结构清晰”——这比炫技更重要。
3. 数据库设计不是画ER图就完事:从概念模型到可执行SQL的四道坎
3.1 概念模型落地:为什么学生表要拆出“班级ID”而不是冗余班级名?
原始需求文档写“学生有姓名、学号、性别、班级、入学年份”,新手常把班级名直接存进student表:
CREATE TABLE student ( id INT PRIMARY KEY, name VARCHAR(20), no VARCHAR(15), gender CHAR(1), class_name VARCHAR(30), -- ❌ 冗余字段,违反第三范式 enroll_year YEAR );本系统采用规范化设计:
-- ✅ 班级独立成表,student表只存外键 CREATE TABLE class ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(30) NOT NULL, grade VARCHAR(10) COMMENT '年级,如高一、大二' ); CREATE TABLE student ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(20) NOT NULL, no VARCHAR(15) UNIQUE NOT NULL, gender ENUM('M','F') DEFAULT 'M', class_id INT NOT NULL, enroll_year YEAR NOT NULL, FOREIGN KEY (class_id) REFERENCES class(id) ON DELETE CASCADE );好处立竿见影:
- 修改班级名称只需更新
class表一行,不用UPDATE student SET class_name='高三(2)班' WHERE class_name='高三二班'; - 统计各班人数用
SELECT c.name, COUNT(s.id) FROM class c LEFT JOIN student s ON c.id=s.class_id GROUP BY c.id,不用字符串模糊匹配; - 删除班级时
ON DELETE CASCADE自动清理学生记录,避免孤儿数据。
提示:
ON DELETE CASCADE在毕设答辩中是加分项——说明你理解了参照完整性,不是只会DROP TABLE。
3.2 关键表字段设计:成绩表为什么用DECIMAL(5,2)而不是FLOAT?
成绩录入模块要求支持“95.5”“87.0”“缺考”三种状态,新手易犯两类错误:
- 用
FLOAT存储分数:INSERT INTO score VALUES (1, 101, 89.5)实际存成89.49999999999999,排序时ORDER BY score DESC出现95.5 < 95.499999的玄学现象; - 用
VARCHAR存“缺考”:导致AVG(score)计算时需CASE WHEN score='缺考' THEN NULL ELSE score END,SQL臃肿。
本系统采用DECIMAL(5,2)+状态字段组合:
CREATE TABLE score ( id INT PRIMARY KEY AUTO_INCREMENT, student_id INT NOT NULL, subject_id INT NOT NULL, score DECIMAL(5,2) NULL COMMENT 'NULL表示未录入,-1表示缺考', create_time DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (student_id) REFERENCES student(id), FOREIGN KEY (subject_id) REFERENCES subject(id), UNIQUE KEY uk_student_subject (student_id, subject_id) );参数说明:
DECIMAL(5,2):总宽度5位(含小数点),小数位2位,最大值999.99,覆盖0~100分+两位小数精度;score NULL:NULL表示该生此科未录入成绩,AVG()自动忽略;score = -1:程序逻辑中约定-1为“缺考”,前端显示“缺考”而非“-1”;UNIQUE KEY uk_student_subject:防止同一学生同一科目重复录入,比在Servlet里查重更可靠。
3.3 数据库连接配置:为什么把密码写在properties文件里而不是代码里?
src/db.properties内容如下:
driver=com.mysql.jdbc.Driver url=jdbc:mysql://localhost:3306/school_system?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8 user=root password=123456对应DBUtil.java中:
public class DBUtil { private static Properties props = new Properties(); static { try { props.load(DBUtil.class.getClassLoader().getResourceAsStream("db.properties")); } catch (IOException e) { throw new RuntimeException("加载db.properties失败", e); } } public static Connection getConnection() throws SQLException { String url = props.getProperty("url"); String user = props.getProperty("user"); String pwd = props.getProperty("password"); return DriverManager.getConnection(url, user, pwd); // ✅ 密码不硬编码 } }这样做有三个实际价值:
- 交毕设时,你把
db.properties里的password=123456改成password=your_real_password,其他同学拿你源码也连不上你的数据库; - 导师检查代码,看到
props.getProperty("password")会认可你有安全意识; - 部署到服务器时,只需替换
db.properties,不用重新编译class文件。
注意:
db.properties必须放在src/目录下(编译后位于WEB-INF/classes/),若放错位置getResourceAsStream()返回null,NullPointerException必现。
3.4 初始数据脚本:为什么INSERT语句要带字段名?
school_system.sql中插入测试数据的写法:
-- ✅ 显式指定字段,避免因表结构变更导致插入失败 INSERT INTO class (name, grade) VALUES ('高三(1)班', '高三'), ('高二(3)班', '高二'); INSERT INTO student (name, no, gender, class_id, enroll_year) VALUES ('张三', '2021001', 'M', 1, 2021), ('李四', '2021002', 'F', 1, 2021); INSERT INTO subject (name, credit) VALUES ('数学', 4), ('英语', 4), ('物理', 3); INSERT INTO score (student_id, subject_id, score) VALUES (1, 1, 92.5), (1, 2, 87.0), (2, 1, 85.5);对比错误写法:
-- ❌ 隐式字段顺序,一旦ALTER TABLE ADD COLUMN,整条INSERT崩掉 INSERT INTO student VALUES (null, '张三', '2021001', 'M', 1, 2021);血泪经验:某次答辩前夜,同学为加“照片路径”字段执行ALTER TABLE student ADD COLUMN photo_path VARCHAR(100),结果所有INSERT INTO student VALUES (...)全报错Column count doesn't match value count——因为新增字段在最后,但VALUES里没给值。显式字段名让DDL变更与DML解耦。
4. 核心模块实现:从登录校验到成绩录入的代码级拆解
4.1 登录模块:MD5加盐校验不是调API,是三步手写逻辑
LoginServlet.java关键代码:
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String username = request.getParameter("username"); String password = request.getParameter("password"); // Step 1: 从配置文件读取盐值(非硬编码) String salt = getSaltFromProperties(); // 返回"school2024" // Step 2: 密码+盐值MD5(用JDK自带MessageDigest,不依赖第三方库) String md5Password = MD5Util.getMD5(password + salt); // Step 3: 查询数据库(注意SQL注入防护:用PreparedStatement) String sql = "SELECT id, username, role FROM user WHERE username=? AND password=?"; PreparedStatement ps = conn.prepareStatement(sql); ps.setString(1, username); ps.setString(2, md5Password); ResultSet rs = ps.executeQuery(); if (rs.next()) { // 登录成功,存session HttpSession session = request.getSession(); session.setAttribute("userId", rs.getInt("id")); session.setAttribute("username", rs.getString("username")); session.setAttribute("role", rs.getString("role")); // 'admin' or 'teacher' response.sendRedirect("main.jsp"); } else { request.setAttribute("error", "用户名或密码错误"); request.getRequestDispatcher("login.jsp").forward(request, response); } }MD5Util.java实现(无依赖,JDK 1.7可用):
public class MD5Util { public static String getMD5(String input) { try { MessageDigest md = MessageDigest.getInstance("MD5"); byte[] digest = md.digest(input.getBytes("UTF-8")); BigInteger bigInt = new BigInteger(1, digest); return bigInt.toString(16).toUpperCase(); // 转大写十六进制 } catch (Exception e) { throw new RuntimeException("MD5加密失败", e); } } }参数说明与避坑点:
getSaltFromProperties()从src/config.properties读取,避免把盐值写死在代码里;input.getBytes("UTF-8")显式指定编码,防止Linux服务器默认ISO-8859-1导致中文密码加密结果不一致;BigInteger(1, digest)中参数1表示正数,避免高位为1时toString(16)产生负号;toUpperCase()确保生成32位大写字母+数字(如A1B2C3...),与MySQLLOWER(HEX(MD5('pwd'))) != 'a1b2c3...'匹配。
4.2 学生成绩录入:表单验证与后端校验的双重保险
score_input.jsp前端验证:
<form action="ScoreServlet" method="post"> <select name="student_id" required> <option value="">请选择学生</option> <c:forEach items="${studentList}" var="s"> <option value="${s.id}">${s.name}(${s.no})</option> </c:forEach> </select> <select name="subject_id" required> <option value="">请选择科目</option> <c:forEach items="${subjectList}" var="sub"> <option value="${sub.id}">${sub.name}(${sub.credit}学分)</option> </c:forEach> </select> <input type="number" name="score" min="0" max="100" step="0.01" placeholder="输入分数,如95.5" required> <button type="submit">提交成绩</button> </form>ScoreServlet.java后端校验:
// 1. 非空校验(防绕过前端) if (studentId == null || subjectId == null || scoreStr == null) { request.setAttribute("error", "参数缺失"); request.getRequestDispatcher("score_input.jsp").forward(request, response); return; } // 2. 数值范围校验(防恶意修改max属性) double score = Double.parseDouble(scoreStr); if (score < 0 || score > 100) { request.setAttribute("error", "分数必须在0~100之间"); request.getRequestDispatcher("score_input.jsp").forward(request, response); return; } // 3. 唯一性校验(防重复提交) String checkSql = "SELECT COUNT(*) FROM score WHERE student_id=? AND subject_id=?"; PreparedStatement checkPs = conn.prepareStatement(checkSql); checkPs.setInt(1, studentId); checkPs.setInt(2, subjectId); ResultSet checkRs = checkPs.executeQuery(); if (checkRs.next() && checkRs.getInt(1) > 0) { request.setAttribute("error", "该学生此科目成绩已存在,请先删除再录入"); request.getRequestDispatcher("score_input.jsp").forward(request, response); return; } // 4. 执行插入 String insertSql = "INSERT INTO score (student_id, subject_id, score) VALUES (?, ?, ?)"; PreparedStatement insertPs = conn.prepareStatement(insertSql); insertPs.setInt(1, studentId); insertPs.setInt(2, subjectId); insertPs.setDouble(3, score); insertPs.executeUpdate();关键设计点:
- 前端
required+min/max是用户体验,后端if (score < 0 || score > 100)是安全底线; COUNT(*)查重比SELECT id FROM score WHERE ...更高效,且避免NPE;- 错误信息
request.setAttribute("error", ...)传回JSP,用<c:if test="${not empty error}">显示,不弹alert——符合毕设界面规范。
4.3 科目管理模块:动态SQL拼接的安全写法
SubjectServlet.java中“按名称模糊查询”功能:
// ❌ 危险!字符串拼接SQL String keyword = request.getParameter("keyword"); String sql = "SELECT * FROM subject WHERE name LIKE '%" + keyword + "%'"; // ✅ 安全!PreparedStatement占位符 String sql = "SELECT * FROM subject WHERE name LIKE ?"; PreparedStatement ps = conn.prepareStatement(sql); ps.setString(1, "%" + keyword + "%"); // %放Java里,不在SQL里拼为什么这样更安全:
- 若用户输入
keyword='; DROP TABLE subject; --,拼接SQL变成SELECT * FROM subject WHERE name LIKE '%'; DROP TABLE subject; --%',直接执行删表; - PreparedStatement会把
%keyword%整体当字符串参数,'%' + keyword + '%'中的'不会被解析为SQL语法。
额外加固:在web.xml中配置<filter>过滤XSS:
<filter> <filter-name>XssFilter</filter-name> <filter-class>com.school.filter.XssFilter</filter-class> </filter> <filter-mapping> <filter-name>XssFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>XssFilter.java中对getParameter()返回值做HTML标签转义:value.replaceAll("<", "<").replaceAll(">", ">")。
4.4 学生管理模块:分页查询的内存友好实现
StudentServlet.java中列表分页:
int pageSize = 10; // 每页10条 int currentPage = 1; String pageParam = request.getParameter("page"); if (pageParam != null && !pageParam.trim().isEmpty()) { currentPage = Integer.parseInt(pageParam); } int offset = (currentPage - 1) * pageSize; // SQL分页(MySQL特有语法,非通用) String sql = "SELECT s.*, c.name as class_name FROM student s " + "LEFT JOIN class c ON s.class_id=c.id " + "ORDER BY s.id DESC LIMIT ? OFFSET ?"; PreparedStatement ps = conn.prepareStatement(sql); ps.setInt(1, pageSize); ps.setInt(2, offset); ResultSet rs = ps.executeQuery(); // 计算总页数(需单独查count,避免在分页SQL里嵌套子查询) String countSql = "SELECT COUNT(*) FROM student"; PreparedStatement countPs = conn.prepareStatement(countSql); ResultSet countRs = countPs.executeQuery(); int totalCount = 0; if (countRs.next()) totalCount = countRs.getInt(1); int totalPages = (int) Math.ceil((double) totalCount / pageSize);JSP分页导航(student_list.jsp):
<div class="pagination"> <c:if test="${currentPage > 1}"> <a href="?page=${currentPage-1}">上一页</a> </c:if> <c:forEach begin="1" end="${totalPages}" var="i"> <c:choose> <c:when test="${i == currentPage}"> <span class="current">${i}</span> </c:when> <c:otherwise> <a href="?page=${i}">${i}</a> </c:otherwise> </c:choose> </c:forEach> <c:if test="${currentPage < totalPages}"> <a href="?page=${currentPage+1}">下一页</a> </c:if> </div>避坑重点:
LIMIT ? OFFSET ?中?必须是整数,不能传字符串"10",否则报SQLException: Parameter index out of range;Math.ceil((double) totalCount / pageSize)用double转除避免整数除法截断(如15/10=1,实际需2页);class_name别名在JSP中用${s.class_name}取值,比<c:out value="${s['class_name']}"/>更简洁。
5. 避坑指南:运行报错、功能失效、答辩被问住的五大高频问题
5.1 现象:启动Tomcat后访问http://localhost:8080/login.jsp报404
原因:
login.jsp未放在WebContent/根目录下,而是放在WebContent/pages/login.jsp,但web.xml中未配置欢迎文件列表;- 或MyEclipse未正确部署:右键项目→Properties→MyEclipse→Web→Context root写成
/school,但浏览器访问/login.jsp(少/school前缀)。
解决:
- 确认
login.jsp路径为WebContent/login.jsp(非WebContent/jsp/login.jsp); - 检查
web.xml是否有<welcome-file-list>:
<welcome-file-list> <welcome-file>login.jsp</welcome-file> </welcome-file-list>- 启动Tomcat后,浏览器地址栏必须是
http://localhost:8080/school/login.jsp(school为Context root),若Context root为空,则用http://localhost:8080/login.jsp。
5.2 现象:登录时提示java.lang.ClassNotFoundException: com.mysql.jdbc.Driver
原因:
- MySQL 5.7+驱动类名已从
com.mysql.jdbc.Driver改为com.mysql.cj.jdbc.Driver,但代码里仍用旧类名; mysql-connector-java-5.1.47.jar未放入WEB-INF/lib/,或放入后MyEclipse未刷新Build Path。
解决:
- 下载
mysql-connector-java-5.1.47.jar(兼容5.7,非8.0版),复制到WebContent/WEB-INF/lib/; - 右键项目→Properties→Java Build Path→Libraries→Add JARs→选中该jar;
- 修改
DBUtil.java中Class.forName("com.mysql.cj.jdbc.Driver")(注意cj); url参数中添加&serverTimezone=GMT%2B8(防时区错误)。
5.3 现象:成绩录入后列表页显示null,数据库里score字段为NULL
原因:
score_input.jsp中<input name="score">未设value,提交时request.getParameter("score")返回null,Double.parseDouble(null)抛NumberFormatException,Servlet未捕获异常直接500;- 或
ScoreServlet.java中setDouble(3, score)传入NaN或Infinity。
解决:
- 在
ScoreServlet开头加全局异常捕获:
try { // 原有业务代码 } catch (NumberFormatException e) { request.setAttribute("error", "请输入有效数字"); request.getRequestDispatcher("score_input.jsp").forward(request, response); return; }- JSP中
<input>加默认值:<input type="number" name="score" value="${param.score}" ...>; - 数据库字段
score DECIMAL(5,2) NULL允许空值,但业务逻辑应保证非空。
5.4 现象:中文乱码,学生姓名显示为??
原因:
- MySQL数据库、表、字段未设
utf8mb4字符集; web.xml中未配置CharacterEncodingFilter;db.properties中URL缺少useUnicode=true&characterEncoding=utf8。
解决:
- 创建数据库时指定字符集:
CREATE DATABASE school_system CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;; web.xml添加过滤器:
<filter> <filter-name>CharacterEncodingFilter</filter-name> <filter-class>org.apache.catalina.filters.SetCharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> </filter> <filter-mapping> <filter-name>CharacterEncodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>db.properties中url必须含useUnicode=true&characterEncoding=utf8。
5.5 现象:答辩时老师点击“导出Excel”按钮无反应
原因:
- 系统根本没实现导出功能(原文档第5章未提,但老师可能随口问);
- 或用了
poi-3.9.jar但JDK 1.7不兼容(POI 4.0+需JDK 8+)。
解决(快速补救):
- 下载
poi-3.9.jar(JDK 1.7兼容),放入WEB-INF/lib/; - 新增
ExportServlet.java:
HSSFWorkbook wb = new HSSFWorkbook(); HSSFSheet sheet = wb.createSheet("学生成绩"); // 写表头... // 写数据... response.setContentType("application/vnd.ms-excel"); response.setHeader("Content-Disposition", "attachment; filename=score_export.xls"); wb.write(response.getOutputStream());student_list.jsp加按钮:<a href="ExportServlet?table=score">导出成绩Excel</a>。
血泪经验:答辩前夜被问“能导出吗?”,现场加这个功能,比解释“技术限制”更有说服力。
6. 毕设交付技巧:三处必改、两处必测、一处必留痕的实战清单
6.1 三处必改:让系统真正属于你,而非“下载即用”
改1:替换所有学校标识
main.jsp顶部<h1>XX学校信息管理系统</h1>→ 改为你校全称;login.jsp背景图bg.jpg替换成校徽PNG(尺寸保持1920×1080,用Photoshop导出为Web格式);footer.jsp中© 2024 XX学校→ 改为© 2024 ${pageContext.request.serverName}(动态获取域名,部署后自动显示localhost或school.yourdomain.com)。
改2:调整数据库连接参数db.properties中url的localhost改为实际IP(如192.168.1.100),root和123456改为你的MySQL账号密码;school_system.sql开头加DROP DATABASE IF EXISTS school_system; CREATE DATABASE school_system CHARACTER SET utf8mb4; USE school_system;,避免与他人数据库冲突。
改3:重命名工程与包名- MyEclipse中右键项目→Refactor→Rename,工程名从
school_system改为yourname_school_2024; src/com/school/包名改为src/com/yourname/school/(右键包→Refactor→Rename),同步修改所有package com.school.dao;为package com.yourname.school.dao;。
从那以后我每次交毕设源码,都强制走一遍这三改——不是为了糊弄,是让代码里每一处痕迹都指向“这是我写的”,答辩时老师问“这个DAO类为什么叫StudentDaoImpl”,我能答“因为我按自己命名习惯加了Impl后缀,不是抄模板”。
6.2 两处必测:答辩现场5分钟内能复现的关键路径
测1:登录→学生管理→新增学生→列表可见
- 步骤:
- 启动Tomcat,访问
http://localhost:8080/yourname_school_2024/login.jsp; - 输入
admin/admin(密码已MD5加盐,初始密码为admin); - 点“学生管理”→“新增学生”,填姓名“王五”、学号“2024001”、班级选“高三(1)班”;
- 提交后返回列表页,确认“王五”出现在第一行。
- 启动Tomcat,访问
- 验证点:若新增后列表无数据,立即查
student表SELECT * FROM student WHERE name='王五',确认是插入失败还是页面未刷新。
测2:成绩录入→按学生查成绩→平均分计算正确
- 步骤:
- 在“成绩录入”页,为“张三”选“数学”,输分数
95.5; - 提交后,点“成绩查询”→“按学生查”,选“张三”,确认显示
数学:95.5; - 手动计算:若张三还有“英语:87.0”,则平均分应为
(95.5+87.0)/2=91.25,页面“统计”模块应显示此值。
- 在“成绩录入”页,为“张三”选“数学”,输分数
- 验证点:若平均分显示
91.0,检查ScoreServlet.java中AVG(score)是否用了ROUND(AVG(score),2),避免数据库默认四舍五入。
6.3 一处必留痕:论文与代码的交叉验证证据链
毕设论文第4章“系统设计”中数据库表结构描述,必须与school_system.sql完全一致。我习惯在论文表格后加一行脚注:
表4-1 学生表字段说明(依据
school_system.sql第12行创建语句生成)
并在school_system.sql每张表CREATE TABLE上方加注释:
-- 【论文第4.3.2节对应】学生表,字段含义见论文P14表4-1 CREATE TABLE student ( ...更进一步:在论文“系统实现”章节截图时,用Windows自带截图工具(Win+Shift+S)截取MyEclipse Package Explorer视图,箭头标出src/com/yourname/school/dao/StudentDaoImpl.java,旁边写“DAO层实现,见源码第33行”。
这样做的效果是:答辩老师翻论文看到表结构,抬头看你屏幕上的SQL文件,再低头看你代码里的DAO方法——三者形成闭环证据链,他不会再问“这个表是你自己设计的吗?”。
希望帮到你。
本文还有配套的精品资源,点击获取