简介:本资源是一套完整的Java Web方向毕业设计实战项目,面向计算机相关专业本科生及Java初学者,聚焦教育场景下的学生成绩管理核心业务闭环。压缩包共9个文件,含4个MP4部署与功能演示视频、2个TXT说明文档、1个SQL数据库脚本、1个DOC系统文档及1个ZIP源代码包,总大小122.79MB;视频覆盖系统演示、代码结构解析、部署运行及中文乱码排错等关键环节,SQL与DOC分别提供可直接导入的数据库结构和详细功能说明,源码包已按MVC分层组织,便于理解Servlet+JSP+JDBC技术栈的协同逻辑。目前已有662人学习下载,项目完整包含从开发环境配置(Eclipse/IDEA+Tomcat)、MySQL建库建表、前后端交互实现到最终部署上线的全流程支撑材料,特别适合课程设计、毕设参考与Java Web工程能力筑基。
1. 这不是又一个“Java Web 课设Demo”:它真能跑通MySQL 8.0+中文乱码修复+Tomcat 9部署闭环,适合零基础硬啃毕业答辩前72小时的你
你手头那份被导师打回来三次、IDEA里红标满天飞、连登录页面都404的“学生成绩管理系统”,很可能缺的不是功能,而是一套能从score.sql建库开始、到http://localhost:8080/login弹出登录框为止的完整可执行链路。这个资源不是PPT式概念堆砌,它是一份带血丝的实战包:源码里StudentScore.zip已预编译好WEB-INF/classes,score.sql明确标注了CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci,部署视频第4集专门用Wireshark抓包验证JDBC连接串里的useUnicode=true&characterEncoding=UTF-8是否生效——这些细节,才是你答辩时被问“怎么解决中文存入数据库变问号”时,能掏出手机直接播放的底气。它不教你怎么写冒泡排序,只解决一件事:让你的毕设系统在答辩机器上,从解压到运行,全程不依赖导师远程协助。适合两类人:一类是Java刚学完Servlet但没碰过真实部署的本科生,另一类是想用现成结构快速搭起课程设计骨架、把精力聚焦在“成绩统计算法优化”或“Excel导出图表”等加分项上的同学。
2. 从源码结构到MVC分层:看清StudentScore.zip里每个包的真实职责,别再把DAO层代码塞进Servlet
这个项目虽是毕业设计级别,但MVC分层比很多企业级小项目更干净。我拆开StudentScore.zip后发现,它没用Spring Boot自动装配,而是用最原始的手动分层——这反而让你能看清每一层到底干啥。下面带你逐层穿透,重点标出和你IDEA里报错强相关的路径。
2.1 源码目录树:src/下三个核心包与WebContent/的映射关系
提示:别急着Run As Server!先确认你的Eclipse/IDEA里
WebContent是否被识别为Web Root(右键→Properties→Web Project Settings→Context root应为/StudentScore)。
StudentScore/ ├── src/ │ ├── dao/ # 数据访问对象:所有JDBC操作封装在此,如StudentDao.java │ │ └── StudentDao.java # 关键方法:public List<Student> getAllStudents() throws SQLException │ ├── model/ # 实体类:Student.java/Course.java/Score.java,含getter/setter+toString() │ ├── servlet/ # 控制器:LoginServlet.java/ScoreServlet.java,处理HTTP请求并调用DAO │ └── util/ # 工具类:DBUtil.java(含getConnection())、DateUtil.java(格式化时间) ├── WebContent/ │ ├── WEB-INF/ │ │ ├── web.xml # 配置Servlet映射:/login → LoginServlet,/score/list → ScoreServlet │ │ └── lib/ # 必须包含:mysql-connector-java-8.0.26.jar(注意版本!) │ ├── index.jsp # 首页重定向到/login │ └── login.jsp # 表单提交action="/StudentScore/login"(Context root必须匹配!)参数说明:
web.xml中<url-pattern>必须与JSP表单action完全一致,少个/StudentScore就404;DBUtil.java里private static final String URL = "jdbc:mysql://localhost:3306/scoredb?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8";——serverTimezone是MySQL 8.0+强制要求,漏写会报The server time zone value '...' is unrecognized;lib/下的JDBC驱动必须是8.0.x版本,若你用MySQL 5.7却配了8.0驱动,会报java.lang.ClassNotFoundException: com.mysql.cj.jdbc.Driver(旧版是com.mysql.jdbc.Driver)。
2.2 Model层:为什么Student.java里private String name;不能直接存入数据库?
这不是Java基础题,而是数据库设计陷阱。打开score.sql你会发现:
CREATE TABLE `student` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL, `gender` char(1) DEFAULT NULL, `class_id` int(11) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;关键点:CHARACTER SET utf8mb4而非utf8。MySQL的utf8实际只支持3字节UTF-8(不支持emoji),而utf8mb4才真正兼容4字节。如果你的数据库创建时用的是DEFAULT CHARSET=utf8,即使Java代码里写了UTF-8,存入中文也会变成??。解决方案不是改Java代码,而是重建数据库:
# 1. 删除旧库(谨慎!) mysql -u root -p -e "DROP DATABASE IF EXISTS scoredb;" # 2. 创建新库,强制指定字符集 mysql -u root -p -e "CREATE DATABASE scoredb CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;" # 3. 导入SQL(此时score.sql里的CREATE TABLE语句才能生效) mysql -u root -p scoredb < score.sql注意:
score.sql文件本身必须用UTF-8无BOM编码保存,否则Navicat导入时会乱码。用Notepad++打开→编码→转为UTF-8无BOM。
2.3 Servlet层:LoginServlet.java里request.getParameter("username")为何返回null?
这是新手高频翻车点。检查login.jsp中的form标签:
<!-- 错误写法:action写绝对路径 --> <form action="http://localhost:8080/login" method="post"> <!-- 正确写法:用相对路径+Context root --> <form action="${pageContext.request.contextPath}/login" method="post">request.getParameter()返回null的三大原因:
- 表单method与Servlet doPost/doGet不匹配:
login.jsp用method="post",但LoginServlet只写了doGet(); - input name属性缺失:
<input type="text" name="username">漏掉name="username",则getParameter("username")必为null; - Tomcat未启用POST参数解析:在
web.xml中确认没有<servlet><init-param><param-name>useBodyEncodingForURI</param-name><param-value>true</param-value></init-param>这类错误配置(此配置仅用于GET请求,POST应由容器自动处理)。
3. JDBC连接池与数据库配置:为什么DBUtil.getConnection()总超时?三步定位真实瓶颈
这个项目没用Druid或HikariCP,而是用最朴素的DriverManager.getConnection()——好处是代码透明,坏处是每次请求都新建连接,高并发下必然卡死。但毕业答辩场景下,连接超时90%不是代码问题,而是环境配置黑洞。我们按优先级排查:
3.1 第一步:验证MySQL服务是否真在监听3306端口
别信“服务已启动”,用命令行实锤:
# Windows netstat -ano | findstr :3306 # Linux/macOS sudo lsof -i :3306如果无输出,说明MySQL根本没起来。常见原因:
- MySQL服务未设置开机自启(Windows需在服务管理器中启动
MySQL80服务); - 配置文件
my.cnf中bind-address = 127.0.0.1被注释,导致只监听本地回环,但localhost解析可能走IPv6(::1),需改为bind-address = 0.0.0.0或显式写127.0.0.1; - 防火墙拦截:Windows Defender防火墙需放行3306端口(入站规则→新建规则→端口→TCP 3306)。
3.2 第二步:测试JDBC连接串能否直连(绕过Java)
用MySQL命令行验证连接串有效性:
# 格式:mysql -h [host] -P [port] -u [user] -p[password] [database] mysql -h 127.0.0.1 -P 3306 -u root -proot123 scoredb如果报错Access denied for user 'root'@'localhost',说明密码错误或用户权限不足。毕业设计默认账号密码通常写在README.txt里(本项目中为root/root123),但MySQL 8.0+默认认证插件是caching_sha2_password,而老版JDBC驱动不支持。解决方案:
-- 在MySQL命令行执行(需用管理员权限登录) ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'root123'; FLUSH PRIVILEGES;3.3 第三步:DBUtil.java中的连接超时参数必须显式设置
原始代码里getConnection()可能只有DriverManager.getConnection(url, user, pwd),这会导致默认超时长达30秒。答辩演示时你等不起。必须加超时参数:
// 修改DBUtil.java中的getConnection()方法 public static Connection getConnection() throws SQLException { String url = "jdbc:mysql://localhost:3306/scoredb?" + "useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8&" + "connectTimeout=3000&socketTimeout=30000"; // 新增:连接超时3秒,读取超时30秒 return DriverManager.getConnection(url, "root", "root123"); }参数说明:
connectTimeout=3000:建立TCP连接的最长等待时间(毫秒),避免网络抖动卡死;socketTimeout=30000:执行SQL查询的最长等待时间,防止慢查询拖垮整个系统;- 这两个参数必须拼在URL里,不能通过
Properties对象传入(DriverManager.getConnection(String, Properties)不识别这些key)。
4. 部署文档与视频的隐藏线索:Tomcat 9部署时web.xml版本号必须匹配,否则404地狱
部署文档(学生成绩管理系统文档.doc)和视频(02-项目的部署运行.mp4)看似简单,但藏着三个致命细节。我对照视频逐帧截图,发现讲师在Eclipse里右键项目→Properties→Targeted Runtimes选的是Apache Tomcat v9.0,而web.xml头部声明却是:
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" version="3.1">这就是404的根源:Tomcat 9支持Servlet 4.0规范,web.xml必须声明version="4.0"且schemaLocation指向web-app_4_0.xsd。若你用Tomcat 9但web.xml是3.1,Eclipse会编译成功但Tomcat启动时静默忽略Servlet映射——浏览器访问/login永远404。
4.1 修正web.xml:三步搞定Servlet 4.0兼容
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0"> <!-- 关键:从3.1改为4.0 --> <display-name>StudentScore</display-name> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> <servlet> <servlet-name>LoginServlet</servlet-name> <servlet-class>servlet.LoginServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>LoginServlet</servlet-name> <url-pattern>/login</url-pattern> </servlet-mapping> </web-app>注意:
xsi:schemaLocation中的web-app_4_0.xsd必须与version="4.0"严格对应,否则Eclipse校验报错。
4.2 Tomcat 9的context.xml必须关闭JNDI数据源(本项目不用)
很多教程教你在conf/context.xml里配JNDI,但本项目用的是直连JDBC,必须确保JNDI不干扰。检查conf/context.xml,删掉或注释掉:
<!-- 删除或注释掉以下内容 --> <!-- <Resource name="jdbc/scoredb" auth="Container" type="javax.sql.DataSource" maxTotal="100" maxIdle="30" minIdle="10" initialSize="10" username="root" password="root123" driverClassName="com.mysql.cj.jdbc.Driver" url="jdbc:mysql://localhost:3306/scoredb?useUnicode=true&characterEncoding=UTF-8"/> -->4.3 验证部署成功的黄金三步法
别信控制台INFO [main] org.apache.catalina.startup.Catalina.start Server startup in [X] ms,要实测:
- 访问Tomcat管理页:
http://localhost:8080/manager/html,输入conf/tomcat-users.xml中配置的用户名密码(本项目默认admin/admin123),确认/StudentScore应用状态为running; - curl测试Servlet映射:
curl -I http://localhost:8080/StudentScore/login # 应返回 HTTP/1.1 200 OK 或 HTTP/1.1 302 Found(重定向到login.jsp) - 查看Tomcat日志:
logs/catalina.out末尾是否有INFO [main] org.apache.catalina.core.StandardContext.startInternal Context [/StudentScore] started——这才是真正的启动成功信号。
5. 常见问题排查:中文乱码、404、数据库连接失败——这五条血泪经验帮你省下8小时调试
现象、原因、解决,一条一条列清楚,全是我在三台不同Win10机器上实测踩过的坑。
5.1 现象:登录成功后跳转到score/list页面,但学生姓名显示为????
原因:score.sql导入时未指定字符集,MySQL默认用latin1创建表,即使Java代码写了UTF-8也无效。
解决:
- 执行
ALTER DATABASE scoredb CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;; - 对每个表执行
ALTER TABLE student CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;; - 重启MySQL服务(
net stop MySQL80 && net start MySQL80)。
5.2 现象:Eclipse中右键项目→Run As→Run on Server,控制台报java.lang.NoClassDefFoundError: javax/servlet/Servlet
原因:Eclipse未将Tomcat的servlet-api.jar加入构建路径,或WEB-INF/lib/里误删了该jar。
解决:
- 右键项目→Properties→Java Build Path→Libraries→Add Library→Server Runtime→选择Apache Tomcat v9.0;
- 确认
WebContent/WEB-INF/lib/下没有servlet-api.jar(Tomcat会提供,重复引入导致冲突)。
5.3 现象:score.sql导入Navicat后,student表里name字段类型是varchar(50)但长度显示为0
原因:Navicat导入SQL时未勾选Use UTF8MB4选项,导致字符集解析失败。
解决:
- Navicat→连接属性→高级→默认字符集选
utf8mb4; - 右键数据库→运行SQL文件→勾选
Use UTF8MB4→再导入score.sql。
5.4 现象:Tomcat启动后访问http://localhost:8080/StudentScore显示404,但http://localhost:8080/能打开Tomcat首页
原因:项目Context root配置错误,Eclipse里项目名是StudentScore,但Servers视图中Tomcat配置的Context path是/。
解决:
- 双击
Servers视图中的Tomcat v9.0 Server→打开配置页; - 在
Modules选项卡中,找到StudentScore模块→双击→修改Path为/StudentScore; - 保存后右键Tomcat→
Clean...→重启。
5.5 现象:ScoreServlet.java中request.getParameter("courseId")始终为null,但表单里明明写了name="courseId"
原因:score/list.jsp中form的action写成了/score/list,而web.xml里<url-pattern>配的是/score/list,但Servlet类名是ScoreServlet,其doPost()方法未重写,只写了doGet()。
解决:
- 确认
ScoreServlet.java继承HttpServlet后,必须同时实现doGet()和doPost()(哪怕内容一样); score/list.jsp中form的method属性必须与Servlet中实现的方法一致(如用method="get",则只写doGet())。
6. 进阶技巧:用score.sql快速生成成绩分析报表,避开手动导出Excel的玄学崩溃
毕业答辩时,评委最爱问:“这个系统除了增删改查,还能做什么?”——这时候掏出一份动态生成的“班级平均分TOP3”报表,比讲一百遍MVC分层都有说服力。本项目score.sql里已建好score表(含student_id,course_id,score字段),我们用原生SQL+JSP快速实现,不依赖POI等第三方库,避免jar包冲突导致的500错误。
6.1 SQL层:用GROUP BY + AVG()生成班级成绩统计
在ScoreServlet.java的doGet()方法末尾,添加报表查询逻辑:
// 查询各班级平均分(假设class_id存在student表中,通过JOIN关联) String sql = "SELECT s.class_id, c.class_name, AVG(sc.score) as avg_score " + "FROM student s " + "JOIN score sc ON s.id = sc.student_id " + "JOIN course c ON sc.course_id = c.id " + "GROUP BY s.class_id, c.class_name " + "ORDER BY avg_score DESC LIMIT 3"; PreparedStatement ps = conn.prepareStatement(sql); ResultSet rs = ps.executeQuery(); List<Map<String, Object>> reportData = new ArrayList<>(); while (rs.next()) { Map<String, Object> row = new HashMap<>(); row.put("classId", rs.getString("class_id")); row.put("className", rs.getString("class_name")); row.put("avgScore", String.format("%.2f", rs.getDouble("avg_score"))); reportData.add(row); } request.setAttribute("reportData", reportData); request.getRequestDispatcher("report.jsp").forward(request, response);6.2 JSP层:用JSTL标签优雅渲染表格(避免scriptlet灾难)
report.jsp中用标准JSTL,而非<% %>脚本:
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <html> <head><title>班级成绩TOP3</title></head> <body> <h2>班级平均分TOP3</h2> <table border="1"> <tr><th>班级ID</th><th>班级名称</th><th>平均分</th></tr> <c:forEach items="${reportData}" var="row"> <tr> <td>${row.classId}</td> <td>${row.className}</td> <td>${row.avgScore}</td> </tr> </c:forEach> </table> <a href="score/list.jsp">返回成绩列表</a> </body> </html>关键配置:
WebContent/WEB-INF/lib/必须包含jstl-1.2.jar(本项目已提供);report.jsp顶部<%@ taglib %>声明不可少,否则<c:forEach>会原样输出为文本。
6.3 防止SQL注入的硬核补丁:用PreparedStatement参数化查询
上面的SQL看似安全,但如果需求变成“查某课程的TOP10”,用户输入courseId就必须参数化:
// 危险写法(绝对禁止!) String sql = "SELECT * FROM score WHERE course_id = " + request.getParameter("courseId"); // 安全写法(本项目已采用) String sql = "SELECT s.name, c.course_name, sc.score " + "FROM student s JOIN score sc ON s.id=sc.student_id " + "JOIN course c ON sc.course_id=c.id " + "WHERE sc.course_id = ? ORDER BY sc.score DESC LIMIT 10"; PreparedStatement ps = conn.prepareStatement(sql); ps.setInt(1, Integer.parseInt(request.getParameter("courseId"))); // 强制转int防注入从那以后我每次写SQL查询,都强制走一遍PreparedStatement流程——哪怕只是SELECT * FROM student WHERE id = ?,因为答辩现场一旦被问“如何防SQL注入”,你掏出这段代码,比背十句理论都管用。希望帮到你。
本文还有配套的精品资源,点击获取