资讯动态

JSP+Servlet+JDBC成绩管理系统:架构解析、部署实战与Maven重构指南

发布时间:2026/9/15 2:33:47 来源:尧图企业网站定制
简介一份基于JSP、Servlet、JDBC与MySQL构建的JavaEE学生成绩管理系统采用MD5加密登录按学生、教师、管理员划分权限。学生端实现考勤管理、请假、选课、成绩查询与个人信息维护教师端支持考勤与请假审核、选课信息管理、学生/班级/课程管理、成绩录入导入导出及可视化统计管理员则额外拥有教师管理功能。系统代码注释清晰、模块划分明确适合JavaWeb学习者从登录鉴权、权限管理到成绩可视化的完整走查基于Java跨平台特性推荐JDK1.8与Tomcat8.0环境运行可直接作为毕业设计或课程项目的可运行参考。压缩包共93个文件涵盖Java源文件、JSP页面、编译后的class、依赖jar包、CSS样式、配置文件和SQL数据库脚本整体大小1.7MB结构精简易于部署。目前已有1486人学习下载随包另附代码说明文档与建库SQL脚本可快速还原数据库并理解各模块表结构减少自查与排错成本。1. 一个 JSPServlet 时代留存下来的完整工程这套成绩管理系统没有用 Spring没有 Maven就是最原始的 JSP Servlet JDBC MySQL 结构代码里还带了 MD5 加密和由 ssms.sql 还原出的完整数据库。它的价值不在技术新而在「完整」学生端有考勤、请假、选课、成绩查询教师端能审核请假、管课程和班级、做成绩导入导出和可视化统计管理员只在教师基础上多一个教师管理。拿到压缩包后解压即得源码、数据库脚本和说明文档适合做数据库课程设计或 JavaEE 课程设计的参考底子也适合想快速看懂传统 JavaWeb 工程分层逻辑的人。这类源码包网上不少但很多跑不起来这一份能落地的关键在于三件事WebContent 目录完整、ssms.sql 能直接导入、JDK/Tomcat/MySQL 版本匹配。2. 先从分层架构看透这套 JSPServletJDBC 的成绩管理骨架2.1 WebContent 与 src 双目录决定的传统 JavaWeb 工程结构解压 StudentScoreManagerSystem 后第一眼看到的是.project、.classpath、src、build、WebContent、ssms.sql、代码说明.doc这几个文件。.project和.classpath是 Eclipse 的工程描述文件直接决定了这个项目能用「导入现有工程」的方式被 IDE 识别而不是新建空白项目再手动拷文件。src下是 Servlet 控制器、JDBC 工具类、DAO、实体类和 MD5 工具类WebContent下是 JSP 页面、WEB-INF 和依赖 jar 包。路径/文件实际作用改动时注意srcJava 源码Servlet、DAO、DBUtil、MD5 工具类修改后必须重新编译再发布build编译输出残留通常是导入 IDE 后由构建过程生成不作为源码直接编辑WebContentJSP、WEB-INF/web.xml、lib 下的依赖 jar、CSS/JS前端页面都在这里改.classpathEclipse 的 classpath 描述声明源码目录和依赖删掉会导致 IDE 识别不出 srcssms.sql建库建表 初始数据脚本先于代码部署执行代码说明.doc项目功能与结构说明答辩时可直接引用其中的功能清单这里有一个容易踩的对外行坑不要用 IDE 的「File - New - Dynamic Web Project」重新建工程再往里拖文件web.xml的 servlet-mapping 路径、WEB-INF/lib下的 jar 引用会全部断掉。正确做法是 Eclipse 里File - Import - General - Existing Projects into Workspace让.classpath文件帮 IDE 还原全部配置。2.2 Servlet 作为控制器时的请求分发与跳转写法这套系统的登录流程是典型的 JSP 时代写法JSP 表单提交到 ServletServlet 取参数、调 DAO、比对数据库再决定跳转到哪个页面。核心逻辑简化为protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding(UTF-8); String username request.getParameter(username); String password MD5Util.md5(request.getParameter(password)); LoginDAO dao new LoginDAO(); User user dao.checkLogin(username, password); if (user null) { response.sendRedirect(login.jsp?error1); return; } HttpSession session request.getSession(); session.setAttribute(loginUser, user); session.setAttribute(role, user.getRole()); if (admin.equals(user.getRole())) { response.sendRedirect(admin/index.jsp); } else if (teacher.equals(user.getRole())) { response.sendRedirect(teacher/index.jsp); } else { response.sendRedirect(student/index.jsp); } }这段代码里MD5Util.md5()对明文密码做摘要数据库里存的是摘要值而不是明文sendRedirect跳转适合「登录这种不能因刷新而重复提交」的场景因为地址栏会变成目标 URL刷新时只重发 GET 请求。反过来如果 Servlet 需要把查询结果递给 JSP 渲染比如教师端查看学生列表就应改用request.getRequestDispatcher(studentList.jsp).forward(request, response)这样 JSP 可以通过request.getAttribute(list)拿到数据。2.3 JDBC 连接与 MD5 加密在校验中的位置JDBC 工具类通常集中放在src下的DBUtil.java所有 DAO 都从它取连接。这个老工程的连接写法一般长这样private static final String DRIVER com.mysql.jdbc.Driver; private static final String URL jdbc:mysql://localhost:3306/ssms?useSSLfalsecharacterEncodingUTF-8serverTimezoneAsia/Shanghai; private static final String USER root; private static final String PASSWORD root; public static Connection getConnection() throws Exception { Class.forName(DRIVER); return DriverManager.getConnection(URL, USER, PASSWORD); }useSSLfalse是因为本地开发环境不需要加密连接加上后会减少握手报错characterEncodingUTF-8保证中文写入数据库、再从数据库读出时不会乱码serverTimezone是 MySQL 5.7 以上版本要求显式声明时区否则时间字段可能报 CST 错。MD5 加密在这里的位置比较固定注册或新增用户时对明文摘要后入库登录时对输入密码做同样的摘要再比对。这个方法本身是单向散列不是可逆加密工程里如果只做单次 MD5 而不加盐只能说挡住了「数据库裸奔」这类基础风险如果想提升安全性可以在 MD5Util 里追加固定的盐字符串改动面很小。3. MySQL 五类核心表设计与成绩导入导出的边界3.1 从 ssms.sql 还原出的表关系与字段设计执行ssms.sql后还原出的核心表大概覆盖用户、班级、课程、考勤、请假、选课与成绩这几条业务线。学生和教师通常落在独立的表里管理员可能是单独一张表也可能是 user 表中的一个角色字段这套系统因为「管理员只比教师多教师管理」角色权限在登录时写入 session页面展示用 JSP 标签或if判断区分而不是靠复杂的 RBAC 表。表名核心字段业务含义studentid, student_no, name, class_id, password学生基础信息登录账号teacherid, teacher_no, name, title, password教师基础信息admin(或 user)id, account, password, role管理员登录账号courseid, course_name, credit, teacher_id课程与授课教师关联student_courseid, student_id, course_id选课关系一个学生多门课scoreid, student_id, course_id, score学生某门课的成绩attendanceid, student_id, course_id, date, status考勤记录leave_requestid, student_id, start_time, end_time, reason, status请假申请与审批状态这里要注意关联字段的命名习惯老工程喜欢用student_id这种下划线命名对应 Java 实体里的studentIdMyBatis 一类的框架会自动映射但纯 JDBC 环境下ResultSet.getInt(student_id)取值后要手动 set 到实体字段上改表结构时漏改一行就会在运行时报空指针或 0 值。3.2 成绩统计的 SQL 与可视化取值方式摘要里明确写了教师端有成绩统计且要求可视化展示这种功能在 JSPServlet 时代的实现公式基本固定先写统计 SQL再把结果转成 JSON最后用 ECharts 或 JFreeChart 渲染。按课程统计平均分、最高分、最低分、及格率时一条 SQL 足够SELECT c.course_name, COUNT(sc.id) AS total_count, ROUND(AVG(sc.score), 1) AS avg_score, MAX(sc.score) AS max_score, MIN(sc.score) AS min_score, ROUND(SUM(CASE WHEN sc.score 60 THEN 1 ELSE 0 END) / COUNT(sc.id) * 100, 2) AS pass_rate FROM score sc JOIN course c ON sc.course_id c.id GROUP BY sc.course_id, c.course_name ORDER BY avg_score DESC;这个 SQL 里CASE WHEN sc.score 60是及格率计算的核心按sc.course_id分组才能保证一门课一行统计结果。及格线写成硬编码不是好习惯建议抽到配置项里因为有些课程可能以 90 分作为优秀线、60 分作为及格线。Java 端拿到ResultSet后常见做法是拼成 JSON 数组返回给前端JSONArray array new JSONArray(); while (rs.next()) { JSONObject obj new JSONObject(); obj.put(courseName, rs.getString(course_name)); obj.put(avgScore, rs.getDouble(avg_score)); obj.put(passRate, rs.getDouble(pass_rate)); array.add(obj); } response.setContentType(application/json;charsetUTF-8); response.getWriter().write(array.toString());这段代码用JSONObject手工组 JSON兼容老工程不引 Spring 的情况。前端 JSP 页面里用 jQuery 的$.getJSON()拉接口再传入 ECharts 柱状图的series.data。如果不想额外引入 JSON 库也可以直接用StringBuilder拼字符串但要注意中文要经过URLEncoder处理。3.3 成绩导入导出容易踩的格式与编码问题成绩导入导出在教师端属于高频功能实现上通常用 Apache POI 读写 Excel。导出成绩单时常见做法是用XSSFWorkbook创建.xlsx文件并把响应头设成文件下载XSSFWorkbook workbook new XSSFWorkbook(); XSSFSheet sheet workbook.createSheet(成绩单); XSSFRow header sheet.createRow(0); header.createCell(0).setCellValue(学号); header.createCell(1).setCellValue(姓名); header.createCell(2).setCellValue(课程); header.createCell(3).setCellValue(成绩); // 遍历成绩列表写入行 for (int i 0; i scoreList.size(); i) { Score s scoreList.get(i); XSSFRow row sheet.createRow(i 1); row.createCell(0).setCellValue(s.getStudentNo()); row.createCell(1).setCellValue(s.getStudentName()); row.createCell(2).setCellValue(s.getCourseName()); row.createCell(3).setCellValue(s.getScore()); } response.setContentType(application/vnd.openxmlformats-officedocument.spreadsheetml.sheet); response.setHeader(Content-Disposition, attachment;filenamescore.xlsx); workbook.write(response.getOutputStream());这个写法里createCell(0)是 Excel 列索引从 0 开始写到响应输出流后不需要手动close()但workbook本身要放finally里关闭。导入方向更敏感Excel 单元格里如果混入文本型数字POI 读出来要判断cell.getCellType()用DataFormatter统一转字符串再parseInt否则大量单元格显示成数字但程序就是解析报错。另一个高频问题是编码Excel 导出后用 WPS 打开乱码多半是Content-Disposition里文件名没有做 URL 编码下载头里中文文件名要这样处理new String(成绩单.xlsx.getBytes(UTF-8), ISO-8859-1)。还有一个容易忽略的边界是导出范围成绩表如果积累了多个学期、多门课程的数据导出前一定要让教师先选课程和班级否则全表导出会把接口拖垮。4. 从零跑通学生成绩管理系统JDK、Tomcat、MySQL 的参数配置顺序4.1 环境版本与开发工具的搭配说明摘要里给定的版本组合是「JDK 1.8 Tomcat 8.0 MySQL 8.0 以下 Eclipse/IDEA」这个组合不是随便写的。JDK 1.8 是 Tomcat 8.0 支持最稳定的版本MySQL 8.0 以上如果驱动的连接串里没有allowPublicKeyRetrievaltrue会在首次连接时直接抛Public Key Retrieval is not allowed老代码里通常不会写这个参数所以推荐 8.0 以下。开发工具上Eclipse 的 JavaEE 版直接内置了 Tomcat Server 视图适合 JSP 调试如果只有社区版 IDEA需要装 Smart Tomcat 插件。搜 VSCode 配置 JavaEE 语言环境的人也不少VSCode 配合 Java Extension Pack 和 Tomcat 插件确实能运行这个工程但 Servlet 断点调试和web.xml的可视化编辑不如 Eclipse 顺手课程设计阶段建议直接用 Eclipse省下的时间都花在调功能上。4.2 导入数据库脚本与修改 JDBC 连接串数据库准备这一步的先后顺序很重要先建库再改代码里的连接串。在 MySQL 命令行或 Navicat 里执行ssms.sql时需要注意脚本里如果写了CREATE DATABASE就不能再手动新建同名库否则会因为库已存在而中断mysql -u root -p ssms.sql执行完成后用SHOW TABLES;确认核心表都已生成再去工程里找到DBUtil.java或db.properties把连接串里的用户名、密码改成自己本机的值。默认配置通常是 root/root如果本机 MySQL 密码不同而工程里又无法启动错误日志永远停在Access denied for user rootlocalhost。修改时一个常见的连带问题是改完连接串后忘了同步修改所有 DAO 里的 SQL。比如想给score表增加一个平时成绩字段需要同时改ssms.sql建表语句、Score.java实体、ScoreDAO.java的 insert/update、成绩录入的 JSP 表单少改一层就会出现页面提交后字段丢失或Unknown column报错。这种「mysql数据库修改结构」场景下建议先用一条DESC score;确认表结构再点开工程里的实体类和 DAO 逐一对照。4.3 部署到 Tomcat 8.0 后的排错对照Eclipse 里右键工程Run As - Run on Server选择 Tomcat 8.0浏览器访问路径默认是http://localhost:8080/StudentScoreManagerSystem/。如果访问不到先从 catalina 日志入手路径一般在 Tomcat 安装目录的logs/catalina.xxx.logEclipse 的 Console 窗口不会显示全部启动细节很多异常只在日志文件里tail -f /path/to/tomcat/logs/catalina.out部署阶段最高频的问题集中在这几类报错现象可能原因处置方式404 或无法访问项目首页工程没发布到 webapps或 context path 不对检查 Servers 视图里工程状态路径大小写严格一致ClassNotFoundException: com.mysql.jdbc.DriverWEB-INF/lib下缺少 MySQL 驱动 jar把驱动复制进WebContent/WEB-INF/lib重建工程Access denied for user rootlocalhostDBUtil 里密码与本地 MySQL 不一致修改连接串后重启 Tomcat不用热部署Public Key Retrieval is not allowed连了 MySQL 8.0 且驱动版本旧换成mysql-connector-java 8.x或降数据库版本中文乱码连接串缺characterEncodingUTF-8或 JSP 页面编码不是 UTF-8连接串、request.setCharacterEncoding、JSP 的pageEncoding三处一致页面布局错乱静态资源路径写死成绝对路径用%request.getContextPath()%/css/style.css这类相对根路径引用ClassNotFoundException是最影响理解的一类纯 JDBC 连接不依赖 Spring但驱动 jar 必须随工程发布到 Tomcat 的类加载路径只放到本机 JDK 的ext目录或编译路径里都不会生效。遇到这个问题先确认WebContent/WEB-INF/lib里有mysql-connector-java.jar再Project - Clean触发重新发布很多时候是旧编译产物覆盖了新 lib 配置。5. 给老工程续命把 JSPServlet 项目重构到 Maven 与前后端分离思路5.1 手工改造为 Maven 结构的目录迁移清单如果不想长期依赖 Eclipse 的.classpath可以把这个老工程手工迁移成 Maven 的 war 包结构。迁移步骤按顺序做新建maven-webapp工程把src里的 Java 文件复制到src/main/javaWebContent内容复制到src/main/webapp原来的WEB-INF/lib下的 jar 全部从 pom.xml 依赖接管然后删掉.classpath与.project用 Maven 命令构建。依赖接管时要重点处理这几个坐标。servlet-api和jsp-api这两个依赖的 scope 必须设成provided因为 Tomcat 自己带有这两套 APIscope 不设的话打包进 war 再部署会和 Tomcat 自带的类冲突启动时出现java.lang.LinkageError或重复类加载告警。dependencies dependency groupIdjavax.servlet/groupId artifactIdjavax.servlet-api/artifactId version3.1.0/version scopeprovided/scope /dependency dependency groupIdjavax.servlet/groupId artifactIdjsp-api/artifactId version2.0/version scopeprovided/scope /dependency dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId version5.1.49/version /dependency dependency groupIdorg.apache.poi/groupId artifactIdpoi-ooxml/artifactId version4.1.2/version /dependency /dependencies依赖版本的选择上有两个细节。mysql-connector-java 5.1.49兼容 MySQL 5.x 和 8.0 早期的服务端前提是连接串里把useSSLfalse写好poi-ooxml 4.x 的XSSFWorkbook读取大文件时比 3.x 版本内存占用更低但导出超过 6 万行的需求在这套系统里几乎不会出现不需要上 SXSSFWorkbook 流式写。5.2 用 Filter 统一登录态与编码替代散落的判断逻辑老工程里最常见的权限写法是每个 Servlet 开头复制粘贴一段「判断 session 里有没有 loginUser」的代码。新页面一旦忘记复制就会形成可直接访问的越权接口。用 Filter 集中处理后新 Servlet 只要写业务逻辑不再关心拦截public class LoginFilter implements Filter { Override public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { HttpServletRequest request (HttpServletRequest) req; HttpServletResponse response (HttpServletResponse) resp; String uri request.getRequestURI(); if (uri.endsWith(login.jsp) || uri.endsWith(LoginServlet) || uri.contains(/css/) || uri.contains(/js/)) { chain.doFilter(req, resp); return; } Object loginUser request.getSession().getAttribute(loginUser); if (loginUser null) { response.sendRedirect(request.getContextPath() /login.jsp); return; } chain.doFilter(req, resp); } }这段doFilter里判断uri.endsWith是放行条件登录页、登录接口、静态资源不拦其余请求一律校验 session。放行列表里如果把/admin/前缀加进去就顺手实现了「管理员接口只允许拥有 admin 角色的用户访问」这一层控制。字符编码过滤同理可以单独再写一个CharacterEncodingFilter把request.setCharacterEncoding(UTF-8)从每个 doPost 里抽出来。改造完之后原来 Servlet 里的重复代码可以删掉一半登录、导入导出这类核心接口的代码量明显下降。5.3 成绩可视化与导出功能的瘦身替换思路原工程的成绩可视化如果用的是 JFreeChart服务端生成图片再输出给页面交互上只能看静态图而且中文标题渲染需要额外处理字体经常在 Linux 服务器上显示成方块。换成 ECharts 之后后端只负责输出 JSON前端渲染改动范围其实可控保留 3.2 节里统计 SQL 和 JSON 拼接逻辑新增一个返回 JSON 的StatServletJSP 页面里用 jQuery 拉取数据渲染柱状图。图表上方的课程下拉框切换时重新请求接口即可不需要刷新整页。导出模块如果觉得 POI 代码太琐碎可以评估是否引入 EasyExcel。EasyExcel 对内存占用优化明显但团队里如果有人不熟悉它的注解模型排障成本反而会升高。我的建议是这套系统的成绩导出批量不大POI 原生写法配合workbook.write(response.getOutputStream())已经完全够用没必要为「少写几行代码」引入一个需要额外学习成本的库。真正值得投入的是把导出逻辑抽成独立工具类把「成绩单.xlsx」文件名和 sheet 名这些常量集中管理避免每个教师端页面各写一套。5.4 交付或答辩前值得检查的几个细节如果这套系统要用来答辩或交给学校验收有几个点值得在最终部署前过一遍。第一改完数据库密码和连接串之后务必重新执行一遍教师端完整流程建课程、导入成绩、查看统计、导出 Excel不要只验证登录。第二检查所有 JSP 页面的pageEncoding统一为UTF-8数据库连接串、web.xml里如果配了字符编码过滤器也要一致否则换一台机器部署时中文乱码会分散你的精力去排查。第三确认web.xml里的welcome-file配置访问根路径能否直接跳转到登录页很多课程设计验收时就是从根地址开始点。第四如果导出的 Excel 需要在 WPS 和 Office 中都能正常打开Content-Disposition里的文件名编码要按 3.3 节的方式处理另外可以在导出接口里加一个响应头response.setHeader(Cache-Control, no-cache)防止浏览器缓存一份旧的下载文件。这几个点都确认过之后再打包部署这套 JSPServlet 老工程就可以很稳定地跑完整个成绩管理流程。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价