ARTICLE DETAIL

资讯详情

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

Java Web学生选课系统实战:Servlet+JSP+JDBC工程入门

Java Web学生选课系统实战:Servlet+JSP+JDBC工程入门 简介本资源是一套完整的Java Web课程设计项目——学生选课系统面向高校计算机专业本科生及Java Web初学者用于数据库原理、Web开发或软件工程类课程实践。系统严格遵循关系型数据库设计规范实现学生、课程、学习三张核心表并完整覆盖基本信息查询、信息维护、选课操作与系统管理四大子系统特别强化了参照完整性与用户自定义完整性校验逻辑。压缩包共164个文件含57个Java后端业务类、36个JSP前端页面、11个SQL建表与初始化脚本、14个JAR依赖库及7个CSS样式文件结构清晰、分层合理配套含课程设计报告Word文档与多套UI样式资源便于理解整体架构与界面实现。资源大小为5.37MB轻量易部署已有4582人学习下载适合课程实训、毕设参考或Java Web入门项目实战。1. 为什么一个“学生选课系统”压缩包能帮你把 Java Web 从课本拉进真实工程现场这不是又一个照着《JSP 与 Servlet 入门》抄写的课堂作业。当你解压课程设计基于Java web 的学生选课系统源码数据库报告.zip你拿到的是一套完整闭环的轻量级企业级 Web 工程切片它有明确角色学生、教师、管理员、有真实数据流转选课冲突检测、课表生成、成绩录入、有可运行的数据库结构MySQL 表设计含外键约束与索引、有可调试的分层代码Servlet JSP JDBC部分含 DAO 模式雏形甚至附带一份手写痕迹明显的课程设计报告——里面藏着老师打分时最在意的“需求分析是否合理”“ER 图是否规范”“测试用例是否覆盖边界”。它不追求 Spring Boot 自动装配的炫技但每行request.getParameter(stuId)都在教你 HTTP 请求如何落地每个c:forEach标签都在演示 MVC 分离的朴素实践。适合刚学完 Servlet 生命周期、正卡在“写了 HelloWorld 却不会连数据库”的大三学生也适合想快速搭个管理后台原型、验证业务逻辑的初级 Java 工程师——因为它的技术栈足够干净没有框架黑盒所有问题都能追到Class.forName(com.mysql.cj.jdbc.Driver)这一行。2. 从解压到启动三步跑通这个 Java Web 项目的真实路径提示别急着导入 IDE先确认环境底座是否稳固。本项目典型依赖是 JDK 8 Tomcat 8/9 MySQL 5.7不是 JDK 17 Spring Boot 3.x。版本错位是 80% 启动失败的根源。2.1 解压后第一眼该看什么识别项目骨架与技术栈指纹打开 ZIP 包重点盯紧这四个目录/文件src/Java 源码根目录观察是否有servlet、dao、entity子包这是传统 MVC 分层信号若看到spring或boot字样则大概率是后期魔改版需谨慎。WebContent/或webapp/Web 资源根目录检查是否存在WEB-INF/web.xmlServlet 3.0 前的部署描述符本项目几乎必有index.jsp是否在根目录下入口页面。lib/JAR 包仓库数一数mysql-connector-java-*.jar数据库驱动、jstl.jarJSP 标签库、servlet-api.jar注意Tomcat 自带此处不应出现否则会冲突。sql/或database/SQL 脚本目录找student_course.sql或类似命名的建库建表脚本——这是数据库落地的唯一依据。# 快速验证项目类型Linux/macOS 终端 unzip -l 课程设计基于Java web 的学生选课系统源码数据库报告.zip | grep -E (web.xml|WEB-INF|src/|sql/|\.sql$)逻辑说明web.xml是 Servlet 规范的“宪法”它的存在直接定义了项目为传统 Java Web非 Spring Boot 内嵌容器。src/下无org.springframework包名lib/中无spring-webmvc.jar基本可排除 Spring 框架。参数说明grep -E用正则匹配多个关键词避免漏掉sql目录被命名为db_script等变体。2.2 数据库初始化用 SQL 脚本创建表结构与基础数据项目自带的 SQL 文件通常包含三部分CREATE DATABASE、CREATE TABLE、INSERT INTO初始化数据如默认管理员账号admin/123456。切勿直接在 MySQL Workbench 里全选执行——中文注释或编码问题常导致建表失败。-- 正确做法逐段执行优先创建库与表 CREATE DATABASE IF NOT EXISTS student_course_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; USE student_course_db; -- 复制粘贴建表语句示例学生表 CREATE TABLE student ( stu_id VARCHAR(10) PRIMARY KEY, stu_name VARCHAR(20) NOT NULL, major VARCHAR(30), grade INT ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;参数说明utf8mb4是必须项支持 emoji 和生僻汉字ENGINEInnoDB保证事务与外键选课关系表必然需要外键约束DEFAULT CHARSETutf8mb4防止 JSP 页面提交中文时乱码。常见错误脚本里写CHARSETutf8MySQL 中utf8实为utf8mb3不兼容四字节 Unicode。2.3 Tomcat 部署手动配置而非 IDE 一键部署的底层逻辑将整个项目文件夹非 ZIP 包复制到 Tomcat 的webapps/目录下重命名为student-course去掉空格与中文避免 URL 访问异常。启动 Tomcat 后访问http://localhost:8080/student-course/若返回404检查webapps/student-course/WEB-INF/web.xml是否存在且welcome-file-list中指定的index.jsp是否在webapps/student-course/根目录。若返回500查看 Tomcat 日志logs/catalina.out高频报错是java.lang.ClassNotFoundException: com.mysql.cj.jdbc.Driver—— 说明mysql-connector-java-*.jar未放入webapps/student-course/WEB-INF/lib/不是 Tomcat 的lib/目录。!-- web.xml 中关键配置示例 -- welcome-file-list welcome-fileindex.jsp/welcome-file /welcome-file-list servlet servlet-nameLoginServlet/servlet-name servlet-classcom.student.servlet.LoginServlet/servlet-class /servlet servlet-mapping servlet-nameLoginServlet/servlet-name url-pattern/login/url-pattern /servlet-mapping逻辑说明servlet-mapping定义了 URL 路径/login如何映射到LoginServlet类。参数说明servlet-class的值必须与src/中实际包路径完全一致大小写敏感例如com.student.servlet.LoginServlet对应src/com/student/servlet/LoginServlet.java。IDE 导入时若包路径错位此处必报 500 错误。3. 代码层深度拆解读懂核心业务逻辑的三个关键类提示不要试图读完全部 2000 行代码。聚焦LoginServlet、CourseSelectServlet、StudentDAO这三类它们覆盖了身份认证、核心业务、数据访问三大主干。3.1 登录控制层Servlet 如何解析请求并跳转LoginServlet.java是用户进入系统的第一个关卡。它不做密码加密课程设计简化版但清晰展示了 HTTP 请求处理全流程public class LoginServlet extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 1. 获取表单参数关键request.getParameter() String username request.getParameter(username); String password request.getParameter(password); // 2. 调用 DAO 层验证解耦思想初现 StudentDAO dao new StudentDAO(); Student user dao.findByUsername(username); // 3. 业务判断与响应注意forward 与 redirect 的区别 if (user ! null user.getPassword().equals(password)) { // 登录成功将用户对象存入 session供后续页面使用 request.getSession().setAttribute(currentUser, user); request.getRequestDispatcher(main.jsp).forward(request, response); } else { // 登录失败设置错误信息回到登录页 request.setAttribute(errorMsg, 用户名或密码错误); request.getRequestDispatcher(index.jsp).forward(request, response); } } }逻辑说明request.getRequestDispatcher().forward()是服务器端跳转URL 不变且request对象中setAttribute的数据可传递而response.sendRedirect()是客户端重定向URL 变化request属性丢失。课程设计中混用二者会导致errorMsg不显示。参数说明request.getSession().setAttribute()将用户信息存入 Session是传统 Web 保持登录态的核心手段有效期由web.xml中session-configsession-timeout30/session-timeout/session-config控制单位分钟。3.2 选课业务层如何用 JDBC 实现“不能重复选同一门课”CourseSelectServlet.java是业务复杂度最高的类。它需完成查询可选课程、检查学生是否已选、插入选课记录、更新余量。关键在于事务控制——避免出现“插入成功但余量未减”的数据不一致。protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String courseId request.getParameter(courseId); String stuId (String) request.getSession().getAttribute(currentUserStuId); Connection conn null; PreparedStatement ps null; try { conn DBUtil.getConnection(); // DBUtil 是封装的数据库连接工具类 conn.setAutoCommit(false); // 关键开启事务 // 1. 检查是否已选SELECT ... FOR UPDATE 锁定行防并发重复选 String checkSql SELECT * FROM student_course WHERE stu_id ? AND course_id ?; ps conn.prepareStatement(checkSql); ps.setString(1, stuId); ps.setString(2, courseId); ResultSet rs ps.executeQuery(); if (rs.next()) { throw new RuntimeException(您已选修此课程); } // 2. 插入选课记录 String insertSql INSERT INTO student_course (stu_id, course_id) VALUES (?, ?); ps conn.prepareStatement(insertSql); ps.setString(1, stuId); ps.setString(2, courseId); ps.executeUpdate(); // 3. 更新课程余量 String updateSql UPDATE course SET remain_num remain_num - 1 WHERE course_id ?; ps conn.prepareStatement(updateSql); ps.setString(1, courseId); ps.executeUpdate(); conn.commit(); // 提交事务 request.setAttribute(msg, 选课成功); } catch (Exception e) { if (conn ! null) { try { conn.rollback(); } catch (SQLException ex) {} } request.setAttribute(errorMsg, e.getMessage()); } finally { DBUtil.close(conn, ps, null); } request.getRequestDispatcher(select_result.jsp).forward(request, response); }逻辑说明SELECT ... FOR UPDATE在查询时对数据行加写锁阻止其他事务同时修改是解决“超卖”问题的数据库级方案。参数说明conn.setAutoCommit(false)是手动事务起点conn.commit()与conn.rollback()成对出现finally块确保连接释放。新手常犯错误忘记rollback()导致事务挂起数据库连接池耗尽。3.3 数据访问层DAO 模式如何隔离数据库细节StudentDAO.java体现了“数据访问对象DAO”模式——将 JDBC 操作封装成方法上层 Servlet 只需调用findByUsername()无需关心Connection如何获取、PreparedStatement如何预编译。public class StudentDAO { public Student findByUsername(String username) { String sql SELECT * FROM student WHERE stu_id ?; Connection conn null; PreparedStatement ps null; ResultSet rs null; try { conn DBUtil.getConnection(); ps conn.prepareStatement(sql); ps.setString(1, username); rs ps.executeQuery(); if (rs.next()) { Student stu new Student(); stu.setStuId(rs.getString(stu_id)); stu.setStuName(rs.getString(stu_name)); stu.setPassword(rs.getString(password)); // 明文存储仅课程设计 return stu; } } catch (SQLException e) { e.printStackTrace(); } finally { DBUtil.close(conn, ps, rs); } return null; } }逻辑说明DAO 类不处理业务逻辑如密码校验只负责“增删改查”原子操作。DBUtil.close()是资源释放的关键防止ResultSet或Connection泄露导致 Tomcat 内存溢出。参数说明rs.getString(stu_id)使用列名而非序号rs.getString(1)增强可读性与健壮性表结构变更时不易出错。4. 避坑指南五个让开发者深夜重启 Tomcat 的真实问题4.1 现象页面中文显示为???数据库存入也是乱码原因JDBC 连接 URL 缺少字符集参数且 Tomcat 的server.xml未配置 URI 编码。解决修改DBUtil.java中的连接字符串jdbc:mysql://localhost:3306/student_course_db?useUnicodetruecharacterEncodingutf8mb4serverTimezoneAsia/Shanghai编辑conf/server.xml在Connector标签内添加URIEncodingUTF-8Connector port8080 protocolHTTP/1.1 URIEncodingUTF-8 ... /JSP 页面顶部声明% page contentTypetext/html;charsetUTF-8 %4.2 现象登录后跳转main.jsp但页面提示javax.servlet.ServletException: java.lang.NullPointerException原因request.getSession().getAttribute(currentUser)返回 null因LoginServlet中存入的是currentUser但main.jsp中取的是user或拼写错误。解决全局搜索getAttribute(核对所有setAttribute()与getAttribute()的 key 字符串是否完全一致包括大小写与下划线。4.3 现象点击“选课”按钮无反应浏览器控制台报404 Not Found原因web.xml中url-pattern配置的路径与前端表单action属性不匹配。例如web.xml写url-pattern/select/url-pattern但select.jsp中写form actionCourseSelectServlet缺少/前缀。解决统一使用绝对路径form action${pageContext.request.contextPath}/select${pageContext.request.contextPath}动态获取应用上下文路径避免硬编码。4.4 现象MySQL 报错The table student_course is full原因student_course表设置了AUTO_INCREMENT主键但未定义主键或主键类型不匹配如用VARCHAR当主键却未设PRIMARY KEY。解决检查建表语句确保关联表有显式主键CREATE TABLE student_course ( id BIGINT AUTO_INCREMENT PRIMARY KEY, -- 必须有自增主键 stu_id VARCHAR(10) NOT NULL, course_id VARCHAR(10) NOT NULL, FOREIGN KEY (stu_id) REFERENCES student(stu_id), FOREIGN KEY (course_id) REFERENCES course(course_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;4.5 现象Tomcat 启动时报SEVERE: Error listenerStart日志末尾显示java.lang.NoClassDefFoundError: javax/servlet/Filter原因lib/目录下误放了servlet-api.jarTomcat 自带重复引入导致类加载冲突。解决删除webapps/your_project/WEB-INF/lib/servlet-api.jar保留 Tomcat 自身lib/servlet-api.jar。验证ls $CATALINA_HOME/lib/ | grep servlet应只输出servlet-api.jar。5. 从课程设计到工程能力三个可立即落地的升级动作提示别满足于“跑起来”。真正的价值在于用这个项目当跳板把课堂知识焊接到工业实践上。以下动作均基于本项目源码微调无需重写架构。5.1 给登录加盐哈希用 BCrypt 替换明文密码5 分钟安全加固课程设计中密码明文存储 (INSERT INTO student VALUES (1001,张三,123456)) 是重大安全隐患。升级只需两步引入 BCrypt 依赖下载bcrypt-3.1.jar放入WEB-INF/lib/改造LoginServlet与RegisterServlet// 注册时加密RegisterServlet.java String rawPassword request.getParameter(password); String hashedPassword BCrypt.hashpw(rawPassword, BCrypt.gensalt(12)); // 12 轮哈希 // 存入数据库的不再是 123456而是 $2a$12$XkYv... // 登录时校验LoginServlet.java if (BCrypt.checkpw(inputPassword, dbHashedPassword)) { // 自动比对盐值 // 登录成功 }逻辑说明BCrypt.gensalt(12)生成随机盐值并参与哈希BCrypt.checkpw()内部自动提取盐值重算无需开发者存储盐。参数说明轮数12是安全与性能平衡点过高拖慢登录过低易被暴力破解课程设计中10~12均可。5.2 为选课加前端校验用 JavaScript 防止空提交与重复点击select.jsp中加入简易 JS提升用户体验与服务端压力script function validateForm() { const courseId document.getElementById(courseId).value; if (!courseId) { alert(请选择课程); return false; } // 禁用提交按钮防重复点击关键 document.getElementById(submitBtn).disabled true; document.getElementById(submitBtn).value 提交中...; return true; } /script form onsubmitreturn validateForm() action${pageContext.request.contextPath}/select methodpost select idcourseId namecourseId.../select input typesubmit idsubmitBtn value选课 / /form逻辑说明onsubmitreturn validateForm()中return false会阻止表单默认提交行为。参数说明disabled true是前端防重提交的最简方案配合后端Override的doPost方法中增加if (request.getSession().getAttribute(submitting) ! null)可实现双重防护。5.3 用 JSTL 替代 Scriptlet让 JSP 页面告别% %黑魔法main.jsp中常见while(rs.next()){ %trtd%rs.getString(name)%/td/tr% }这种嵌入 Java 代码的方式难以维护。替换为 JSTL% taglib prefixc urihttp://java.sun.com/jsp/jstl/core % % taglib prefixfn urihttp://java.sun.com/jsp/jstl/functions % !-- 将 ResultSet 封装为 ListStudent 传入 request -- c:forEach items${studentList} varstu tr td${stu.stuId}/td td${stu.stuName}/td td${stu.major}/td /tr /c:forEach逻辑说明c:forEach是标准标签${stu.stuId}通过 JavaBean 的 getter 方法getStuId()反射获取值。参数说明需确保Student类有符合 JavaBean 规范的 getter如public String getStuId(){return stuId;}且studentList在 Servlet 中通过request.setAttribute(studentList, list)传入。6. 我的血泪经验如何用这个 ZIP 包在简历和面试中打出差异化三年前我拿这个选课系统参加校招面试官扫了一眼就问“你说你改过它那告诉我如果现在要支持‘退课’功能数据库表结构怎么变后端接口怎么设计前端按钮放哪”——我当场卡壳。后来我才明白课程设计的价值不在‘做出来’而在‘讲清楚为什么这么设计’。现在我带新人会让他们对着这个 ZIP 包做三件事画一张真实的部署拓扑图标出index.jsp→LoginServlet→StudentDAO→MySQL的数据流向用箭头注明协议HTTP、JDBC与端口8080、3306。很多候选人说不清web.xml和server.xml的职责边界。模拟一次线上故障故意删掉WEB-INF/lib/mysql-connector-java.jar然后复现ClassNotFoundException再教他们看catalina.out日志定位——这才是运维视角的工程师思维。给CourseSelectServlet写单元测试用 JUnit H2 内存数据库MockDBUtil.getConnection()验证“选已选课程”时是否抛出预期异常。不求完美但要知道Test(expected RuntimeException.class)怎么写。这些动作不会增加代码行数但会让你在面试时脱口而出“当时我遇到 XXX 问题查了 Tomcat 官方文档第 X 章发现原因是 YYY于是改了 ZZZ 行代码……”——这比背一百道“Spring Bean 生命周期”更有说服力。最后送你一句我刻在 IDE 启动页的话“能跑通的代码只是开始能讲清每一行为什么存在的代码才是你的护城河。”希望帮到你。本文还有配套的精品资源点击获取
返回列表