ARTICLE DETAIL

资讯详情

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

JavaWeb教务管理系统实战:从环境搭建到选课成绩模块完整实现

JavaWeb教务管理系统实战:从环境搭建到选课成绩模块完整实现 简介这是一份面向JavaWeb初学者与课程设计学习者的教务管理系统项目源码基于MVC设计模式与DAO模式搭建采用Servlet、JSP、Filter与MySQL数据库实现运行于Windows下的Tomcat 7与JDK1.8环境。系统按角色划分为登录、学生、教师、教务员与系统管理员四大部分涵盖登录、找回与修改密码、注销以及学生成绩查询、选修与考级报名、学籍信息查看修改、考级成绩查询教师个人信息维护教务员成绩管理与报名名单管理管理员用户管理等功能结构完整、层次清晰。资源包共223个文件以82个class、82个java、38个jsp为主另含6个jar、4个xml及sql脚本等压缩包约2.6MB导入后按README.txt说明即可启动。目前已有11656人学习下载适合作为课程设计参考与二次开发基础部分功能留有扩展空间便于继续完善。1. 教务管理系统 JavaWeb 项目从课程设计到能写进简历的那一步很多同学做教务管理系统 JavaWeb 项目卡住的地方根本不是业务逻辑而是环境。IDEA 里 Tomcat 一启动就 404JSP 页面中文乱码MySQL 8 连不上驱动报Public Key Retrieval is not allowed改完一轮配置天都黑了。这个标题背后真正要解决的问题是用一套能跑通的 JavaWeb 技术栈把学生、课程、成绩、选课这几条核心业务串起来并且让它在本地稳定运行、能演示、能讲清楚。它适合三类人正在做课程设计的学生、想补一个完整 Web 项目经验的转行者、以及需要快速搭后台管理原型的人。技术选型上常见做法是 Servlet JSP JDBC 打底或者直接上 SpringBoot MyBatis 提效率两条路我都会讲清楚边界你按自己的时间预算选。2. 教务管理系统的业务边界与技术选型别一上来就堆框架2.1 先划清业务边界再决定表结构教务管理系统的功能看起来很多但真正撑起一个可演示项目的核心只有四块学生信息管理、课程信息管理、选课关系维护、成绩录入与查询。很多教程一上来就给你画十几个模块的思维导图结果做到一半发现连最基础的学生表和课程表的外键关系都没理清。我的建议是先把实体关系想明白一个学生可以选多门课一门课可以被多个学生选所以学生和课程之间是多对多必须有一张中间表来承载选课关系同时成绩字段挂在这张中间表上而不是挂在学生表或课程表上。具体来说核心表就四张student学号、姓名、班级、密码、course课程号、课程名、学分、授课教师、sc学号、课程号、成绩、teacher工号、姓名、密码。这四张表能覆盖 90% 的课程设计需求。如果你还想加排课、教室管理那是加分项但不要在第一版就做否则你会在还没跑通登录的时候就陷入排课冲突算法的泥潭。提示表名和字段名统一用小写加下划线避免 MySQL 在 Linux 下大小写敏感导致本地能跑、服务器上翻车。2.2 Servlet JSP 还是 SpringBoot两条路的真实成本对比这是最多人纠结的地方。我把两条路的关键差异列出来你对着自己的情况选。对比项Servlet JSP JDBCSpringBoot MyBatis环境搭建时间2-4 小时Tomcat 配置容易出问题30 分钟内嵌 Tomcat代码量大每个请求要写一个 Servlet小Controller 一个类搞定多个接口对理解 Web 原理的帮助高请求响应流程全透明中很多细节被框架封装适合场景课程设计要求体现底层原理想快速出成果、写进简历常见翻车点web.xml 配置、JSP 路径、中文乱码依赖版本冲突、Mapper 扫描不到如果你的课程设计明确要求「不能用框架」那就老老实实走 Servlet JSP把web.xml和注解两种配置方式都搞明白。如果没有这个限制我一般会直接上 SpringBoot因为省下来的时间可以花在业务逻辑和前端交互上项目看起来更完整。热搜词里「javaweb项目完整案例mysql」和「计算机英文文献关于springboot javaweb」其实指向的就是这两条路线选哪条都不丢人关键是你自己能讲清楚为什么选它。2.3 用 Maven 管依赖别手动往 lib 里丢 jar 包不管你选哪条路依赖管理都用 Maven。手动往WEB-INF/lib里丢 jar 包是十年前的做法版本冲突的时候你连谁依赖了谁都查不出来。下面是一个 Servlet JSP 路线的最小pom.xml依赖配置。dependencies !-- Servlet APITomcat 已提供scope 必须是 provided -- dependency groupIdjavax.servlet/groupId artifactIdjavax.servlet-api/artifactId version4.0.1/version scopeprovided/scope /dependency !-- JSP API同样由 Tomcat 提供 -- dependency groupIdjavax.servlet.jsp/groupId artifactIdjavax.servlet.jsp-api/artifactId version2.3.3/version scopeprovided/scope /dependency !-- MySQL 驱动注意 8.x 版本驱动类名变了 -- dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId version8.0.33/version /dependency !-- JSTLJSP 页面做循环和判断用别忘了 -- dependency groupIdjavax.servlet/groupId artifactIdjstl/artifactId version1.2/version /dependency /dependencies这里的关键参数是scope。Servlet 和 JSP 的 API 包必须设成provided因为 Tomcat 运行时自带这些类你如果打成compile一起放进WEB-INF/lib启动时会报类冲突症状是 Tomcat 日志里一堆ClassCastException或者NoSuchMethodError排查起来非常费劲。MySQL 驱动 8.x 的类名是com.mysql.cj.jdbc.Driver不再是 5.x 的com.mysql.jdbc.Driver连接 URL 也要加上时区和 SSL 参数这些在下一章会具体写。3. 从建表到跑通第一个页面本地环境搭建的完整路径3.1 数据库建表脚本与初始数据先把数据库建好后面所有代码都依赖它。下面这份 SQL 可以直接在 MySQL 8 里执行包含四张核心表和几条测试数据。CREATE DATABASE IF NOT EXISTS edu_sys DEFAULT CHARSET utf8mb4; USE edu_sys; CREATE TABLE student ( id INT PRIMARY KEY AUTO_INCREMENT, sno VARCHAR(20) NOT NULL UNIQUE COMMENT 学号, name VARCHAR(50) NOT NULL, class_name VARCHAR(50), password VARCHAR(100) NOT NULL DEFAULT 123456 ); CREATE TABLE course ( id INT PRIMARY KEY AUTO_INCREMENT, cno VARCHAR(20) NOT NULL UNIQUE COMMENT 课程号, cname VARCHAR(100) NOT NULL, credit DECIMAL(3,1) DEFAULT 2.0, teacher_name VARCHAR(50) ); CREATE TABLE sc ( id INT PRIMARY KEY AUTO_INCREMENT, sno VARCHAR(20) NOT NULL, cno VARCHAR(20) NOT NULL, score DECIMAL(5,1) DEFAULT NULL, UNIQUE KEY uk_sno_cno (sno, cno) ); INSERT INTO student (sno, name, class_name) VALUES (2024001, 张三, 计科2401), (2024002, 李四, 计科2401); INSERT INTO course (cno, cname, credit, teacher_name) VALUES (C001, 数据结构, 4.0, 王老师), (C002, 操作系统, 3.5, 刘老师);sc表上的UNIQUE KEY uk_sno_cno是必须加的它保证同一个学生不能重复选同一门课。这个约束在代码层面也要做判断但数据库层面的兜底能防止并发插入导致的脏数据。字符集用utf8mb4而不是utf8因为utf8在 MySQL 里其实是阉割版存不了 emoji 和部分生僻字学生姓名里如果有生僻字就会插入失败。3.2 IDEA 配置 Tomcat 与项目结构IDEA 里跑 JavaWeb 项目最容易翻车的就是 Artifact 配置。步骤我拆细一点第一步File → Project Structure → Modules确认你的源码目录src/main/java被标记为 Sources Rootsrc/main/webapp被标记为 Web Resource Directory。第二步Project Structure → Artifacts添加一个Web Application: Exploded输出目录默认是target/项目名。这里的关键是确认WEB-INF/lib里包含了所有compile范围的依赖如果 MySQL 驱动没进去运行时会报ClassNotFoundException。第三步Run → Edit Configurations添加 Tomcat Local在 Deployment 标签页里把刚才的 Artifact 加进去Application context 设成/edu。这样你的访问路径就是http://localhost:8080/edu/。第四步启动 Tomcat如果控制台没有报错但浏览器 404先检查webapp目录下有没有index.jsp以及web.xml里的welcome-file-list是否配置正确。注意IDEA 社区版没有 Tomcat 集成如果你用的是社区版要么换 Ultimate要么用 Maven 的tomcat7-maven-plugin插件跑命令是mvn tomcat7:run。3.3 JDBC 工具类与数据库连接参数连接数据库的代码不要散落在每个 Servlet 里抽一个工具类出来。下面这个DBUtil用静态块加载驱动提供获取连接和关闭资源的方法。public class DBUtil { // MySQL 8 必须加 serverTimezone 和 allowPublicKeyRetrieval private static final String URL jdbc:mysql://localhost:3306/edu_sys?useUnicodetruecharacterEncodingutf8 serverTimezoneAsia/ShanghaiallowPublicKeyRetrievaltrueuseSSLfalse; private static final String USER root; private static final String PWD 你的密码; static { try { Class.forName(com.mysql.cj.jdbc.Driver); } catch (ClassNotFoundException e) { throw new RuntimeException(MySQL 驱动加载失败, e); } } public static Connection getConn() throws SQLException { return DriverManager.getConnection(URL, USER, PWD); } public static void close(Connection c, Statement s, ResultSet r) { // 关闭顺序ResultSet - Statement - Connection try { if (r ! null) r.close(); } catch (SQLException ignored) {} try { if (s ! null) s.close(); } catch (SQLException ignored) {} try { if (c ! null) c.close(); } catch (SQLException ignored) {} } }URL 里三个参数必须加serverTimezoneAsia/Shanghai解决时区报错allowPublicKeyRetrievaltrue解决 MySQL 8 默认认证插件导致的连接拒绝useSSLfalse避免本地开发时的 SSL 警告。characterEncodingutf8配合数据库的utf8mb4使用能解决绝大部分中文乱码问题。如果你的密码里有特殊字符记得在 URL 里做转义或者干脆换一个纯字母数字的密码。3.4 登录功能的最小闭环Servlet JSP登录是第一个能验证「环境是否真的通了」的功能。写一个LoginServlet处理 POST 请求查库比对密码成功就跳转主页失败就回登录页带错误提示。WebServlet(/login) public class LoginServlet extends HttpServlet { Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setCharacterEncoding(UTF-8); // 必须在 getParameter 之前调用 String sno req.getParameter(sno); String pwd req.getParameter(password); String sql SELECT name FROM student WHERE sno ? AND password ?; try (Connection conn DBUtil.getConn(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setString(1, sno); ps.setString(2, pwd); try (ResultSet rs ps.executeQuery()) { if (rs.next()) { req.getSession().setAttribute(sno, sno); req.getSession().setAttribute(name, rs.getString(name)); resp.sendRedirect(req.getContextPath() /main.jsp); } else { req.setAttribute(msg, 学号或密码错误); req.getRequestDispatcher(/login.jsp).forward(req, resp); } } } catch (SQLException e) { throw new ServletException(登录查询失败, e); } } }req.setCharacterEncoding(UTF-8)这行必须放在所有getParameter之前否则 POST 中文参数会乱码。用PreparedStatement而不是字符串拼接是为了防 SQL 注入这也是面试常问的点。登录成功后把用户信息放进session后续页面通过session.getAttribute(sno)判断是否登录没登录就重定向回登录页。sendRedirect和forward的区别要清楚前者是浏览器重新发请求地址栏会变后者是服务器内部转发地址栏不变。登录成功用重定向防止用户刷新页面重复提交表单。4. 选课与成绩模块把多对多关系写对4.1 选课功能的防重复与容量控制选课的核心逻辑是往sc表插一条记录但必须处理两个边界重复选课和课程容量。重复选课靠数据库的唯一索引兜底代码里也要先查一次给出友好提示。容量控制需要在course表加一个max_num字段选课前统计已选人数。public String selectCourse(String sno, String cno) { String checkSql SELECT COUNT(*) FROM sc WHERE sno ? AND cno ?; String countSql SELECT COUNT(*) FROM sc WHERE cno ?; String capSql SELECT max_num FROM course WHERE cno ?; String insertSql INSERT INTO sc (sno, cno) VALUES (?, ?); try (Connection conn DBUtil.getConn()) { conn.setAutoCommit(false); // 开启事务防止并发超选 try (PreparedStatement ps1 conn.prepareStatement(checkSql)) { ps1.setString(1, sno); ps1.setString(2, cno); try (ResultSet rs ps1.executeQuery()) { if (rs.next() rs.getInt(1) 0) { conn.rollback(); return 你已经选过这门课了; } } } // 查容量和已选人数 int maxNum, currentNum; try (PreparedStatement ps2 conn.prepareStatement(capSql)) { ps2.setString(1, cno); try (ResultSet rs ps2.executeQuery()) { if (!rs.next()) { conn.rollback(); return 课程不存在; } maxNum rs.getInt(max_num); } } try (PreparedStatement ps3 conn.prepareStatement(countSql)) { ps3.setString(1, cno); try (ResultSet rs ps3.executeQuery()) { rs.next(); currentNum rs.getInt(1); } } if (currentNum maxNum) { conn.rollback(); return 课程已满; } try (PreparedStatement ps4 conn.prepareStatement(insertSql)) { ps4.setString(1, sno); ps4.setString(2, cno); ps4.executeUpdate(); } conn.commit(); return 选课成功; } catch (SQLException e) { throw new RuntimeException(选课失败, e); } }这里用事务包住「查重 → 查容量 → 插入」三步是为了防止两个学生同时选最后一个名额时都查到currentNum maxNum然后都插入成功。setAutoCommit(false)之后任何一步失败都要rollback成功才commit。这个逻辑在课程设计里能讲出「并发控制」四个字面试时是个加分项。当然更严谨的做法是在course表加乐观锁版本号或者用SELECT ... FOR UPDATE行锁但课程设计级别的事务已经够用了。4.2 成绩录入与查询的 SQL 写法成绩录入是教师端功能本质是UPDATE sc SET score ? WHERE sno ? AND cno ?。查询学生成绩单需要三表联查把学生、课程、成绩拼在一起。-- 查询某个学生的完整成绩单 SELECT s.sno, s.name, c.cname, c.credit, sc.score FROM sc JOIN student s ON sc.sno s.sno JOIN course c ON sc.cno c.cno WHERE sc.sno ? ORDER BY c.cno; -- 统计某门课的平均分、最高分、最低分 SELECT c.cname, AVG(sc.score) AS avg_score, MAX(sc.score) AS max_score, MIN(sc.score) AS min_score, COUNT(sc.score) AS cnt FROM sc JOIN course c ON sc.cno c.cno WHERE sc.cno ? GROUP BY c.cno, c.cname;三表联查的JOIN顺序会影响性能但在这个数据量下可以忽略。关键是WHERE条件要放在JOIN之后GROUP BY要包含所有非聚合列。如果查出来的成绩是null说明学生选了课但还没录入成绩前端要显示「未录入」而不是空白。统计查询里COUNT(sc.score)只统计非空成绩如果用COUNT(*)会把没录成绩的记录也算进去平均分就偏了。4.3 JSP 页面用 JSTL 渲染数据表格JSP 里不要写 Java 脚本片段用 JSTL 和 EL 表达式。下面是一个成绩单表格的写法。% 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} varrow tr td${row.sno}/td td${row.name}/td td${row.cname}/td td${row.credit}/td td c:choose c:when test${empty row.score}未录入/c:when c:otherwise fmt:formatNumber value${row.score} pattern0.0/ /c:otherwise /c:choose /td /tr /c:forEach /tablec:forEach的items属性对应request或session里的List对象var是循环变量。c:choose用来处理成绩为空的情况fmt:formatNumber把BigDecimal格式化成一位小数。JSTL 的uri地址如果写错页面会直接报 500 错误提示找不到标签库这时候检查pom.xml里 JSTL 依赖是否引入、web.xml版本是否太低2.3 以下不支持 EL。5. 部署与联调避坑那些让你加班到凌晨的细节5.1 中文乱码的三层排查现象登录页输入中文姓名提交后数据库里存的是???或者页面显示å¼ ä¸‰。原因乱码可能发生在三个环节——浏览器提交编码、服务器接收编码、数据库存储编码。任何一层不一致都会乱。解决第一层JSP 页面顶部加% page contentTypetext/html;charsetUTF-8 languagejava %HTML 的meta charsetUTF-8也要加。第二层Servlet 里request.setCharacterEncoding(UTF-8)和response.setContentType(text/html;charsetUTF-8)都要写。第三层数据库和表的字符集确认是utf8mb4连接 URL 里加characterEncodingutf8。三层都对齐了乱码基本消失。如果还有检查 Tomcat 的server.xml里 Connector 的URIEncoding是否设成UTF-8。5.2 Tomcat 启动报 404 的四种可能现象Tomcat 启动日志没有异常但访问http://localhost:8080/edu/login.jsp返回 404。原因和解决第一种Application context 配错了IDEA 里 Deployment 的 context 是/edu你访问的时候漏了或者写成了别的。第二种Artifact 没构建target目录下没有edu文件夹重新 Build Artifact 即可。第三种web.xml里welcome-file-list配的首页文件不存在或者文件名大小写不对。第四种JSP 文件放在了WEB-INF下面这个目录是受保护的浏览器不能直接访问必须通过 Servlet 转发。排查顺序就是从 context 到 artifact 到文件路径一层层看。5.3 MySQL 8 连接失败的三个高频报错现象一Public Key Retrieval is not allowed。原因MySQL 8 默认用caching_sha2_password认证插件JDBC 驱动需要显式允许公钥检索。解决URL 加allowPublicKeyRetrievaltrue。现象二The server time zone value ???ú±ê×??± is unrecognized。原因MySQL 服务器时区没配驱动解析不了。解决URL 加serverTimezoneAsia/Shanghai。现象三Access denied for user rootlocalhost。原因密码错了或者 root 用户不允许从 localhost 以外连接。解决确认密码必要时用ALTER USER rootlocalhost IDENTIFIED BY 新密码;重置。如果是在 Docker 里跑 MySQL还要确认端口映射和bind-address配置。5.4 JSP 修改后不生效的缓存问题现象改了 JSP 页面刷新浏览器还是旧内容。原因Tomcat 会缓存编译后的 JSP ServletIDEA 的热部署有时候不触发重新编译。解决在 Tomcat 配置里把On frame deactivation设成Update classes and resources或者直接点 IDEA 的Build → Rebuild Project然后重启 Tomcat。更彻底的办法是在web.xml里加jsp-config配置development模式但生产环境不要这么做。我一般习惯改完 JSP 按CtrlF5强制刷新能解决大部分缓存问题。6. 从能跑到能讲项目收尾的三个进阶技巧项目跑通之后真正拉开差距的是你能不能把它讲清楚、能不能在演示时不出岔子。第一个技巧是加一层简单的权限过滤。用Filter拦截所有/main/*和/student/*路径检查session里有没有sno没有就踢回登录页。代码很短但演示的时候能体现「未登录不能直接访问内部页面」的安全意识。WebFilter(urlPatterns {/main.jsp, /course/*, /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; Object sno request.getSession().getAttribute(sno); if (sno null) { response.sendRedirect(request.getContextPath() /login.jsp); return; } chain.doFilter(req, resp); // 已登录放行 } }第二个技巧是准备一份「演示数据脚本」。答辩或面试演示时最怕数据库里只有两条测试数据页面看起来空荡荡。提前写好一份包含 20 个学生、8 门课、若干选课和成绩记录的 SQL 脚本演示前一键执行。数据要真实一点姓名用常见中文名课程用「数据结构」「计算机网络」这类成绩分布在 60 到 95 之间看起来像真的。第三个技巧是给项目加一个README.md写清楚技术栈、启动步骤、数据库配置、默认账号密码。很多人项目做得不错但别人拿到之后跑不起来问题就出在没写清楚环境要求。README 里把 JDK 版本、MySQL 版本、Tomcat 版本、IDEA 配置要点列成表格再附上建表脚本的执行顺序。这个习惯在团队协作里非常加分。我自己做第一个教务管理系统的时候在 Tomcat 配置上耗了整整一个周末最后发现是 Artifact 里没加 MySQL 驱动。后来我养成了一个习惯每次新建 JavaWeb 项目先把数据库连接跑通写一个main方法测试DBUtil.getConn()能不能拿到连接再去写 Servlet。这个顺序能帮你把环境问题和代码问题分开排查起来快很多。希望帮到你。本文还有配套的精品资源点击获取
返回列表