首页 / 资讯中心 / 文章详情

Java Web入门必做:Servlet+JSP+MySQL成绩管理系统实战指南

Java Web入门必做:Servlet+JSP+MySQL成绩管理系统实战指南 ★ FEATURED ARTICLE
简介这是一套面向Java Web初学者与高校课程设计学生的学生成绩管理系统完整实现基于ServletJSPMySQL三层架构开发覆盖用户登录、学生信息管理、课程录入、成绩录入与查询等核心教务功能适合作为期末大作业或JavaWeb课程设计实践项目。资源包共188个文件包含42个JSP页面负责前端交互与展示、24个Java源文件含GradeDao、StudentDao、LoginServlet等关键业务逻辑类、72个编译后Class文件、12个XML配置文件如web.xml与数据库连接配置、6个SQL脚本含建库建表与初始化数据以及CSS/JS/图片等配套资源整体压缩包仅11.29MB轻量易部署。已有223人学习下载所有代码均经本地实测可运行评审得分98分内容通过助教审定附带完整设计报告结构清晰、注释规范便于理解MVC分层思想与数据库操作全流程。1. 为什么一个“JavaServletJSPMySQL学生成绩管理系统”仍是Java Web入门最硬核的通关凭证不是因为它多酷炫——它连响应式布局都懒得做也不是因为它多先进——Spring Boot早把XML配置甩出八条街。恰恰相反正是因为它“土”、它“重”、它“啰嗦”才成了检验你是否真把Java Web底层脉络摸透的试金石。当你用HttpServlet#doPost手动解析表单、在JSP里混写Java脚本片段、为一个ResultSet循环写五遍rs.getString(name)、还要手写SQL拼接防注入哪怕只是初级过滤你才真正踩在了Web容器与数据库之间那层薄薄但至关重要的胶水上。这不是过时的技术栈而是Java工程师的“肌肉记忆训练器”它逼你直面请求生命周期、会话管理、MVC分层失衡、SQL执行路径、字符编码乱码这五大高频翻车现场。适合谁刚学完Java基础、正卡在“写了HelloWorld却不会接HTTP请求”的人准备校招面试、被问到“Servlet和Filter执行顺序”当场哑火的应届生还有想给学生讲清“为什么需要三层架构”的实训课老师——这个系统就是你讲清楚request.setAttribute()和session.setAttribute()差别的最佳教具。2. 从零搭起运行环境避开Windows下MySQL 8.0与JDK 17的三重兼容陷阱2.1 JDK 17 Tomcat 9.0.83选对版本比写代码更关键很多新手栽在第一步下载最新版JDK 21或Tomcat 10结果启动报java.lang.UnsupportedClassVersionError。Servlet规范与JDK版本有强绑定关系Servlet 4.0Tomcat 9支持要求JDK 8但Tomcat 9.0.x官方明确不支持JDK 17的模块化特性--add-opens参数绕不过去。实测稳定组合是JDK 17.0.10 LTS非最新17.0.12因Oracle修复了java.net.http.HttpClient在Windows下的DNS缓存bugApache Tomcat 9.0.832023年10月发布对JDK 17的SecurityManager弃用兼容性最好提示不要用JAVA_HOME指向JDK 21即使IDE显示正常Tomcat启动脚本catalina.bat会因java -version输出格式差异静默失败。验证命令# 在cmd中执行必须看到17.0.10且无警告 %JAVA_HOME%\bin\java.exe -version2.2 MySQL 8.0.33绕开默认caching_sha2_password认证插件JDBC连接报Unknown initial character set index 255或Public Key Retrieval is not allowed这是MySQL 8.0默认认证方式与老版mysql-connector-java:5.1.49不兼容的典型症状。解决方案不是降级MySQL而是精准配置JDBC URL// ✅ 正确的连接字符串注意useSSLfalse serverTimezoneAsia/Shanghai String url jdbc:mysql://localhost:3306/studentdb?useSSLfalseserverTimezoneAsia/ShanghaiallowPublicKeyRetrievaltrue; String username root; String password your_secure_password; // 切勿用空密码参数说明allowPublicKeyRetrievaltrue强制JDBC使用RSA密钥交换解决Public Key Retrieval错误serverTimezoneAsia/Shanghai避免ResultSet.getTimestamp()返回nullMySQL 8.0时区默认UTCJava客户端默认本地时区useSSLfalse开发环境可关闭SSL生产环境必须启用并配置证书2.3 验证环境连通性的最小闭环测试在webapps/ROOT/test.jsp中写入% page importjava.sql.* % % String url jdbc:mysql://localhost:3306/studentdb?useSSLfalseserverTimezoneAsia/ShanghaiallowPublicKeyRetrievaltrue; try (Connection conn DriverManager.getConnection(url, root, your_secure_password); Statement stmt conn.createStatement(); ResultSet rs stmt.executeQuery(SELECT VERSION() AS ver)) { if (rs.next()) { out.print(✅ MySQL连接成功版本 rs.getString(ver)); } } catch (Exception e) { out.print(❌ 连接失败 e.getMessage()); } %访问http://localhost:8080/test.jsp看到绿色✅才算环境真正就绪。血泪经验此页面必须放在webapps/ROOT/下若放错目录如webapps/student/Tomcat不会报错但JSP根本不会编译——这是新手排查环境问题的第一道门槛。3. 核心业务逻辑落地用原生Servlet/JSP实现成绩CRUD的四层防御3.1 数据库建模为什么student表必须设id为主键且AUTO_INCREMENT成绩管理系统看似简单但字段设计暴露底层理解深度。常见错误是直接用student_id作为主键却不设自增导致插入时手动维护ID易冲突。正确SQLCREATE DATABASE IF NOT EXISTS studentdb CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; USE studentdb; CREATE TABLE student ( id INT PRIMARY KEY AUTO_INCREMENT, student_no VARCHAR(12) NOT NULL UNIQUE COMMENT 学号如20230001, name VARCHAR(20) NOT NULL, gender ENUM(男,女) DEFAULT 男, class_name VARCHAR(30) NOT NULL, enrollment_date DATE NOT NULL ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE score ( id INT PRIMARY KEY AUTO_INCREMENT, student_id INT NOT NULL, subject VARCHAR(20) NOT NULL, score DECIMAL(5,2) NOT NULL CHECK(score BETWEEN 0 AND 100), exam_date DATE NOT NULL, FOREIGN KEY (student_id) REFERENCES student(id) ON DELETE CASCADE ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;关键设计点utf8mb4支持emoji和生僻汉字如“䶮”避免Incorrect string value错误ON DELETE CASCADE删除学生时自动清除其成绩记录省去手动清理逻辑CHECK(score BETWEEN 0 AND 100)数据库层强制分数范围比Servlet里if(score0)更可靠3.2 Servlet分层为什么ScoreServlet要继承HttpServlet而非直接写doGet新手常把所有逻辑塞进一个Servlet导致代码不可维护。标准做法是按功能拆分StudentServlet.java处理学生信息增删改查ScoreServlet.java处理成绩录入、修改、统计LoginServlet.java处理登录验证含密码MD5加盐以ScoreServlet为例核心结构WebServlet(/score) public class ScoreServlet extends HttpServlet { Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 1. 获取参数注意getParameter()对中文默认ISO-8859-1需转码 String studentNo new String(request.getParameter(student_no).getBytes(ISO-8859-1), UTF-8); String subject request.getParameter(subject); String scoreStr request.getParameter(score); // 2. 参数校验防御式编程起点 if (studentNo null || studentNo.trim().isEmpty()) { request.setAttribute(error, 学号不能为空); request.getRequestDispatcher(/add_score.jsp).forward(request, response); return; } // 3. 业务逻辑查学生ID → 插入成绩 → 重定向到列表页 try (Connection conn JdbcUtil.getConnection()) { int studentId getStudentIdByNo(conn, studentNo); // 自定义方法 insertScore(conn, studentId, subject, Double.parseDouble(scoreStr)); response.sendRedirect(request.getContextPath() /score/list); } catch (NumberFormatException e) { request.setAttribute(error, 分数必须是数字); request.getRequestDispatcher(/add_score.jsp).forward(request, response); } } }逻辑说明new String(...getBytes(ISO-8859-1), UTF-8)解决GET请求中文乱码Tomcat 9默认编码ISO-8859-1response.sendRedirect()避免表单重复提交F5刷新不会再次插入JdbcUtil.getConnection()封装了DriverManager.getConnection()避免每次写冗长URL3.3 JSP页面为什么list_score.jsp要用JSTLc:forEach替代% while(rs.next()) %原始JSP脚本片段% %将Java代码与HTML混写导致SQL注入风险若直接拼接request.getParameter空指针异常rs未判空维护困难前端改样式要动Java逻辑现代写法需引入JSTL 1.2% taglib prefixc urihttp://java.sun.com/jsp/jstl/core % % taglib prefixfmt urihttp://java.sun.com/jsp/jstl/fmt % table border1 trth学号/thth姓名/thth科目/thth分数/thth考试日期/th/tr c:forEach items${scoreList} varscore tr td${score.studentNo}/td td${score.studentName}/td td${score.subject}/td tdfmt:formatNumber value${score.score} pattern#.##//td tdfmt:formatDate value${score.examDate} patternyyyy-MM-dd//td /tr /c:forEach /table参数说明${scoreList}Servlet中request.setAttribute(scoreList, list)传入的ListScore对象fmt:formatNumber自动处理null值避免NullPointerExceptionfmt:formatDate统一日期格式避免SimpleDateFormat线程安全问题4. 避坑指南五个让90%新手调试超3小时的致命细节4.1 现象JSP页面中文显示为??但数据库里存的是正确的汉字原因Tomcat默认URIEncoding为ISO-8859-1而浏览器发送GET请求时用UTF-8编码导致URL参数解码失败。解决修改conf/server.xml在Connector标签内添加URIEncodingUTF-8Connector port8080 protocolHTTP/1.1 connectionTimeout20000 redirectPort8443 URIEncodingUTF-8 / !-- 关键 --4.2 现象登录后跳转到index.jsp但session.getAttribute(user)始终为null原因HttpSession默认基于Cookie而Chrome等浏览器对localhost的Cookie策略更严格若JSP中未显式调用session.setAttribute()或web.xml中session-config未设置http-onlytrue/http-only可能导致会话丢失。解决在LoginServlet中强制创建会话并检查web.xml// LoginServlet.java HttpSession session request.getSession(true); // true表示不存在则创建 session.setAttribute(user, user); session.setMaxInactiveInterval(1800); // 30分钟超时!-- web.xml -- session-config session-timeout30/session-timeout http-onlytrue/http-only securefalse/secure !-- 开发环境设false -- /session-config4.3 现象INSERT INTO score执行成功但SELECT * FROM score查不到数据原因MySQL默认事务隔离级别为REPEATABLE READ若Servlet中未显式conn.commit()且连接未关闭事务处于未提交状态。解决在JDBC操作后必须commit()或使用try-with-resources自动提交try (Connection conn JdbcUtil.getConnection()) { conn.setAutoCommit(false); // 关闭自动提交 try (PreparedStatement ps conn.prepareStatement(sql)) { ps.setInt(1, studentId); ps.setString(2, subject); ps.setDouble(3, score); ps.executeUpdate(); } conn.commit(); // 显式提交 } catch (SQLException e) { conn.rollback(); // 回滚 throw e; }4.4 现象c:forEach遍历scoreList为空但后台System.out.println(list.size())输出5原因JSTL标签库未正确引入或web.xml中web-app版本声明过低如2.3导致JSTL无法解析EL表达式${scoreList}。解决确认web.xml头声明为3.0并在pom.xml中引入JSTL!-- web.xml第一行 -- web-app xmlnshttp://xmlns.jcp.org/xml/ns/javaee xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xsi:schemaLocationhttp://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd version4.0!-- pom.xml -- dependency groupIdjavax.servlet/groupId artifactIdjstl/artifactId version1.2/version /dependency4.5 现象student_no字段唯一约束失效插入重复学号不报错原因MySQL表引擎误用MyISAM不支持外键和事务或UNIQUE索引未生效。解决执行SQL验证-- 检查表引擎 SHOW CREATE TABLE student; -- 检查索引 SHOW INDEX FROM student WHERE Key_name student_no; -- 若无索引手动添加 ALTER TABLE student ADD UNIQUE INDEX idx_student_no (student_no);5. 进阶实战用原生技术栈实现“班级平均分TOP10”动态报表5.1 后端在ScoreServlet中扩展/score/report映射报表需求不能只靠前端JS计算——数据量大时会拖垮浏览器。必须由Servlet执行聚合SQLWebServlet(/score/report) public class ReportServlet extends HttpServlet { Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { ListReportItem reportList new ArrayList(); String sql SELECT s.class_name AS className, COUNT(sc.id) AS studentCount, AVG(sc.score) AS avgScore, MIN(sc.score) AS minScore, MAX(sc.score) AS maxScore FROM student s INNER JOIN score sc ON s.id sc.student_id GROUP BY s.class_name ORDER BY avgScore DESC LIMIT 10 ; try (Connection conn JdbcUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql); ResultSet rs ps.executeQuery()) { while (rs.next()) { ReportItem item new ReportItem(); item.setClassName(rs.getString(className)); item.setStudentCount(rs.getInt(studentCount)); item.setAvgScore(rs.getDouble(avgScore)); item.setMinScore(rs.getDouble(minScore)); item.setMaxScore(rs.getDouble(maxScore)); reportList.add(item); } } request.setAttribute(reportList, reportList); request.getRequestDispatcher(/report.jsp).forward(request, response); } }ReportItem.java需提供getter/setter这是JavaBean规范强制要求否则JSTL无法反射取值。5.2 前端用CSS Grid实现响应式报表卡片report.jsp不再用老旧table改用语义化卡片布局div classreport-grid c:forEach items${reportList} varitem div classcard h3${item.className}/h3 p人数c:out value${item.studentCount}//p p均分fmt:formatNumber value${item.avgScore} pattern#.##//p p区间fmt:formatNumber value${item.minScore} pattern#.##/~fmt:formatNumber value${item.maxScore} pattern#.##//p /div /c:forEach /div style .report-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: 16px; padding: 16px; } .card { border: 1px solid #ddd; border-radius: 8px; padding: 16px; background: #fff; box-shadow: 0 2px 4px rgba(0,0,0,0.1); } /style关键技巧grid-template-columns: repeat(auto-fill, minmax(280px, 1fr))让卡片在小屏自动换行无需媒体查询。5.3 安全加固为报表接口添加登录拦截Filter防止未登录用户直接访问/score/reportWebFilter(/score/*) public class AuthFilter implements Filter { Override public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { HttpServletRequest request (HttpServletRequest) req; HttpServletResponse response (HttpServletResponse) resp; HttpSession session request.getSession(false); if (session null || session.getAttribute(user) null) { response.sendRedirect(request.getContextPath() /login.jsp); return; } chain.doFilter(req, resp); } }注意WebFilter(/score/*)必须写在ScoreServlet和ReportServlet的WebServlet之前否则Filter不生效。5.4 性能优化为GROUP BY class_name添加复合索引报表SQL执行慢查看执行计划EXPLAIN SELECT s.class_name, AVG(sc.score) FROM student s INNER JOIN score sc ON s.idsc.student_id GROUP BY s.class_name;若type为ALL全表扫描则需建索引-- 在score表上为student_id建索引已存在因外键约束 -- 在student表上为class_name建索引 CREATE INDEX idx_class_name ON student(class_name);我带过三届实训班每届都有学生坚持用Spring Boot重写这个系统结果在“如何在Controller里获取HttpSession”上卡住两天。后来他们发现原生Servlet里request.getSession()那一行代码背后是Tomcat的StandardSession、ManagerBase、PersistentManager三级对象协作——而Spring Session只是把这套机制包装得更优雅。所以别急着跳过它把这个系统里的每个request.getParameter()、每个response.sendRedirect()、每个c:forEach都亲手敲一遍debug进去看HttpServlet#service()怎么分发doGet/doPost这才是Java Web工程师真正的“成人礼”。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?
咨询建站