ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

JavaWeb学生成绩管理系统源码实战:JSP+MySQL环境配置与避坑指南

JavaWeb学生成绩管理系统源码实战:JSP+MySQL环境配置与避坑指南 简介这是一份基于JavaWeb的学生成绩管理系统项目源码采用JSP与MySQL技术栈适用于计算机专业学生完成课程设计或期末大作业。项目曾作为大三学期期末大作业获得98分高分结构完整、功能覆盖学生、教师、成绩、考试等模块并包含数据库文件可直接导入运行或二次开发。资源包共353个文件大小约9.85MB涵盖Java源码与class文件、JSP页面、CSS样式、JS脚本、XML配置以及数据库SQL文件等类型同时包含大量界面截图和设计文档便于对照理解。截至目前已有198人学习浏览适合正在搭建JavaWeb项目、需要完整参考实现的中级学习者。通过该项目可以快速掌握分层架构、ServletJSP交互、JDBC数据处理及基础权限管理思路无论是答辩演示还是项目复盘都有实际参考价值。1. 学生成绩管理系统为什么JavaWeb课程设计都绕不开它如果你正在找一份能直接交差的JavaWeb课程设计类似“基于JavaWeb学生成绩管理系统源码Jsp MySQL含数据库文件”这样的标题一定不陌生。它几乎是JavaWeb学习路线里最典型的综合练习JSP做页面、Servlet控制请求、MySQL存数据把Java基础、JDBC、数据库设计和前端标签全部串起来。我见过不少人从网上拿到这类源码后卡在第一步IDEA里导入报错Tomcat没配对数据库文件导不进去页面跑起来只剩空壳。这篇文章就从环境配置讲起把源码的目录结构、数据库文件导入、核心功能的改法拆开讲最后把那些让你想砸电脑的坑提前排掉。不管你是做期末作业还是补课程设计跟着走完就能自己改、自己部署。2. 先把工程立起来IDEA 里跑通 JavaWeb 项目的最小配置标题里的“JavaWeb”已经说明了技术形态浏览器直接访问 JSP 页面Servlet 处理表单和跳转MySQL 保存学生、班级和成绩数据。拿到源码第一步不是急着改代码而是让项目在本地能被 Tomcat 认出来。这一步配置错了后面所有操作都是白费。我一般会先按“环境 → 目录 → 导入 → 部署”的顺序走逐个确认最后再点启动按钮。2.1 从标题反推项目技术栈和目录结构Jsp MySQL意味着这大概率是一个 JSP Servlet JDBC 的三层结构没有 Spring没有 Maven依赖包直接放在 WEB-INF/lib 下。null典型的目录结构长这样拿到手先对照一遍student-manager/ ├── src/ │ ├── com/xxx/dao/ // 数据访问层SQL都写在这里 │ ├── com/xxx/entity/ // 实体类对应表结构 │ ├── com/xxx/servlet/ // 控制层处理请求转发 │ └── com/xxx/util/ // JDBC工具类Connectoin管理 ├── web/ │ ├── css/ js/ images/ │ ├── WEB-INF/ │ │ ├── web.xml // Servlet映射、欢迎页 │ │ └── lib/ // mysql驱动、jstl标签库 ├── db/ │ └── student.sql // 数据库文件建表初始数据 └── README.md这个结构对应的是 Eclipse 的 Web 项目格式根目录叫web而不是WebContent。IDEA 导入时认的是web目录下的WEB-INF/web.xml只要这个文件存在IDEA 就能识别成 JavaWeb 模块。如果你拿到的源码是src和WebContent并列那通常是 Eclipse 老项目导入方式会不一样这个在第 5 章会讲。2.2 JDK、Tomcat、MySQL 的版本搭配怎么选版本搭配决定了你后面会不会踩“驱动类找不到”“时区报错”这类低级坑。我常用的组合是 JDK 8 Tomcat 8.5 MySQL 5.7或者 JDK 8 Tomcat 9 MySQL 8.0。没必要追求新版课程设计项目用 JDK 8最稳因为教材和大多数笔记都基于这个版本。null先确认本机环境命令如下java -version mysql --version # 再打开一个终端查看Tomcat版本 $CATALINA_HOME/bin/version.sh参数说明-version用来查看 JDK 具体版本必须出现1.8.0_xxx或“build 1.8”才算合格。mysql --version会打印类似mysql Ver 14.14 Distrib 5.7.44的信息注意看后面是 5.7 还是 8.0。这里的版本会直接决定 JDBC 驱动类名怎么写后面第 3 章会专门解释。如果本机没有 MySQL建议先按官方安装教程装 5.7.44 或 8.0安装时记住 root 密码字符集选 utf8mb4。装完再继续不要跳过这一步。2.3 导入源码到 IDEA 并配置 TomcatIDEA 里有两种导入方式一种是File - New - Project from Existing Sources选目录后让 IDEA 自动识别另一种是新建空项目然后把src和web目录手动拷进来。我一般用后者因为源码可能是从别处下载的自带的.idea或.classpath文件会干扰识别手动拷更干净。null导入后需要把项目标记成 Web 项目关键动作是让 IDEA 知道web目录是 Web 根目录。这一步在Project Structure里完成核心配置如下Project Structure - Modules - 选中当前模块 - 点“”选择 Web - Web Resource Directory 指向 web 目录 - 把 WEB-INF/lib 加入依赖Libraries这个配置决定了 Tomcat 启动时去哪里找.jsp文件和.jar包。如果lib目录没有加进去后面启动会直接报ClassNotFoundException错误信息会提到com.mysql.cj.jdbc.Driver或javax.servlet.jsp.jstl.core.LoopTag这类问题第 5 章排查。配置完模块再给 IDEA 加一个 Tomcat 运行环境。Run/Debug Configurations里点加号选 Tomcat Server - Local然后指定 Tomcat 安装目录。部署页签里把当前项目的war exploded加进去Application context 填/student这样启动后浏览器访问地址就是http://localhost:8080/student/index.jsp。这里我强烈建议用war exploded因为课程设计要频繁改页面这种部署方式支持热更新不用每次重启 Tomcat。上面这套流程做完点启动正常能看到 Tomcat 日志输出Server startup in xxx ms浏览器打开http://localhost:8080/student如果出现登录页环境就通了。如果在启动时端口被占用改conf/server.xml里的 8005/8080 端口或者换一个 Tomcat 实例这一步别硬扛。3. 数据库文件怎么用从建库到连上 MySQL标题专门强调“含数据库文件”说明这份源码里有一个.sql文件不需要你手工建表。但“导入即用”其实是理想情况实际会遇到 SQL 版本语法不兼容、字符集不对、甚至脚本里带了DROP DATABASE之类的坑。这一章就讲怎么把db/student.sql安全地变成可用的库以及 JDBC 怎么连进去。3.1 读懂 SQL 脚本表结构、初始化数据与事务打开student.sql先别急着执行。我习惯先看三样东西建库语句、建表语句、插入的数据。一份合格的学生成绩管理脚本至少有三张表——用户表、学生表、成绩表结构大致如下CREATE DATABASE IF NOT EXISTS student_db DEFAULT CHARACTER SET utf8mb4; USE student_db; CREATE TABLE t_user ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(20) NOT NULL UNIQUE, password VARCHAR(32) NOT NULL, role VARCHAR(10) DEFAULT student ); CREATE TABLE t_student ( id INT PRIMARY KEY AUTO_INCREMENT, stu_no VARCHAR(20) NOT NULL UNIQUE, name VARCHAR(30) NOT NULL, cls VARCHAR(30), phone VARCHAR(11) ); CREATE TABLE t_score ( id INT PRIMARY KEY AUTO_INCREMENT, stu_id INT NOT NULL, course VARCHAR(30) NOT NULL, score DECIMAL(5,2), exam_time DATE, FOREIGN KEY (stu_id) REFERENCES t_student(id) );逻辑说明t_user存登录账号role字段决定是学生还是管理员这是后面做权限的基础。t_student是学生基础信息t_score通过stu_id外键关联学生表一门课一条成绩。注意score用了DECIMAL(5,2)能存的最大值是999.99百分制完全够用而且不会出现浮点误差。参数说明外键FOREIGN KEY在这里起了约束作用插入成绩时如果stu_id在学生表里不存在会直接报错。这能防止脏数据但也意味着删除学生时必须先删成绩或者用ON DELETE CASCADE改成级联删除。很多课程设计源码喜欢用INT AUTO_INCREMENT做主键如果你看到脚本里没有外键也能跑但数据一致性就差了。3.2 用 MySQL 命令行导入 SQL 文件确认表结构没问题后开始导入。最干净的方式是命令行不要直接复制到 Navicat 的查询窗口里执行因为如果脚本里有DELIMITER$$之类的存储过程图形工具经常会执行错位。命令行导入命令如下mysql -uroot -p --default-character-setutf8mb4 db/student.sql参数说明-uroot指定用户-p表示需要输入密码这里没有写密码是因为会交互式提示避免密码泄露在终端历史里。--default-character-setutf8mb4很重要它告诉 MySQL 客户端SQL 文件里的中文是用 utf8mb4 编码的。如果你的源码当初用的是 gbk这里就写gbk否则表里会出现中文乱码。导入完成后验证一下表和数据mysql -uroot -p -e USE student_db; SHOW TABLES; SELECT COUNT(*) FROM t_student;-e参数表示执行一段 SQL 然后退出。如果输出里能看到三张表并且COUNT(*)返回非零说明数据文件导入成功。如果报ERROR 1064多半是 SQL 版本语法问题常见的是 MySQL 5.7 和 8.0 对DEFAULT值、int(11)这些写法要求不同可以手动把报错行改成通用写法。3.3 JDBC 连接配置与 db.properties数据库导入成功只是第一步Java 代码还得能连上它。源码里一般会有一个db.properties文件放在src根目录或者由DBUtil工具类直接读。它的核心内容长这样jdbc.drivercom.mysql.cj.jdbc.Driver jdbc.urljdbc:mysql://localhost:3306/student_db?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/Shanghai jdbc.usernameroot jdbc.password123456逻辑说明jdbc.driver是数据库驱动类名。MySQL 5.7 的驱动写com.mysql.jdbc.Driver8.0 的驱动写com.mysql.cj.jdbc.Driver两者不能混用。如果你的源码给的是 5.x 驱动但本机装了 MySQL 8.0启动时会报Public Key Retrieval is not allowed或者找不到类最简单的办法是统一换成 8.0 驱动并加useSSLfalse和serverTimezone。参数说明URL 里的characterEncodingutf8控制客户端和服务器之间的编码useUnicodetrue必须配合它一起写。serverTimezoneAsia/Shanghai是 MySQL 8.0 新增的强制要求不写会报时区错误。jdbc.username和jdbc.password要按你的实际 MySQL 账号改密码别用中文Tomcat 和纯 JDBC 对中文密码的支持不太稳定。改完这个文件后需要重启 Tomcat 才生效。4. 核心功能实现登录、成绩 CRUD 和 JSP 页面环境通了数据库也连上了接下来才是真正要和源代码打交道的部分。一份 98 分的课程设计核心功能无非三个登录、成绩的增删改查、JSP 页面展示。这里面的代码套路非常固定我会把每一层的写法和容易错的细节讲透。4.1 登录模块Servlet 处理表单与 Session 控制登录逻辑是所有 JavaWeb 项目的门面。常见的做法是 JSP 表单提交到LoginServletServlet 查数据库成功后把用户对象放进 Session失败则带着错误信息转发回登录页。核心代码片段如下WebServlet(/login) public class LoginServlet extends HttpServlet { // 如果你的项目不用注解就在 web.xml 里配 servlet-mapping protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String username req.getParameter(username); String password req.getParameter(password); UserDao dao new UserDao(); User user dao.findByUsernameAndPassword(username, password); if (user ! null) { req.getSession().setAttribute(loginUser, user); resp.sendRedirect(index.jsp); } else { req.setAttribute(error, 用户名或密码错误); req.getRequestDispatcher(login.jsp).forward(req, resp); } } }逻辑说明这里用了WebServlet(/login)注解属于 Servlet 3.0 以后不再需要web.xml的方式。如果源码是web.xml配置的你就在里面写servlet和servlet-mapping效果一样。关键是req.getSession().setAttribute(loginUser, user)这一步它把登录用户存在服务端后续每个 JSP 都能通过${sessionScope.loginUser}取到。sendRedirect和forward的区别要记牢重定向是浏览器重新发请求地址会变转发是服务端内部跳转地址栏不变。登录成功用重定向可以防止刷新页面重复提交表单登录失败用转发能保留错误信息。参数说明setAttribute和getParameter是两类不同的东西前者是往对象里塞数据后者是从 HTTP 请求里取表单值。很多新手把session.setAttribute(username, username)写成session.setParameter编译直接报错。4.2 成绩列表与条件查询DAO 层 SQL 拼接成绩管理系统的核心是查询而且往往要支持按学号、按课程、按分数段筛选。DAO 层最常见的写法是动态拼接 SQL但拼接必须用PreparedStatement的占位符不能直接塞字符串。参考实现如下public ListScoreVO query(String stuNo, String course, Double minScore, Double maxScore) { StringBuilder sql new StringBuilder(SELECT s.stu_no, s.name, sc.course, sc.score, sc.exam_time ); sql.append(FROM t_score sc JOIN t_student s ON sc.stu_id s.id WHERE 11); ListObject params new ArrayList(); if (stuNo ! null !stuNo.isEmpty()) { sql.append( AND s.stu_no ?); params.add(stuNo); } if (course ! null !course.isEmpty()) { sql.append( AND sc.course LIKE ?); params.add(% course %); } if (minScore ! null) { sql.append( AND sc.score ?); params.add(minScore); } if (maxScore ! null) { sql.append( AND sc.score ?); params.add(maxScore); } sql.append( ORDER BY sc.score DESC); return jdbcTemplate.queryList(sql.toString(), params); }逻辑说明WHERE 11这个写法看着奇怪但它是动态条件拼接的利器——后面所有条件都用AND开头不需要第一句单独判断要不要加WHERE。LIKE ?配合% course %可以实现课程名模糊查询这里不能写成LIKE %?%因为占位符不能出现在引号里。最后加了ORDER BY sc.score DESC就是热词里常说的 MySQL 排序按成绩从高到低显示这是成绩列表的默认排序。参数说明query方法的四个参数允许传null所以每个条件都做了空判断。如果你拿到的是if (args.length 0)那套老代码建议改成这种预编译写法它天然防 SQL 注入。jdbcTemplate如果源码里没有就是普通的PreparedStatementResultSet循环核心逻辑相同。4.3 新增和修改成绩事务管理与参数校验新增成绩不是INSERT一句话那么简单。学生表、成绩表、以及可能有的统计表任何一个插入失败都会导致数据不一致。所以写save方法时必须手动开事务。参考代码public boolean saveScore(Score score) { Connection conn null; try { conn DBUtil.getConnection(); conn.setAutoCommit(false); // 手动开启事务 String sql INSERT INTO t_score(stu_id, course, score, exam_time) VALUES (?,?,?,?); try (PreparedStatement ps conn.prepareStatement(sql)) { ps.setInt(1, score.getStuId()); ps.setString(2, score.getCourse()); ps.setBigDecimal(3, score.getScore()); ps.setDate(4, score.getExamTime()); ps.executeUpdate(); } conn.commit(); // 全部成功才提交 return true; } catch (SQLException e) { if (conn ! null) { try { conn.rollback(); } catch (SQLException ex) { ex.printStackTrace(); } } return false; } finally { if (conn ! null) { try { conn.setAutoCommit(true); conn.close(); } catch (SQLException e) {} } } }逻辑说明setAutoCommit(false)是把“每执行一条 SQL 就自动落盘”改为手动控制。这里只插入一条成绩看起来没必要但如果你后续还要更新学生表里的平均分、或者写成绩明细流水就必须等所有 SQL 都成功后再commit。任何一步SQLException都会执行rollback把已经执行的半截操作撤销这就是标题相关技术点里最容易被忽略的 MySQL 事务处理。参数说明setBigDecimal(3, score.getScore())对应表里的DECIMAL(5,2)如果用setDouble会有精度问题你插入 79.9 到库里变成 79.90 是小事但如果成绩有“优良中差”之类的等级计算浮点位差就会翻车。setDate接收的是java.sql.Date你在 Servlet 层拿到的是String要先Date.valueOf(str)转换。4.4 JSP 页面里的数据展示与个人信息页面JSP 页面是这套系统里改动最多的地方。课程设计评分很看页面观感但功能层面重点是会用 JSTL 遍历成绩列表。一个标准的成绩列表页核心片段% page contentTypetext/html;charsetUTF-8 languagejava % % taglib prefixc urihttp://java.sun.com/jsp/jstl/core % table border1 trth学号/thth姓名/thth课程/thth成绩/thth考试时间/th/tr c:forEach items${scoreList} varsc tr td${sc.stuNo}/td td${sc.name}/td td${sc.course}/td td${sc.score}/td td${sc.examTime}/td /tr /c:forEach /table逻辑说明${scoreList}是 Servlet 里request.setAttribute(scoreList, list)存进去的集合。c:forEach是 JSTL 核心标签按集合里的对象循环输出tr。${sc.stuNo}会调用ScoreVO的getStuNo()方法本质还是 getter。如果不引入 JSTL只能写% for(ScoreVO sc : (List)request.getAttribute(scoreList)) { %页面会很乱维护也难。参数说明页面开头两行% page %和% taglib %缺一不可。第一行指定了响应编码和语言第二行引入 JSTL 标签库引入的uri要和你WEB-INF/lib下的jstl-1.2.jar版本匹配否则会报can not find the tag library descriptor。个人信息页面则是更简单的展示从 Session 里取loginUser把用户名、角色、登录时间显示在页面上即可不需要再查库。5. 避坑指南从环境到运行时最常见的 5 个坑课程设计源码本身不难难的是环境差异。同一个项目在作者的电脑上能跑换到你电脑上就是一连串报错。下面这几个坑我几乎每次帮人调这种东西都会遇到按“现象→原因→解决”写清楚你遇到直接照着改。5.1 Tomcat 启动后 404项目部署名称与访问路径现象Tomcat 正常启动日志也没有异常但浏览器访问http://localhost:8080/student显示 404或者只能看到 Tomcat 首页。原因IDEA 部署时 Application context 填的和实际访问路径不一致。比如部署名称是student_war_exploded而你访问的是student或者源码里的web.xml把欢迎页配置成了login.html但根目录下没有这个文件。解决点开Run/Debug Configurations找到 Deployment 页签把 Application context 改成/student同时确认web.xml里的welcome-file指向真实存在的页面一般是login.jsp或index.jsp。改完重新部署不要只点刷新。5.2 MySQL 8.0 连接报错驱动类和时区设置现象启动 Tomcat 后第一次访问登录页后台日志抛java.sql.SQLException: The server time zone value Öйú±ê׼ʱ¼ä is unrecognized或者Unable to load authentication plugin caching_sha2_password。原因本机装的是 MySQL 8.0而源码里的 JDBC 驱动还是 MySQL 5.x 的com.mysql.jdbc.Driver。8.0 的驱动已经改名为com.mysql.cj.jdbc.Driver同时 8.0 默认时区不是 UTC需要显式指定。解决把db.properties里的驱动类改成com.mysql.cj.jdbc.DriverURL 末尾加上serverTimezoneAsia/Shanghai。如果还报 caching_sha2_password就在 MySQL 命令行执行ALTER USER rootlocalhost IDENTIFIED WITH mysql_native_password BY 密码;把认证方式改成老的再刷新权限。注意这个修改会影响所有用 root 的本地连接。5.3 中文乱码请求、响应、数据库三处编码统一现象页面显示姓名、课程名全是问号???或者提交中文表单到数据库后变成乱码。原因编码链条断裂。JSP 响应是UTF-8但 Servlet 读取请求参数用的是ISO-8859-1或者数据库表是latin1连接串里没加characterEncodingutf8。解决三步统一。第一步每张 JSP 顶部写% page contentTypetext/html;charsetUTF-8 %第二步在doPost方法里最先执行req.setCharacterEncoding(UTF-8)第三步确认连接的 URL 里有useUnicodetruecharacterEncodingutf8且建表时DEFAULT CHARSET是utf8mb4。如果表已经建错只能ALTER TABLE t_student CONVERT TO CHARACTER SET utf8mb4;但已有的乱码需要重新插入数据才能恢复。5.4 jar 包没进 ArtifactsClassNotFoundException现象Tomcat 启动成功但一访问包含 JSTL 标签的 JSP报java.lang.ClassNotFoundException: org.apache.taglibs.standard.tag.rt.core.ForEachTag。原因WEB-INF/lib下的jstl.jar和standard.jar没有打包进 Artifacts。IDEA 在导入项目时如果没把目录标记为依赖Tomcat 运行时根本看不到这些 jar。解决进入Project Structure - Artifacts在WEB-INF/lib下点加号选择自己的 jar 文件或Library Files把项目里的依赖全部加进去。然后Build - Rebuild Project重启 Tomcat。这个坑最容易出现在没有 Maven 的课程设计里手动拷 jar 的一定要注意这一步。5.5 数据库文件导入报错版本、SQL模式、自增值现象执行student.sql时报ERROR 1067 (42000): Invalid default value for create_time或者提示Unknown column sex in field list。原因源码里的 SQL 可能是针对 MySQL 5.6 老版本写的里面用了TIMESTAMP DEFAULT CURRENT_TIMESTAMP但没加DATETIME兼容写法或者是把desc、name这类保留字当成了列名。另外 MySQL 5.7 之后默认开启了STRICT_TRANS_TABLES对非法默认值更敏感。解决看报错行如果是DEFAULT CURRENT_TIMESTAMP改成DATETIME DEFAULT CURRENT_TIMESTAMP并确保字段类型是DATETIME如果是保留字问题给列名加上反引号比如CREATE TABLE t_user (desc VARCHAR(20))。偶尔还会遇到自增值冲突因为脚本里写死了AUTO_INCREMENT100而当前表已有更大值就改成AUTO_INCREMENT不带数字让 MySQL 自动处理。6. 从 98 分到上线还能加分的三个扩展点课程设计拿到 98 分说明基础功能做完整了但如果你想让这份代码真正能放到服务器上跑建议补三个点分页、权限、导出。这三个点投入不大却能把系统的工程感拉高一个档次。分页是最容易加的。现在成绩列表是全量查询学生一多页面就会卡。在 DAO 层加一个分页查询SELECT id, stu_no, name, course, score, exam_time FROM score_view ORDER BY exam_time DESC LIMIT ?, ?;第一个问号是偏移量(page-1) * pageSize第二个问号是每页条数。配合前端上一页下一页按钮用request.getParameter(page)控制页码即可。权限控制建议用 Servlet Filter 做。写一个LoginFilter在web.xml里映射/admin/*未登录用户请求这些路径时统一跳到login.jsp。这样一个注解都不同改就能让管理员页面和学生页面分开。导出功能是答辩加分项。常见做法是 JSP 生成不了 Excel改用 Servlet 输出流把成绩以 CSV 格式写出去用resp.setHeader(Content-Disposition, attachment; filenamescore.csv)浏览器就会直接下载文件。这个方法比引入 POI 更轻适合课程设计体量。我自己的习惯是拿到这类源码后先复制一份干净备份再动数据库连接和目录配置。改任何文件前先想清楚会不会影响原有结构尤其是web.xml和db.properties这两个文件错了整个项目就废了。版本坑能靠一把鼻涕一把泪踩平但备份才是后悔药希望帮到你。本文还有配套的精品资源点击获取
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进