JavaWeb教务管理系统开发实战:从环境搭建到核心代码实现
简介一份面向初学者的教务管理系统JavaWeb项目源码采用MVC设计模式与DAO分层思想综合运用Servlet、JSP、Filter及MySQL数据库完成开发。系统涵盖登录认证、找回密码、修改密码、注销以及学生端的成绩查询、选修与考级报名、学籍信息维护教师端个人信息管理教务员端成绩与报名名单管理管理员端用户管理等模块适合JavaWeb入门者结合源码与SQL脚本研读。开发环境为Eclipse与Tomcat 7.0.84资源包共223个文件打包为RAR格式大小仅2.6MB其中包含82个Java源文件、82个Class文件、38个JSP页面、6个JAR依赖包以及SQL数据库脚本和项目配置工程文件目录结构清晰便于逐层学习。随包附有README.txt启动说明能帮助快速部署运行。目前已有11654人浏览学习项目本身预留了可继续扩展的功能点既可用于毕业设计参考也适合二次开发练手。 不管是做课程设计、毕业设计还是单纯想把JavaWeb技术栈完整过一遍教务管理系统都是我见过出现频率最高的练手项目之一。这个方向热度高不是因为名字好听而是因为它的业务链足够完整学生、教师、课程、选课、成绩一环扣一环每个模块都是典型的数据增删改查但又不止是增删改查里面还夹着登录认证、权限控制、事务管理、分页查询这类高频面试点。这篇文章我就从选题思路开始讲一直讲到环境搭建、表结构设计、核心代码实现、常见排查把做这一个项目涉及的完整过程都摊开来说给准备动手或者正在卡壳的人做一个参考。1. 这个项目到底在做什么为什么值得选1.1 教务管理系统的核心业务链路先说清楚业务本身。教务管理系统面向三类用户学生、教师、教务管理员。学生要能登录、查看可选课程、选课、退课、查成绩教师要能登录、查看自己教的课程、录入和修改成绩管理员要做系统配置比如维护学生信息、教师信息、课程信息处理开课和停课。看上去功能很多但如果把流程放到真实教务场景里走一遍核心其实是一条非常清晰的主链路管理员开课学生选课教师上课并录成绩学生查成绩。系统里的所有模块包括用户管理、班级维护、课程维护、选课管理、成绩管理本质上都是围绕这条链路展开的。比如管理员开课要设置课程名称、学分、容量、授课教师学生选课要看到课程的剩余名额教师录成绩的时候还要能按班级筛选学生。把这些角色和流程理清楚之后项目的边界就出来了后面设计表结构时也会自然很多。1.2 用JavaWeb做这套系统优势在哪很多同学会纠结现在都流行Spring Boot为什么还要用传统的Servlet、JSP、JDBC那套JavaWeb技术我的看法是如果目标是课设、毕设或者想借一个项目把JavaWeb基础打牢传统JavaWeb反而是更合适的路线。原因有三个。第一这套技术组合能逼着你把HTTP请求、Servlet生命周期、Session会话、JDBC操作这些底层机制真正搞明白。Spring Boot帮你封装掉的内容越多你对底层越没概念面试时一旦被问“一个请求从浏览器发出后经过哪些步骤才返回页面”很容易卡壳。第二教务管理系统的业务复杂度刚好卡在“简单项目”和“企业级项目”之间用传统JavaWeb能完整实现又不会因为代码量太大而失控。第三传统JavaWeb项目部署很直接打成war包丢进Tomcat就能跑答辩演示时不容易因为环境问题翻车。所以如果你是刚学完JavaSE准备通过一个Web项目把这些知识串起来教务管理系统是一个非常合适的切入点。等你把这套流程跑通之后再去学Spring Boot、MyBatis会发现很多概念都是熟悉的只是换了一种更优雅的写法。2. 技术选型与开发环境搭建的一套稳妥方案2.1 技术栈怎么搭配既不过时也不过度设计直接给一套比较稳妥的组合Maven Servlet JSP JSTL JDBC MySQL Tomcat前端用JSP页面配合原生JavaScript再加一个轻量级CSS框架让界面不至于太粗糙。这套组合在大多数JavaWeb课程完整案例里都能看到最大的好处是资料多、遇到问题容易搜到答案。如果你想让代码更接近企业开发习惯可以把JDBC换成MyBatis减少DAO层大量重复代码但前提是你对预编译SQL、ResultSet映射这些基础操作已经熟。我的建议是第一版项目先用原生JDBC把手写CRUD、事务控制的流程走一遍之后再考虑引入MyBatis。还有一点不用太纠结后台管理界面用不用Layui、ElementUI之类的框架。前期先保证功能稳定界面能看就行。你真正需要关注的是后端逻辑界面后面要升级随时可以换。2.2 从零创建JavaWeb项目IDEA 2023实测路径我用的是IDEA 2023版本整体步骤和旧版差不多但有几个细节需要注意。第一步新建项目时左侧选Maven右侧勾选“Create from archetype”然后选择maven-archetype-webapp。如果下拉列表里没有说明模板列表还没加载出来等一会儿或者检查一下网络也可以手动输入模板坐标。第二步填好GroupId和ArtifactId等Maven把骨架拉下来。第三步打开生成的pom.xml确认打包方式是war然后加入Servlet、JSP、JSTL、MySQL驱动这几个核心依赖。下面是我用的最小依赖配置。dependencies dependency groupIdjavax.servlet/groupId artifactIdjavax.servlet-api/artifactId version4.0.1/version scopeprovided/scope /dependency dependency groupIdjavax.servlet.jsp/groupId artifactIdjavax.servlet.jsp-api/artifactId version2.3.3/version scopeprovided/scope /dependency dependency groupIdjavax.servlet/groupId artifactIdjstl/artifactId version1.2/version /dependency dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId version8.0.33/version /dependency /dependencies依赖配置好之后点击右上角运行配置添加Tomcat ServerLocal类型指定本地Tomcat安装目录和JDK。然后在Deployment选项卡里把项目的war包加进去设置Application context为/cms。启动后看到Tomcat started on port 8080说明环境就绪。这一步有一个很常见的坑本机如果已经装了其他Web服务占用了8080端口Tomcat会启动失败报端口被占用。直接在Tomcat配置界面把HTTP port改成8081或者别的端口就行不用去系统进程里硬杀。3. 数据库设计先把表结构理清后面少返工3.1 从角色和业务流程反推数据表做数据库设计时我习惯把自己代入教务管理员的角色模拟一遍日常操作。新学期开始管理员录入班级、学生、教师、课程学生登录系统看到课程列表选课老师登录看到自己教的课程和名单录成绩。顺着这个流程往下想核心表基本就浮出水面了用户表、学生表、教师表、班级表、课程表、选课表、成绩表。用户表负责登录认证角色字段区分学生、教师、管理员学生表和教师表各自扩展业务属性并通过user_id和用户表关联。课程表保存课程本身的信息比如课程名、学分、容量、授课教师。选课表是学生和课程的多对多关联表成绩表则关联到具体的选课记录。先把这个关系图画清楚再写建表SQL基本不会返工。3.2 关键建表SQL与字段设计说明下面是我用过的建表SQL去掉了部分冗余字段保留了最核心的部分。CREATE TABLE sys_user ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(64) NOT NULL, role VARCHAR(10) NOT NULL ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE student ( student_id VARCHAR(20) PRIMARY KEY, name VARCHAR(30) NOT NULL, gender CHAR(1), class_id INT, user_id INT ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE course ( course_id INT PRIMARY KEY AUTO_INCREMENT, course_name VARCHAR(50) NOT NULL, credit DECIMAL(3,1), teacher_id VARCHAR(20), capacity INT DEFAULT 30, selected_count INT DEFAULT 0 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE student_course ( id INT PRIMARY KEY AUTO_INCREMENT, student_id VARCHAR(20), course_id INT, status TINYINT DEFAULT 1, create_time DATETIME, UNIQUE KEY uk_student_course (student_id, course_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE score ( id INT PRIMARY KEY AUTO_INCREMENT, student_course_id INT NOT NULL, score DECIMAL(5,2), update_time DATETIME ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;这几张表背后有几点值得注意。sys_user里password字段长度我留了64位因为密码不会明文存一般用MD5加盐或者SHA-256做哈希哈希结果长度会长一些。student_id用VARCHAR而不是INT因为学号可能是类似“2024001”这种以0开头的字符串如果设计成INT前导零会被丢掉。student_course表加了唯一约束从数据库层面防止一个学生重复选同一门课这个方法比在代码里select再判断要可靠得多。course表里的selected_count字段是冗余字段但用它来做选课人数上限判断时效率很高不用每次去count选课表。3.3 数据访问层的三个“不要”这块基本都是踩坑踩出来的经验写出来提醒一下。第一不要用Statement拼接SQL尤其是字符串类型的查询条件。一方面存在SQL注入风险另一方面手拼SQL很容易漏引号最好统一用PreparedStatement占位符。第二不要把事务放在DAO层。DAO里的一个方法只代表一次数据库操作但一个业务往往涉及多次操作比如选课要插入记录、更新人数这两个操作必须放在同一个事务里而事务边界应该控制在Service层用同一个Connection去执行两个DAO方法最后统一提交或回滚。第三不要忽略连接资源的关闭。Tomcat的数据库连接资源是有限的尤其在循环查询的场景里忘记关闭连接很快就会打满连接池后面的请求全部排队超时。用完就在finally里关闭ResultSet、Statement、Connection或者用try-with-resources更省心。4. 核心功能模块的落地与代码实现4.1 登录认证和角色权限控制登录模块是所有功能模块的入口逻辑本身不复杂用户提交用户名和密码Servlet调用Service层查sys_user表比对密码。密码比对时不是明文比较数据库里存的是哈希值前端传来的明文密码在Service层做同样的哈希后再比对。登录成功之后把用户对象放进Session后续请求通过Session判断登录状态。只做登录判断还不够教务管理系统要区分角色学生只能进学生模块教师只能进教师模块。我的做法是写一个登录过滤器统一拦截请求放行登录页、登录接口和静态资源其他所有请求都必须有Session里的loginUser否则直接重定向回登录页。角色判断放在各自主的Servlet里这样职责更清晰。WebFilter(/*) 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) || uri.endsWith(/login.jsp) || uri.contains(/css/) || uri.contains(/js/)) { chain.doFilter(request, response); return; } Object loginUser request.getSession().getAttribute(loginUser); if (loginUser null) { response.sendRedirect(request.getContextPath() /login.jsp); } else { chain.doFilter(request, response); } } }过滤器只判断“是否登录”不判断“是什么角色”。角色校验放到具体的Servlet里比如课程管理相关的Servlet先判断当前session里的用户角色是不是管理员不是就返回无权限提示这样登录和授权各管一段代码不会糊在一起。4.2 选课/退课的事务边界选课这个功能看起来简单但最容易出现数据问题。一次选课操作涉及两步往student_course表插入一条选课记录然后把course表的selected_count加1。如果第一步成功第二步失败就会出现选了课但人数没变或者人数变了但没选上的情况。解决办法是使用同一个Connection手动控制事务代码如下。Connection conn JdbcUtil.getConnection(); conn.setAutoCommit(false); try { // 1. 校验是否已选这门课 // 2. 校验课程容量当前已选人数小于capacity // 3. 插入选课记录 // 4. 更新课程已选人数 conn.commit(); } catch (Exception e) { conn.rollback(); throw e; } finally { JdbcUtil.release(conn, null, null); }事务边界一定要放在Service层不要在Servlet里写这些逻辑。另外在并发选课场景下代码层面的“先查后插”可能出现两个人同时选最后一门课的情况所以数据库层面的唯一约束和容量校验依然要保留所谓兜底就是不管代码怎么写数据库都要有最后一道防线。4.3 成绩录入与查询的权限细节成绩模块最容易漏掉权限细节。教师登录之后只能录入自己教的课程的成绩不能通过改前端参数去录其他老师的课。所以教师查课程列表时SQL里要带teacher_id条件录入时要再次校验当前登录教师和课程teacher_id匹配。学生端查成绩时只查当前学生自己的记录用Session里的学生ID作为查询条件。分数本身也要做范围校验百分制就限制0到100之间否则数据库层面虽然不会报错但业务上会出现负分、超过满分的脏数据。录入时间也建议记录一下方便到时候出现成绩争议时追溯。4.4 分页查询的封装思路数据量上来之后学生列表、课程列表、成绩列表都需要分页。我不太推荐一上来就引入PageHelper这种第三方工具最好自己写一个简单的分页工具类也能加深对分页原理的理解。核心字段就几个pageNum当前页码、pageSize每页条数、totalCount总记录数、totalPages总页数再加一个存当前页数据的List。SQL层面先执行SELECT COUNT(*)拿总数再执行SELECT * LIMIT offset, pageSize拿当前页数据offset计算公式是(pageNum - 1) * pageSize。JSP里只需要展示“上一页”“下一页”和当前页码把页码参数传给后台Servlet。这里注意一点分页参数要用Integer来接收不要用字符串拼接SQL否则很容易类型转换异常或者出安全问题。5. 实战问题排查与避坑记录5.1 环境类问题速查表做这个项目过程中最容易翻车的都是环境问题我把高频的整理成了一个速查表遇到可以对照检查。现象常见原因解决办法Tomcat启动报端口被占用8080被其他进程占用改Tomcat HTTP port或清理占用进程运行主类找不到Java类Maven依赖没引入或者provided依赖没生效检查pom.xml确认依赖坐标和版本页面能打开但Servlet返回404WebServlet路径配错或部署上下文不对核对访问路径和Application context中文乱码页面编码、请求编码、数据库编码不一致统一UTF-8连接参数加characterEncodingutf8连接MySQL报Public Key Retrieval错误MySQL 8.0连接参数缺失JDBC URL加useSSLfalseallowPublicKeyRetrievaltrueJSTL标签不生效页面没引入JSTL标签库JSP顶部加taglib引用确认jstl依赖存在这些环境类问题里乱码和MySQL连接失败是出现次数最多的。建议拿到一个项目骨架之后先写一个最简单的“查询数据库当前时间并在页面显示”的Servlet先把环境问题清零再开始写业务。这样后面出问题时基本可以确定是业务代码的问题而不是环境问题。5.2 一套从报错到定位的排查思路很多同学遇到报错的第一反应是盯着浏览器页面看但500页面的信息往往很有限。我的习惯是先把IDEA控制台的异常栈完整看一遍重点找“Caused by”那一行那行信息通常会直接告诉你SQL哪里错了、表不存在、字段名字不对还是空指针。如果异常栈里没看出问题就在Servlet入口处打印日志把前端传来的参数打出来看看。很多时候问题不在SQL本身而是前端传到后台的参数本来就是null或者类型不对。还有一种容易混淆的情况JSP页面报错不一定是JSP的问题可能是EL表达式写错拿不到List属性页面就会抛异常。这种问题看JSP编译后的详细异常内容比猜快得多。最后再分享一点个人经验做这个项目最容易翻车的地方往往不在代码技术本身而在前期思路乱。我见过好多人一打开IDEA就开始写数据库表写完几张表又觉得不对改来改去最后DAO层、Service层全在打补丁。所以我要在这里多说一句动手写代码之前先花半天时间把角色、业务流程、表关系在纸上捋一遍理不通绝对不要急着往下写。教务管理系统这个项目的价值不只是交一份课设作业或毕设代码它几乎把JavaWeb阶段的核心知识全部串了起来。做完一遍你会对请求处理、Session管理、数据库操作、事务控制、异常排查这些关键能力有真真切切的体感这时候再进入Spring、MyBatis这些框架阶段学起来会轻松很多。希望这些过程和踩坑经验能对你有用也欢迎聊聊你在做这个项目时遇到的问题。本文还有配套的精品资源点击获取