ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Jsp+MySQL学生成绩管理系统:从建库到部署的完整实战指南

Jsp+MySQL学生成绩管理系统:从建库到部署的完整实战指南 简介这是一套基于JavaWeb技术栈的学生成绩管理系统完整源码采用JSPServletMySQL架构适合计算机相关专业学生用于课程设计、期末大作业或项目实战练习。项目为个人大三学期期末大作业经导师指导并评审通过获得98分高分具备较高的参考与复用价值。压缩包共353个文件约9.85MB包含53个Java源文件与53个class文件、24个JSP页面、40个CSS样式、10个JS脚本及24个JAR依赖另有4个数据库文件与1个SQL脚本涵盖前端页面、后端业务逻辑与数据持久层结构完整。内容涉及BaseDao、TeacherService、ExamService、ScoreService、ExcelTool及各类Servlet等核心模块覆盖成绩录入、查询、统计与导出等典型业务场景。目前已有198人学习下载可作为课程设计模板或二次开发基础帮助读者快速理解JavaWeb分层开发思路与数据库设计方法。1. 从一份 98 分课设说起Jsp MySQL 学生成绩管理系统到底能跑多远每年毕业季和课程设计周总有一批人卡在同一个地方选题定了「学生成绩管理系统」技术栈写的是 JavaWeb Jsp MySQL但真到动手时发现 IDEA 新建 Jsp 项目这一步就能劝退一半人。这份 98 分项目的核心价值不在于分数而在于它是一条被验证过的完整链路——从 MySQL 建库建表到 Jsp 页面渲染再到 Servlet 处理业务逻辑最后打包成 war 丢进 Tomcat 跑起来。它解决的不是「学 Java」这种大问题而是「我能不能在三天内交出一个能登录、能增删改查、能导出成绩的系统」这种具体诉求。适合两类人一是被课设逼到墙角的在校生二是想从传统 Jsp 项目入手理解 JavaWeb 请求流转的转行者。下面我按实际搭建顺序把这条链路拆开讲清楚包括那些教程里不会写的翻车点。2. 环境选型与 MySQL 建库别在第一步就把自己埋了2.1 JDK、Tomcat、IDEA 的版本搭配逻辑传统 Jsp 项目最怕的就是版本打架。我一般推荐这套组合JDK 8 或 JDK 11、Tomcat 8.5 或 9.0、IDEA 2021 以上版本。为什么不用 JDK 17因为很多老教程里的 Jsp 项目用了javax.servlet包而 Tomcat 10 之后换成了jakarta.servlet包名一变所有 import 全红。这不是你代码写错了是规范迁移的锅。具体操作上IDEA 新建项目时选「Java Enterprise」勾选「Web Application」然后手动指定 Tomcat 安装目录。如果你用的是社区版 IDEA没有 Java Enterprise 选项那就新建普通 Java 项目再手动加 Web 框架支持——右键项目 → Add Framework Support → Web Application。这一步做完项目里会出现web目录和WEB-INF/web.xml这是 Jsp 项目的骨架。提示Tomcat 不要装在中文路径下否则启动时可能报ClassNotFoundException这个坑我踩过不止一次。2.2 MySQL 建库建表三张核心表就够跑通全流程学生成绩管理系统的数据模型不复杂核心就三张表学生表、课程表、成绩表。下面是我常用的建表 SQL字段类型和约束都按实际业务调过-- 创建数据库字符集用 utf8mb4避免中文乱码 CREATE DATABASE student_score_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE student_score_db; -- 学生表学号做主键姓名和班级是必填项 CREATE TABLE student ( student_id VARCHAR(20) PRIMARY KEY COMMENT 学号, name VARCHAR(50) NOT NULL COMMENT 姓名, gender ENUM(男,女) DEFAULT 男 COMMENT 性别, class_name VARCHAR(50) COMMENT 班级, create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 课程表课程编号主键学分用小数因为可能有 0.5 学分 CREATE TABLE course ( course_id VARCHAR(20) PRIMARY KEY COMMENT 课程编号, course_name VARCHAR(100) NOT NULL COMMENT 课程名称, credit DECIMAL(3,1) DEFAULT 0.0 COMMENT 学分 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 成绩表联合主键防止同一学生同一课程重复录入 CREATE TABLE score ( student_id VARCHAR(20), course_id VARCHAR(20), score DECIMAL(5,2) DEFAULT 0.00 COMMENT 成绩, exam_time DATE COMMENT 考试日期, PRIMARY KEY (student_id, course_id), FOREIGN KEY (student_id) REFERENCES student(student_id) ON DELETE CASCADE, FOREIGN KEY (course_id) REFERENCES course(course_id) ON DELETE CASCADE ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;这段 SQL 里有几个参数值得说清楚。utf8mb4而不是utf8是因为 MySQL 的utf8其实是阉割版存不了 emoji 和部分生僻字用utf8mb4一步到位。DECIMAL(5,2)表示总共 5 位、小数占 2 位最大能存 999.99足够覆盖百分制成绩。外键的ON DELETE CASCADE意味着删学生时他的成绩记录自动清掉避免脏数据——但如果你做的是正式项目建议用软删除课设阶段用级联删除省事。建完表后插几条测试数据别空着库去调页面否则你分不清是 SQL 写错了还是页面没传参。2.3 JDBC 连接配置URL 里每个参数都有意义Jsp 项目连 MySQL 最常见的方式是 JDBC把驱动 jar 包丢进WEB-INF/lib目录。连接代码我一般封装成一个工具类public class DBUtil { // 加载驱动只需一次静态块里做 static { try { Class.forName(com.mysql.cj.jdbc.Driver); } catch (ClassNotFoundException e) { e.printStackTrace(); } } public static Connection getConnection() throws SQLException { // useSSLfalse 关掉 SSL 警告serverTimezone 必须设否则时间差 8 小时 String url jdbc:mysql://localhost:3306/student_score_db ?useSSLfalseserverTimezoneAsia/Shanghai characterEncodingutf8allowPublicKeyRetrievaltrue; return DriverManager.getConnection(url, root, 你的密码); } }serverTimezoneAsia/Shanghai这个参数不加插入的时间字段会莫名其妙差 8 小时这是 MySQL 8 的经典坑。allowPublicKeyRetrievaltrue是 MySQL 8 默认认证插件caching_sha2_password要求的不加会报Public Key Retrieval is not allowed。驱动类名从 MySQL 8 开始是com.mysql.cj.jdbc.Driver老教程里的com.mysql.jdbc.Driver已经废弃了虽然还能用但会打警告。3. Jsp 页面与 Servlet 分工把请求流转讲透3.1 登录模块从 form 表单到 session 的完整链路登录是系统的入口也是理解 Jsp Servlet 协作的最佳样本。页面部分login.jsp只负责展示和提交业务逻辑交给LoginServlet处理。先看页面% page contentTypetext/html;charsetUTF-8 languagejava % html headtitle学生成绩管理系统 - 登录/title/head body form actionLoginServlet methodpost 用户名input typetext nameusername requiredbr 密码input typepassword namepassword requiredbr input typesubmit value登录 /form %-- 错误提示从 request 域取登录失败时 Servlet 会转发回来 --% % String error (String) request.getAttribute(error); if (error ! null) { out.print(p stylecolor:red error /p); } % /body /htmlServlet 端处理逻辑WebServlet(/LoginServlet) public class LoginServlet extends HttpServlet { Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setCharacterEncoding(UTF-8); // 必须在取参数之前设 String username req.getParameter(username); String password req.getParameter(password); // 实际项目密码要加密存储课设阶段先明文比对 if (admin.equals(username) 123456.equals(password)) { HttpSession session req.getSession(); session.setAttribute(username, username); resp.sendRedirect(studentList.jsp); // 重定向防止表单重复提交 } else { req.setAttribute(error, 用户名或密码错误); req.getRequestDispatcher(login.jsp).forward(req, resp); } } }这里有两个关键区别sendRedirect是重定向浏览器地址栏会变且会发起新请求forward是服务器内部转发地址栏不变request 域的数据能带过去。登录成功用重定向失败用转发这是标准做法。req.setCharacterEncoding(UTF-8)必须放在getParameter之前否则中文参数会乱码——这个顺序问题坑过无数人。3.2 成绩列表页Jsp 里嵌 Java 代码的正确姿势成绩列表要展示学生、课程、成绩三表关联的数据。我一般先在 Servlet 里查好数据塞进 request 域再转发到 Jsp 渲染WebServlet(/ScoreListServlet) public class ScoreListServlet extends HttpServlet { protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { ListMapString, Object list new ArrayList(); String sql SELECT s.name, c.course_name, sc.score, sc.exam_time FROM score sc JOIN student s ON sc.student_id s.student_id JOIN course c ON sc.course_id c.course_id ORDER BY sc.score DESC; try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql); ResultSet rs ps.executeQuery()) { while (rs.next()) { MapString, Object row new HashMap(); row.put(name, rs.getString(name)); row.put(courseName, rs.getString(course_name)); row.put(score, rs.getBigDecimal(score)); row.put(examTime, rs.getDate(exam_time)); list.add(row); } } catch (SQLException e) { e.printStackTrace(); } req.setAttribute(scoreList, list); req.getRequestDispatcher(scoreList.jsp).forward(req, resp); } }Jsp 端用 JSTL 遍历比直接写 Java 脚本片段干净得多% taglib prefixc urihttp://java.sun.com/jsp/jstl/core % table border1 trth姓名/thth课程/thth成绩/thth考试日期/th/tr c:forEach items${scoreList} varrow tr td${row.name}/td td${row.courseName}/td td${row.score}/td td${row.examTime}/td /tr /c:forEach /table用 JSTL 需要引入jstl.jar和standard.jar两个包。${row.name}这种 EL 表达式会自动调用 getter比% %脚本片段可读性高一个档次。注意scoreList这个 key 要和 Servlet 里setAttribute的名字完全一致大小写敏感。3.3 增删改查的复用模式一个 Servlet 处理多种操作如果每个操作写一个 Servlet项目里会堆几十个类。常见做法是用一个 Servlet 配合action参数分发WebServlet(/StudentServlet) public class StudentServlet extends HttpServlet { protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String action req.getParameter(action); if (delete.equals(action)) { String id req.getParameter(id); String sql DELETE FROM student WHERE student_id ?; try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setString(1, id); ps.executeUpdate(); } catch (SQLException e) { e.printStackTrace(); } resp.sendRedirect(StudentServlet?actionlist); } else { // 默认查列表 ListStudent students new ArrayList(); String sql SELECT * FROM student; try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql); ResultSet rs ps.executeQuery()) { while (rs.next()) { Student s new Student(); s.setStudentId(rs.getString(student_id)); s.setName(rs.getString(name)); students.add(s); } } catch (SQLException e) { e.printStackTrace(); } req.setAttribute(students, students); req.getRequestDispatcher(studentList.jsp).forward(req, resp); } } }PreparedStatement用?占位符既防 SQL 注入又省去拼字符串的麻烦。try-with-resources语法确保连接自动关闭不用手写 finally 块。这套模式跑通后课程管理和成绩管理照搬即可改的只是 SQL 和实体类字段。4. 避坑与排查那些让项目跑不起来的经典问题4.1 中文乱码三个位置都要设 UTF-8现象页面输入中文存进数据库变成问号或者从数据库读出来显示乱码。原因乱码可能出现在三个环节——Jsp 页面编码、request 请求编码、数据库连接编码。任何一个没设对都会出问题。解决Jsp 页面头部加% page contentTypetext/html;charsetUTF-8 %Servlet 里req.setCharacterEncoding(UTF-8)放在取参数之前JDBC URL 加characterEncodingutf8数据库和表都用utf8mb4。四处全对齐乱码基本绝迹。4.2 404 错误路径对了但 Servlet 找不到现象浏览器访问LoginServlet报 404但类明明写了。原因常见有三种——web.xml里没配 servlet-mapping或者注解WebServlet的路径和 form 的 action 不一致或者项目没有正确部署到 Tomcat 的 webapps 目录。解决先看 Tomcat 启动日志有没有Servlet [xxx] registered之类的信息。如果用注解确认web.xml的metadata-complete不是true设成 true 会忽略注解。IDEA 里检查 Artifact 配置确保WEB-INF/lib下的 jar 包被包含进部署包。4.3 MySQL 连接报错驱动版本和时区是重灾区现象java.sql.SQLException: No suitable driver found或者The server time zone value ?D1ú±ê×?ê±?? is unrecognized。原因前者是驱动 jar 没放进WEB-INF/lib或者类名写错后者是 MySQL 8 的时区参数没设系统默认时区名带中文导致解析失败。解决驱动包用mysql-connector-java-8.0.x.jar类名写com.mysql.cj.jdbc.DriverURL 里加serverTimezoneAsia/Shanghai。如果还报Public Key Retrieval is not allowed再加allowPublicKeyRetrievaltrue。4.4 Jsp 修改后不生效Tomcat 缓存没清现象改了 Jsp 页面刷新浏览器还是旧内容。原因Tomcat 会把 Jsp 编译成 Servlet 后缓存有时候热部署没触发重新编译。解决IDEA 里点 Build → Rebuild Project然后重启 Tomcat。或者直接去 Tomcat 的work目录把对应项目的缓存文件夹删掉。生产环境不会这么干但课设阶段这招最快。4.5 打包 war 后部署失败依赖没打进去现象IDEA 里跑得好好的导出 war 丢进 Tomcat 的 webapps 就报ClassNotFoundException。原因IDEA 的 Artifact 配置里WEB-INF/lib下的依赖没有全部被包含进 war 包。解决File → Project Structure → Artifacts检查 Output Layout 里WEB-INF/lib是否包含了所有第三方 jar。最稳妥的办法是把所有 jar 手动复制到web/WEB-INF/lib目录然后在 Artifact 里直接引用这个目录。5. 从能跑到好用三个让课设加分的小技巧5.1 用过滤器统一处理编码和登录校验每个 Servlet 都写一遍setCharacterEncoding太啰嗦用 Filter 一次性解决WebFilter(/*) public class EncodingFilter implements Filter { public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { req.setCharacterEncoding(UTF-8); resp.setContentType(text/html;charsetUTF-8); chain.doFilter(req, resp); } }WebFilter(/*)表示拦截所有请求。如果还想做登录校验在 Filter 里判断 session 有没有 username没有就跳回登录页。注意放行登录页和登录请求本身否则会死循环。5.2 分页查询LIMIT 加 COUNT 两步走成绩多了要分页核心就两条 SQL-- 查总数 SELECT COUNT(*) FROM score; -- 查当前页数据page 从 1 开始pageSize 每页条数 SELECT s.name, c.course_name, sc.score FROM score sc JOIN student s ON sc.student_id s.student_id JOIN course c ON sc.course_id c.course_id LIMIT ?, ?;Java 里算偏移量int offset (page - 1) * pageSize;然后ps.setInt(1, offset); ps.setInt(2, pageSize);。前端传page参数Servlet 算好总页数塞进 request 域Jsp 渲染上一页/下一页链接。5.3 数据库连接池从 DriverManager 到 DruidDriverManager.getConnection每次请求都新建连接并发一高就崩。换成 Druid 连接池public class DBUtil { private static DataSource dataSource; static { DruidDataSource ds new DruidDataSource(); ds.setUrl(jdbc:mysql://localhost:3306/student_score_db?useSSLfalseserverTimezoneAsia/Shanghai); ds.setUsername(root); ds.setPassword(你的密码); ds.setInitialSize(5); // 初始连接数 ds.setMaxActive(20); // 最大连接数 ds.setMinIdle(5); // 最小空闲连接 ds.setMaxWait(3000); // 获取连接超时毫秒 dataSource ds; } public static Connection getConnection() throws SQLException { return dataSource.getConnection(); } }initialSize设 5 够课设用maxActive20 防止连接泄漏拖垮数据库。Druid 的 jar 包丢进WEB-INF/lib即可不需要额外配置文件。换完之后所有 DAO 代码不用改因为getConnection的签名没变。这套系统跑通之后你会发现 Jsp Servlet 的请求流转其实很直白浏览器发请求 → Filter 预处理 → Servlet 调 DAO 查数据 → 数据塞 request 域 → forward 到 Jsp 渲染。把这个链路吃透再去看 Spring MVC 的 DispatcherServlet会发现只是多了一层封装。我自己的习惯是每做完一个模块就用 Postman 直接打 Servlet 的 URL确认后端逻辑没问题再调页面这样排错时能快速定位是前端传参问题还是后端处理问题。希望帮到你。本文还有配套的精品资源点击获取
返回列表