ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

JavaWeb学生选课管理系统源码部署:JSP+Servlet+JDBC+MySQL全链路解析

2026/10/7 14:08:21 拓冰建站 浏览量
JavaWeb学生选课管理系统源码部署:JSP+Servlet+JDBC+MySQL全链路解析 简介一套基于Servlet和JSP技术实现的学生选课管理系统面向计算机相关专业需要毕业设计参考或项目实战练习的Java学习者覆盖管理员、教师、学生三类角色的核心业务场景。系统后端采用JavaWeb与MySQL前端结合JSP、CSS、Bootstrap和jQuery完整实现学生信息管理、教师课程管理、成绩录入与查询、学生选课等典型功能并内置权限控制逻辑适合作为毕设方案二次开发。压缩包有112个文件含22个Java源码类、22个class编译文件、21个JSP页面、10个jar依赖以及数据库SQL脚本、样式表、脚本文件、图片和项目配置总大小约2.39MB导入开发工具并配置数据库即可运行。该资源已有554人学习下载对正在完成毕业设计或希望快速掌握传统JavaWeb分层开发的读者是一份可直接运行、便于改造的完整范例。1. 学生选课管理系统这套javaWeb源码到底能不能直接上手跑很多人在网上下载了“javaWeb学生选课管理系统(完整源码数据库)”这类资源第一反应是导入IDEA结果要么一堆红叉、要么Tomcat起不来、要么数据库脚本导入直接报错最后不得不去求助“idea运行javaweb项目配置”的帖子。这套系统说白了就是一个JSPServletJDBCMySQL的经典三层架构课程设计主要解决学生在线选课、退课管理员维护课程信息和查看选课统计的需求。它不像Spring Boot那么讲究工程化但胜在技术栈直接对应课本非常适合刚学完Servlet和JDBC的初学者、需要交课程设计或毕业设计案例的人以及想完整看一遍“请求→Servlet→DAO→数据库→JSP渲染”全链路的转行者。但拿到源码只是开始能不能跑起来、跑起来之后怎么验证功能是对的才是真正见功夫的地方。我下面会按“拆解系统→设计数据库→读懂核心代码→在IDEA里跑通→避坑→优化”这条线把整套流程讲透。2. 拆开这套学生选课管理系统功能模块、技术选型与源码结构2.1 三个角色、两条业务线功能需求先理清任何一个学生选课管理系统核心角色就两个学生和管理员。有的版本会加一个“教师”角色用来维护课程和录入成绩但最常见的课程设计版本还是以学生和管理员为主。学生这条业务线登录、浏览可选课程、选课、退课、查看自己已选的课程列表。注意这里有个隐含需求——选课之前要判断课程是否已满退课之后要释放名额不然会出现“选课人数超过容量”这种数据不一致问题。管理员这条业务线登录、课程的增删改查、查看某门课程的选课学生名单、统计每门课的选课人数。管理员和学生虽然都“登录”但权限完全不同学生不能访问管理员页面这就要求在Servlet层或Filter层做权限控制。拿到一套源码第一步不是急着跑而是先把web.xml里的URL映射和Servlet类名过一遍弄清楚每个入口对应哪个Servlet页面跳转的路径是怎么设计的。否则后面排查问题的时候你会连“这个请求到底走了哪个类”都搞不清楚全部靠猜测效率极低。2.2 为什么是JSPServletJDBC而不是Spring Boot“javaWeb项目完整案例mysql”这个关键词下大多数资源还是JSPServlet的老骨架。很多初学者会问现在企业里都Spring Boot了学这个是不是过时了我的看法是课程设计这个场景JSPServlet恰恰是最合适的。第一它直接对应学校教材的章节答辩的时候老师问“请求是怎么从页面到数据库再返回的”你能用手画出来整个链路。第二它没有Spring的自动配置黑匣子所有连接都要自己写所有对象都要自己new反而能逼着你理解HTTP、Session、JDBC这些基础概念。第三Tomcat部署一个war包就能跑不像Spring Boot还得理解内嵌容器。等你把这套系统调通再去看Spring Boot里的DispatcherServlet、MyBatis这些封装会轻松很多。当然如果你拿到的源码是Maven工程并且用了Spring MVC那也别拒绝这代表它更像是企业级项目的简化版跑通之后价值更高。但本文后面以最常见的“纯JSPServletJDBCMySQL”版本为主线来讲因为大多数“完整源码数据库”的资源都是这个形态。2.3 拿到源码先看这五个文件目录结构清单解压源码包之后先别急着导入IDEA花五分钟对着目录结构认一遍文件。常见的目录结构长这样路径作用src/entity/实体类对应数据库表的Java对象例如Student.java、Course.javasrc/dao/数据访问层封装JDBC连接和增删改查SQLsrc/servlet/控制器层接收HTTP请求调用DAO跳转JSPsrc/util/工具类通常是DBUtil数据库连接工具WebContent/JSP页面、CSS、JS、图片等静态资源sql/或者根目录下的.sql文件数据库初始化脚本建库建表测试数据WebContent/WEB-INF/web.xml核心配置文件Servlet映射、欢迎页、Filter配置这里最容易犯的错误是只关注src目录里的Java代码忽略了sql脚本和web.xml。实际上一个javaWeb项目能不能跑起来sql脚本决定数据层是否正常web.xml决定请求是否能被正确分发。很多“完整源码”下载下来发现缺web.xml或者sql脚本是空文件的情况我都见过所以我建议你解压之后先确认这三个东西都在源码目录、sql脚本、web.xml。3. 先把数据库跑起来选课管理系统的建库脚本与三张核心表3.1 从student、course、student_course说起表结构设计学生选课管理系统的数据库很简单但设计得好不好直接影响后面写代码的复杂度。核心就三张表student学生表、course课程表、student_course选课关联表。设计规范一点三张表就够了。student表常见字段id主键自增、stu_no学号、stu_name姓名、password密码、major专业。course表常见字段id主键自增、course_no课程编号、course_name课程名、teacher授课教师、credit学分、capacity容量、selected已选人数。student_course表常见字段id、student_id学生ID、course_id课程ID、select_time选课时间而且必须加一个联合唯一索引UNIQUE KEY uk_stu_course (student_id, course_id)。这个联合唯一索引非常重要。很多初版源码没有这个索引导致同一个学生可以重复选同一门课数据表里出现两条一模一样的记录。你可能会说“我的Service层已经判断过了”但程序判断总有漏洞数据库层面的唯一约束是兜底的。加了这个索引之后就算代码漏判数据库也会直接报Duplicate entry错误至少数据不会脏。课程表里的selected字段特别值得说一句。它表示“已选人数”每次有人选课成功就1退课就-1。这个字段的存在是为了在选课列表页直接显示“还剩多少个名额”避免每次都去count选课表。但如果你的源码里没有这个字段而是在查询的时候临时统计选课人数也完全可以只不过SQL会稍微复杂一点。两种方案各有取舍用冗余字段读写快但是要维护一致性用实时统计不会出错但课程多的时候查询会慢。课程设计阶段冗余字段事务控制更常见。3.2 导入MySQL的完整步骤与常用命令拿到sql脚本之后导入数据库的操作其实可以全程在黑窗口里完成。先确保你的MySQL服务是启动状态然后用root登录mysql -uroot -p输入密码之后先创建一个数据库再切换到该库最后执行source命令导入脚本CREATE DATABASE IF NOT EXISTS course_selection DEFAULT CHARSET utf8mb4; USE course_selection; SOURCE /路径/你的sql脚本文件.sql;这里要注意三点。第一路径里的反斜杠在Windows下要改成斜杠否则会报找不到文件。第二脚本文件的编码格式最好是UTF-8如果是GBK导入之后中文可能乱码这也是“数据库中文乱码”问题最常见的来源。第三如果脚本里已经写了CREATE DATABASE和USE语句那你直接SOURCE就行不用手动建库。导入完成之后用几条常用命令验证一下数据是否正常SHOW TABLES; SELECT COUNT(*) FROM student; SELECT * FROM course LIMIT 5;看到有数据输出说明数据库这块已经通了。如果SOURCE执行时报语法错误大概率是下载的sql脚本是用MySQL 8.0的语法导出、而你的MySQL是5.7或更低版本。这时打开sql文件检查有没有utf8mb4_0900_ai_ci这类字符集排序规则把它批量替换成utf8mb4_general_ci再导入即可。3.3 初始化数据为什么要带测试账号一个设计得好的sql脚本除了建表还会预置几条测试数据。比如管理员账号admin/123456学生账号2023001/123456以及几门课程、每门课的容量和已选人数。别小看这些测试数据。我见过不少下载下来的脚本只有建表语句没有任何数据结果你启动项目之后连登录页面都进不去因为数据库里根本没有这个用户。有测试数据的另一个好处是你跑通登录之后立刻能看到课程列表里有内容可以马上进行选课操作整个系统是否正常的验证链路是连续的不需要你自己再去手动INSERT一堆数据。如果你拿到的脚本里确实没有测试数据那就自己补上这是最省事的路子INSERT INTO student (stu_no, stu_name, password, major) VALUES (2023001, 张三, 123456, 计算机科学与技术); INSERT INTO admin (admin_name, password) VALUES (admin, admin123); INSERT INTO course (course_no, course_name, teacher, credit, capacity, selected) VALUES (C001, Java程序设计, 李老师, 3, 50, 0);注意admin表不是每个版本都有有的系统把管理员和学生放在同一张表用role字段区分角色。跑通之前先看清楚你的表结构别拿着我的SQL往上套。4. 看懂核心代码登录校验、选课事务与权限拦截4.1 登录Servlet从request参数到session会话登录功能是所有JavaWeb系统里最经典的入门案例学生选课管理系统的登录逻辑也不外乎这几步接收前端传来的用户名和密码去数据库查询比对成功就把用户信息放进session失败就返回错误提示。下面这段是简化后的LoginServlet核心逻辑WebServlet(/login) public class LoginServlet extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String username request.getParameter(username); String password request.getParameter(password); UserDao userDao new UserDao(); User user userDao.findByUsernameAndPassword(username, password); if (user ! null) { HttpSession session request.getSession(); session.setAttribute(user, user); if (admin.equals(user.getRole())) { response.sendRedirect(admin/courseList.jsp); } else { response.sendRedirect(student/courseList.jsp); } } else { request.setAttribute(error, 用户名或密码错误); request.getRequestDispatcher(login.jsp).forward(request, response); } } }这段代码的逻辑很直白从请求里拿参数、查库、判断、跳转。三个容易忽略的细节第一密码比对应该放在哪我见过有些源码把密码比对写在Servlet里也就是先把数据库里的密码查出来再在Servlet里equals。这样做不是不行但最好把“按用户名查用户”和“密码校验”两层分开否则以后想改成MD5加密存储时修改范围会很大搞不好就漏改一处导致登录全线崩盘。第二redirect和forward的区别。登录成功用sendRedirect因为要改变URL地址栏避免刷新页面时重复提交表单登录失败用forward因为需要把error信息带到JSP页面上。这个区别面试也常问值得在心里过一遍。第三session里存什么。只存用户ID和角色就够了不要存密码更不要存整个User对象到JSP里直接getPassword展示出来。这既是安全习惯也是给自己省麻烦。4.2 选课操作的DAO层一个坑位一个事务选课这个操作看似简单其实是一个典型的“先检查后更新”场景。学生点击选课后端要做的完整逻辑是判断课程是否存在→判断选课时间是否截止→判断课程是否已满→判断学生是否已经选过→插入选课记录→更新课程的selected字段。这六步里任何一步都可能出问题。最经典的问题就是并发两个学生同时点选课都查到了“还剩1个名额”然后同时执行INSERT结果课程人数超了。要解决这个问题代码层面必须加事务把“插入选课记录”和“更新课程已选人数”绑定在一起要么都成功要么都失败public boolean selectCourse(int studentId, int courseId) { Connection conn null; PreparedStatement ps1 null; PreparedStatement ps2 null; try { conn DBUtil.getConnection(); // 开启事务关闭自动提交 conn.setAutoCommit(false); // 第一步插入选课记录 String sql1 INSERT INTO student_course (student_id, course_id, select_time) VALUES (?, ?, NOW()); ps1 conn.prepareStatement(sql1); ps1.setInt(1, studentId); ps1.setInt(2, courseId); ps1.executeUpdate(); // 第二步更新课程已选人数 String sql2 UPDATE course SET selected selected 1 WHERE id ? AND selected capacity; ps2 conn.prepareStatement(sql2); ps2.setInt(1, courseId); int rows ps2.executeUpdate(); if (rows 0) { // 影响行数为0说明课程已满回滚 conn.rollback(); return false; } // 都成功才提交 conn.commit(); return true; } catch (Exception e) { if (conn ! null) { try { conn.rollback(); } catch (SQLException ex) { ex.printStackTrace(); } } e.printStackTrace(); return false; } finally { DBUtil.close(conn, ps1, ps2); } }很多人写选课事务的时候容易忽略一个细节UPDATE语句里要带上AND selected capacity这个条件。这样一来即使两个请求同时到达数据库也只能有一个UPDATE影响行数为1另一个影响行数为0从而触发回滚。这是利用数据库行锁来防止超卖的关键比在Java代码里加synchronized靠谱得多。另一个细节是NOW()这个函数。MySQL里插入当前时间NOW()和SYSDATE()都能用但在有主从复制的场景下NOW()更稳因为它返回的是语句开始执行的时间而不是实时时间。课程设计阶段用NOW()已经足够注意别写成CURRENT_TIMESTAMP常量虽然结果一样但阅读性差一些。4.3 web.xml与Filter学生凭什么进不了管理页登录校验只是第一步权限控制才是区分“能用”和“好用”的分水岭。如果学生把浏览器的URL直接改成/admin/courseManage.jsp结果页面照样打开那这套系统的权限就是摆设。用Filter做拦截是标准做法。在web.xml里配置一个过滤器拦截所有以.jsp结尾的请求检查session里有没有用户以及用户的角色是否匹配要访问的路径filter filter-namePermissionFilter/filter-name filter-classcom.cs.filter.PermissionFilter/filter-class /filter filter-mapping filter-namePermissionFilter/filter-name url-pattern/*/url-pattern /filter-mapping对应的Filter实现逻辑大致是放行login.jsp和静态资源其他请求都必须有user对象访问admin开头的路径必须是admin角色访问student开头的路径必须是student角色。这里有个坑Filter的url-pattern写在/*时它也会拦截login.jsp本身。如果你在Filter里忘记放行login.jsp就会出现“访问登录页面也被重定向到登录页面”的死循环跳转。这是我见过发生率极高的配置失误几乎每届学生都会在这上面翻一次车。web.xml里除了Filter还会配置Servlet映射和欢迎页。如果你拿到源码里web.xml没有配置任何Servlet映射说明用的是WebServlet注解方式。两种方式共存的时候要注意同一个Servlet路径不要同时出现在web.xml和注解里否则Tomcat启动时会报fatal error整个项目直接起不来。5. 在IDEA里跑通完整源码Tomcat配置、数据库连接与避坑指南5.1 用IDEA导入项目和配置Tomcat照着做就能起来的步骤数据库就绪之后总算轮到在IDEA里跑通项目了。这一步是“idea运行javaweb项目配置”热搜词背后最集中的痛点我按步骤说一遍。第一步导入项目。打开IDEAFile → New → Project from Existing Sources选中你解压后的源码目录。如果项目下面有pom.xml就选Maven如果没有就选Web目录直接导入。导入之后IDEA可能会提示“Unlinked Maven Project”有pom就点右上角的刷新按钮让它下载依赖没有pom就忽略。第二步配置项目结构。File → Project Structure → Modules选中你的模块点号添加Web把WebContent目录有的叫webapp或webroot指定为Web资源目录。然后切到Artifacts选项卡点击号选择Web Application: Exploded从Modules里把刚才配置的Web模块加进去。这一步不做或者做错后面运行Tomcat时十有八九报“404”或者“Error: Artifact not deployed”。第三步配置Tomcat。Run → Edit Configurations → 点号 → Tomcat Server → Local。在Server选项卡里选择Tomcat安装目录在Deployment选项卡里点号选择ArtifactApplication context填/或者/course注意这个路径会拼在URL前面填错了访问路径就不对。第四步改数据库连接配置。找到源码里的DBUtil工具类确认jdbcUrl、用户名、密码这三点和你本机MySQL一致。用Navicat或命令行把数据库建好并导入脚本后再来核对这行代码别弄反顺序。连接串里建议加上useSSLfalsecharacterEncodingutf8serverTimezoneAsia/Shanghai三个参数少一个都可能踩坑。Class.forName(com.mysql.jdbc.Driver); Connection conn DriverManager.getConnection( jdbc:mysql://localhost:3306/course_selection?useSSLfalsecharacterEncodingutf8serverTimezoneAsia/Shanghai, root, 你的密码);这里特别说明一下URL参数“我的MySQL连接为什么报错”九成是这三种情况——MySQL 8.0的驱动类名要写成com.mysql.cj.jdbc.Driver时区不设置会报Server returns invalid timezone异常不加上useSSLfalse会看到一堆SSL告警日志刷屏虽然不影响运行但很碍眼。驱动JAR包如果导入不进来检查一下lib目录是否被IDEA标记为库右键lib目录选Add as Library。第五步启动Tomcat。点右侧的绿色三角号启动控制台输出Server startup in xxx ms就说明启动成功了。访问http://localhost:8080/就能看到登录页面。5.2 数据库连接失败、中文乱码、端口占用三个必查项数据库连接失败是最常见的启动异常现象有两种第一种是IDEA控制台直接报java.lang.ClassNotFoundException: com.mysql.jdbc.Driver说明驱动JAR包没放进来第二种是Communications link failure说明端口或者账号密码有问题。先ping一下数据库端口通不通再确认MySQL服务有没有在运行最后看账号密码。很多人密码里有特殊字符比如符号在JDBC URL里要用URL编码格式写否则解析会出错。中文乱码是另一个高频问题要分清是页面乱码还是数据库乱码。页面中文乱码检查JSP文件头部有没有% page contentTypetext/html;charsetUTF-8 %以及Tomcat的server.xml里Connector有没有配URIEncodingUTF-8。数据库乱码检查建库时字符集是不是utf8mb4导入的sql文件本身编码是不是UTF-8。这两条排查路径可以并行修改任意一处之后都要重启Tomcat加重新导入数据只改一处往往不够。端口占用问题现象是启动时报Port 8080 was already in use。处理方式不必杀进程直接改端口更简单——在Tomcat配置里把HTTP port改成8081然后访问http://localhost:8081/即可。5.3 六条血泪踩坑记录现象、原因与一次性解决下面这几条是我经手这类项目时遇到概率最高的坑每一条都按“现象→原因→解决”写清楚建议你对照排查。第一个Tomcat启动成功但访问404。现象是控制台没有报错日志里也没有Exception但浏览器访问首页就是404。原因是Artifact没有正确Deploy或者Web资源目录没有关联到Module。解决方式检查Deployment选项卡里有没有添加Artifact检查Facets里关联的Web目录路径是否正确修改后重启Tomcat。第二个登录成功后跳转到manage页面仍然跳到login。现象是登录显示成功URL却永远停在login.jsp。原因是Filter拦截了所有JSP请求但登录Servlet只放行了login.jsp跳转到admin页面时Filter发现session里没有user就重定向了。解决方式确认LoginServlet里登录成功执行了session.setAttribute(user, user)并且Filter的放行逻辑里对login.jsp和静态资源都做了排除。第三个MySQL 8.0版本导入旧版sql脚本报Unknown collation: utf8mb4_0900_ai_ci。原因是MySQL 5.7及更低版本不认识8.0的默认排序规则。解决方式用文本编辑器打开sql文件全局替换utf8mb4_0900_ai_ci为utf8mb4_general_ci再重新导入。第四个选课页面点击选课没反应控制台报Duplicate entry 1-2 for key student_course。现象是第一次选课成功后刷新页面再点一次同一个按钮就报这个SQL异常。原因是数据库层面加了联合唯一索引而代码没捕获SQLIntegrityConstraintViolationException导致错误直接抛给了浏览器。解决方式在DAO层捕获该异常返回“你已选过该课程”的业务提示。不要为了省事把唯一索引删掉那是防超卖的最后防线。第五个JSP页面里写Java代码Scriptlet报错说变量找不到。现象是页面里% for (Course c : courseList) { %报编译错误。原因是Servlet转发时没有把courseList放到request或session里JSP拿不到。解决方式回到对应的Servlet查看该列表是否在转发前通过request.setAttribute(courseList, list)写入。第六个数据库连接正常但更新数据后查不出来或者查出来是旧数据。现象是选课成功后课程列表页面的已选人数没变。原因可能性很多但优先级最高的检查项是事务是否提交。在DAO层用了conn.setAutoCommit(false)却没调conn.commit()事务会一直挂在那里连接池里的连接都被这些未提交事务占满后续请求全部卡住。解决方式检查代码路径上是否有异常导致跳过commit以及finally里是否正确关闭了连接。6. 让选课系统扛住并发从防重复选课到连接池替换把上面这套系统跑通其实只达到了“能用”的水平。如果这个课程设计要在答辩现场演示、或者老师要求加一个“选课高峰期不卡顿”的亮点接下来这个优化就是你的加分项。第一步用JMeter或写一个简单的Java并发测试模拟30个线程同时为同一个学生选同一门课。你会发现即使代码里有事务和selected capacity判断依然可能产生脏数据。原因在于两个并发的SELECT都读到了selected49然后都通过了业务层的“未满”校验但事务提交时只有一条UPDATE生效另一条UPDATE因为selected capacity条件不成立而回滚这时候业务层的返回结果和数据库最终记录是一致的不会超卖。但如果你的源码里UPDATE语句漏了条件那并发场景下必超卖无疑。这就是为什么我反复强调数据库条件是最底层兜底。第二步把JDBC的DriverManager连接方式换成连接池。纯JDBC每次请求都新建连接并发上来之后性能很差。在src/lib下加入Druid或C3P0的JAR包在util包里新增一个连接池初始化类然后把DAO层的DBUtil.getConnection()替换为从连接池取连接。核心改动不到十行但效果立竿见影。这一步做完你可以在课程设计的说明文档里写“使用连接池管理数据库连接避免了频繁创建销毁连接带来的性能开销”答辩时这就是一个实打实的亮点。另外把登录密码从明文改成MD5加盐存储也是一个成本很低但很加分的优化。在sql脚本的初始数据里事先存好加密后的密码在LoginServlet里对用户输入的密码做同样加密再比对就能避免“数据库被人看到直接泄露所有账号”的尴尬。当然MD5在今天已经不算安全了答辩时可以提一句“生产环境会替换成BCrypt”说明你了解密码存储的演进。实践建议是只加盐到固定长度不要用随机盐否则在登录时还得先查盐值再算哈希绕来绕去容易把自己绕晕。最后一章用一个我自己的习惯收尾任何系统跑通之后我都会人为地注入几个破坏性操作——重复点选课、管理员删掉正在被选的课程、学生退课后再次选课。把这些边界场景挨个试一遍凡是报500错误的都是可以在答辩前修复的隐患。这套“破坏性自测”帮我省了无数次当场翻车的尴尬希望帮到你。本文还有配套的精品资源点击获取