ARTICLE DETAIL

资讯详情

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

Java+JSP+MySQL学生信息管理系统:课程设计最稳落地方案

Java+JSP+MySQL学生信息管理系统:课程设计最稳落地方案 简介这份资源是面向高校计算机相关专业学生的Java Web课程设计参考项目采用JSPServletMySQL技术栈实现学生信息管理系统适合正在准备期末大作业、课程设计或需要练手Java Web完整开发流程的初学者与中级学习者。压缩包共241个文件约9.17MB其中109个java源文件承载后端业务逻辑56个jsp页面负责前端展示与交互另有23个js脚本、8个css样式及15个jar依赖包并附带sql建库脚本与项目说明文档结构完整、层次清晰。目前已有247人学习下载说明该方案在同类课设资源中具有一定参考价值。项目源码经过本地编译验证可正常运行评审分达到98分内容经助教老师审定难度适中。读者可据此快速理解分层架构、数据库表设计与前后端数据交互方式并在此基础上完成功能扩展与二次开发满足学习、答辩与提交需求。1. JavaJSPMySQL 学生信息管理系统为什么这套“老技术栈”仍是课程设计最稳的落地方案如果你正在搜“JavaJSPMysql实现Web学生信息管理系统源码数据库文档说明”大概率不是想研究什么前沿架构而是要在有限时间里交出一个能跑、能演示、能讲清楚、还能应付答辩追问的完整 Web 项目。这套组合看着“老”但它恰好卡在课程设计最舒服的位置Java 负责业务逻辑JSP 负责页面渲染MySQL 负责数据持久化三层职责清晰环境依赖少一台普通笔记本就能跑通。更关键的是它不像 Spring Boot 那样把大量细节藏在自动配置里你写的每一行代码都能在答辩时讲出“为什么”。所以这篇文章不聊虚的直接按“环境怎么搭、库表怎么建、JSP 页面怎么写、增删改查怎么串、坑在哪”这条线把一套可复现的学生信息管理系统讲透。适合正在做课程设计、毕设选题或者想用 JSP Model2 思想练手 JavaWeb 的从业者和学生。2. 环境与工程骨架把 JDK、Tomcat、MySQL 和目录结构一次定死2.1 版本选型为什么我建议 JDK 8 Tomcat 8.5 MySQL 5.7/8.0这套技术栈的版本选择直接决定你后面会不会被“玄学报错”折磨。血泪经验是不要追新。JDK 8 对 JSP/Servlet 的兼容性最稳Tomcat 8.5 对 Servlet 3.1 支持完整MySQL 5.7 和 8.0 在语法上差异不大但 8.0 的驱动类名和时区配置变了很多人第一次连库就翻车在这里。组件推荐版本关键原因JDK1.8与 Tomcat 8.5、旧版 JSTL 兼容性最好Tomcat8.5.xServlet 3.1支持注解配置启动快MySQL5.7 或 8.05.7 省心8.0 需注意驱动和时区JDBC 驱动mysql-connector-java 5.1.x配 5.7/ 8.0.x配 8.0版本必须和数据库对应IDEEclipse / IntelliJ IDEA能配 Tomcat 即可不挑安装 MySQL 时8.0 版本一定要记住 root 密码并在安装向导里勾选“Use Legacy Authentication Method”否则后面 JDBC 连接会报Unable to load authentication plugin caching_sha2_password。这是新手最容易卡住的一步。2.2 工程目录结构Model2 思想下的标准分层按 JSP Model2 思想Servlet 当控制器JSP 只做视图JavaBean 做模型。目录结构建议这样定StudentMS/ ├── src/ │ └── com/student/ │ ├── dao/ # 数据库访问层 │ ├── entity/ # 实体类 │ ├── servlet/ # 控制器 │ └── util/ # DBUtil 工具类 ├── WebContent/ │ ├── WEB-INF/ │ │ ├── web.xml # 部署描述符 │ │ └── lib/ # 放 mysql-connector-java.jar │ ├── css/ │ ├── js/ │ ├── index.jsp # 登录页 │ ├── studentList.jsp # 学生列表 │ └── studentForm.jsp # 新增/编辑表单这个结构的好处是WEB-INF下的 JSP 不能被浏览器直接访问必须经过 Servlet 转发天然实现了“控制器先行”的 Model2 约束。很多人把 JSP 全放在根目录结果页面能被直接打开业务逻辑绕过了 Servlet答辩时被问“你的控制器在哪”就答不上来。2.3 数据库连接工具类一次写对后面不再改package com.student.util; import java.sql.*; public class DBUtil { // MySQL 8.0 用 com.mysql.cj.jdbc.Driver // MySQL 5.7 用 com.mysql.jdbc.Driver private static final String DRIVER com.mysql.cj.jdbc.Driver; private static final String URL jdbc:mysql://localhost:3306/student_db ?useUnicodetruecharacterEncodingutf8 serverTimezoneAsia/ShanghaiuseSSLfalse; private static final String USER root; private static final String PASSWORD 你的密码; static { try { Class.forName(DRIVER); } catch (ClassNotFoundException e) { throw new RuntimeException(JDBC驱动加载失败, e); } } public static Connection getConnection() throws SQLException { return DriverManager.getConnection(URL, USER, PASSWORD); } public static void close(Connection conn, Statement stmt, ResultSet rs) { try { if (rs ! null) rs.close(); } catch (SQLException ignored) {} try { if (stmt ! null) stmt.close(); } catch (SQLException ignored) {} try { if (conn ! null) conn.close(); } catch (SQLException ignored) {} } }逻辑说明静态代码块保证驱动只加载一次getConnection()每次返回新连接适合课程设计规模close()按 ResultSet → Statement → Connection 顺序关闭避免资源泄漏。参数说明serverTimezone在 MySQL 8.0 下必须加否则报时区错误useSSLfalse避免本地连接时的 SSL 警告characterEncodingutf8保证中文不乱码。如果你的 MySQL 是 5.7把驱动类改成com.mysql.jdbc.DriverURL 里的serverTimezone可以去掉。3. 库表设计与实体映射学生、班级、用户三张表怎么建才不返工3.1 建库建表 SQL字段类型和约束一次到位CREATE DATABASE student_db DEFAULT CHARACTER SET utf8mb4; USE student_db; CREATE TABLE student ( id INT PRIMARY KEY AUTO_INCREMENT, sno VARCHAR(20) NOT NULL UNIQUE COMMENT 学号, name VARCHAR(50) NOT NULL COMMENT 姓名, gender CHAR(2) DEFAULT 男 COMMENT 性别, age INT DEFAULT 0 COMMENT 年龄, class_id INT COMMENT 班级ID, phone VARCHAR(20) COMMENT 联系电话, create_time DATETIME DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE class_info ( id INT PRIMARY KEY AUTO_INCREMENT, class_name VARCHAR(50) NOT NULL COMMENT 班级名称, major VARCHAR(50) COMMENT 专业 ); CREATE TABLE sys_user ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(30) NOT NULL UNIQUE, password VARCHAR(64) NOT NULL, role VARCHAR(10) DEFAULT admin ); INSERT INTO sys_user (username, password) VALUES (admin, 123456); INSERT INTO class_info (class_name, major) VALUES (计科2101, 计算机科学与技术);逻辑说明sno加 UNIQUE 约束防止学号重复录入class_id作为外键关联班级表但课程设计里不强制加 FOREIGN KEY方便删改create_time默认当前时间省去手动赋值。参数说明utf8mb4比utf8更完整能存 emojiVARCHAR(64)存密码如果做 MD5 加密也够用。注意MySQL 8.0 默认字符集已经是 utf8mb4但显式写出来更保险。3.2 实体类与 DAO把 ResultSet 映射成对象的标准写法package com.student.entity; public class Student { private int id; private String sno; private String name; private String gender; private int age; private int classId; private String phone; // 省略 getter/setterIDE 一键生成 }package com.student.dao; import com.student.entity.Student; import com.student.util.DBUtil; import java.sql.*; import java.util.*; public class StudentDao { public ListStudent findAll() { ListStudent list new ArrayList(); String sql SELECT * FROM student ORDER BY id DESC; try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql); ResultSet rs ps.executeQuery()) { while (rs.next()) { Student s new Student(); s.setId(rs.getInt(id)); s.setSno(rs.getString(sno)); s.setName(rs.getString(name)); s.setGender(rs.getString(gender)); s.setAge(rs.getInt(age)); s.setClassId(rs.getInt(class_id)); s.setPhone(rs.getString(phone)); list.add(s); } } catch (SQLException e) { e.printStackTrace(); } return list; } public int insert(Student s) { String sql INSERT INTO student(sno,name,gender,age,class_id,phone) VALUES(?,?,?,?,?,?); try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setString(1, s.getSno()); ps.setString(2, s.getName()); ps.setString(3, s.getGender()); ps.setInt(4, s.getAge()); ps.setInt(5, s.getClassId()); ps.setString(6, s.getPhone()); return ps.executeUpdate(); } catch (SQLException e) { e.printStackTrace(); return 0; } } }逻辑说明用 try-with-resources 自动关闭连接比手动 close 更安全PreparedStatement用占位符天然防 SQL 注入findAll()返回 List方便 JSP 用 JSTL 遍历。参数说明ORDER BY id DESC让最新录入的学生排前面executeUpdate()返回受影响行数大于 0 表示成功。注意如果字段名和实体属性名不一致如class_idvsclassId映射时要手动对应别指望自动映射。3.3 分页查询课程设计里最容易被追问的点学生列表数据一多不分页就显得很业余。最简单的分页 SQLSELECT * FROM student ORDER BY id DESC LIMIT ?, ?;第一个?是偏移量(pageNo - 1) * pageSize第二个?是每页条数。在 DAO 里加一个findByPage(int pageNo, int pageSize)方法即可。答辩时如果被问“总页数怎么算”回答先SELECT COUNT(*) FROM student拿到总数再Math.ceil(total * 1.0 / pageSize)。这是最朴素也最稳的做法。4. JSP 页面与 Servlet 控制器登录、列表、增删改查怎么串起来4.1 登录功能Session 存用户过滤器拦未登录请求登录 Servlet 核心逻辑WebServlet(/login) public class LoginServlet extends HttpServlet { 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(currentUser, user); resp.sendRedirect(student?actionlist); } else { req.setAttribute(msg, 用户名或密码错误); req.getRequestDispatcher(index.jsp).forward(req, resp); } } }逻辑说明登录成功后把 User 对象放进 Session后续页面通过session.getAttribute(currentUser)判断是否登录失败则转发回登录页并带错误提示。参数说明sendRedirect是重定向地址栏会变避免刷新重复提交forward是转发地址栏不变适合带错误信息回显。再配一个登录过滤器WebFilter(/*) public class LoginFilter implements Filter { 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(index.jsp) || uri.endsWith(/login) || uri.contains(/css/) || uri.contains(/js/)) { chain.doFilter(req, resp); return; } if (request.getSession().getAttribute(currentUser) null) { response.sendRedirect(index.jsp); return; } chain.doFilter(req, resp); } }逻辑说明过滤器拦截所有请求白名单放行登录相关和静态资源其余检查 Session。参数说明WebFilter(/*)表示拦截全部uri.endsWith判断比contains更精确避免误放行。注意如果用了 JSTL 标签/WEB-INF/lib下的 jar 不需要放行因为不是浏览器直接请求。4.2 学生列表页JSTL 遍历 删除确认% page contentTypetext/html;charsetUTF-8 % % taglib prefixc urihttp://java.sun.com/jsp/jstl/core % html headtitle学生列表/title/head body h2学生信息列表/h2 a hrefstudentForm.jsp新增学生/a table border1 width80% tr th学号/thth姓名/thth性别/th th年龄/thth电话/thth操作/th /tr c:forEach items${studentList} vars tr td${s.sno}/td td${s.name}/td td${s.gender}/td td${s.age}/td td${s.phone}/td td a hrefstudent?actioneditid${s.id}编辑/a a hrefstudent?actiondeleteid${s.id} onclickreturn confirm(确定删除)删除/a /td /tr /c:forEach /table /body /html逻辑说明c:forEach遍历 Servlet 放进 request 的studentList${s.sno}是 EL 表达式等价于s.getSno()删除前用confirm弹窗二次确认防止误操作。参数说明taglib引入 JSTL 核心库需要jstl.jar和standard.jar放进WEB-INF/lib如果页面报The absolute uri cannot be resolved就是 jar 没放对。4.3 统一控制器一个 Servlet 用 action 参数分发WebServlet(/student) public class StudentServlet extends HttpServlet { private StudentDao dao new StudentDao(); protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String action req.getParameter(action); if (action null) action list; switch (action) { case list: req.setAttribute(studentList, dao.findAll()); req.getRequestDispatcher(studentList.jsp).forward(req, resp); break; case delete: dao.deleteById(Integer.parseInt(req.getParameter(id))); resp.sendRedirect(student?actionlist); break; case edit: req.setAttribute(student, dao.findById( Integer.parseInt(req.getParameter(id)))); req.getRequestDispatcher(studentForm.jsp).forward(req, resp); break; default: resp.sendRedirect(student?actionlist); } } protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setCharacterEncoding(UTF-8); Student s new Student(); s.setSno(req.getParameter(sno)); s.setName(req.getParameter(name)); s.setGender(req.getParameter(gender)); s.setAge(Integer.parseInt(req.getParameter(age))); s.setClassId(Integer.parseInt(req.getParameter(classId))); s.setPhone(req.getParameter(phone)); String id req.getParameter(id); if (id null || id.isEmpty()) { dao.insert(s); } else { s.setId(Integer.parseInt(id)); dao.update(s); } resp.sendRedirect(student?actionlist); } }逻辑说明GET 处理查询和删除POST 处理新增和修改用action参数分发避免写多个 Servlet新增和修改共用一个表单靠隐藏域id是否为空区分。参数说明req.setCharacterEncoding(UTF-8)必须在取参数之前调用否则中文乱码sendRedirect后跟return或直接结束方法避免继续执行。5. 避坑与排查这套系统最容易翻车的 5 个地方5.1 中文乱码POST 和 GET 要分开治现象表单提交后数据库里存的是???或者页面显示乱码。原因POST 请求默认 ISO-8859-1 解码GET 请求在 Tomcat 8.5 里由server.xml的URIEncoding决定。解决POST 在 Servlet 第一行加req.setCharacterEncoding(UTF-8)GET 要么改 Tomcat 的server.xml加URIEncodingUTF-8要么在代码里手动new String(str.getBytes(ISO-8859-1), UTF-8)。JSP 页面头部必须写% page contentTypetext/html;charsetUTF-8 %数据库连接 URL 加characterEncodingutf8。三处都对齐乱码基本绝迹。5.2 驱动加载失败ClassNotFoundException现象启动时报java.lang.ClassNotFoundException: com.mysql.cj.jdbc.Driver。原因mysql-connector-java.jar没放进WEB-INF/lib或者只加到了 IDE 的 build path 但没随项目部署。解决确认 jar 在WEB-INF/lib下Eclipse 里右键项目 → Properties → Deployment Assembly 确认 jar 被部署到/WEB-INF/lib。MySQL 8.0 用com.mysql.cj.jdbc.Driver5.7 用com.mysql.jdbc.Driver别搞混。5.3 时区报错The server time zone value is unrecognized现象连接 MySQL 8.0 时报The server time zone value ?D1ú±ê×?ê±?? is unrecognized。原因MySQL 8.0 默认时区是系统时区JDBC 驱动无法识别中文时区名。解决URL 里加serverTimezoneAsia/Shanghai或者登录 MySQL 执行SET GLOBAL time_zone 8:00。前者更省事推荐直接写在连接串里。5.4 JSTL 标签不生效页面直接输出 ${s.name}现象JSP 页面把${s.name}原样显示出来没有被解析。原因缺少 JSTL jar或者web.xml版本太低导致 EL 被禁用。解决把jstl.jar和standard.jar放进WEB-INF/lib确认web.xml的web-app版本是 3.0 以上并且没有% page isELIgnoredtrue %。如果用的是 Tomcat 8.5Servlet 3.1 默认开启 EL不需要额外配置。5.5 删除后列表不刷新浏览器缓存了旧页面现象删除一条记录后列表页还是显示旧数据。原因sendRedirect后浏览器可能从缓存加载列表页。解决在列表 Servlet 里设置响应头resp.setHeader(Cache-Control, no-cache, no-store, must-revalidate)或者删除后重定向时加一个时间戳参数student?actionlisttSystem.currentTimeMillis()。最稳的是前者一行代码解决。6. 进阶技巧把课程设计做出“可讲性”的三个加分项6.1 用 MD5 加密密码答辩时能多讲两分钟课程设计里密码明文存库是常态但如果你加一层 MD5答辩老师问“安全性怎么考虑”时就有话可说。写一个工具类public class MD5Util { public static String md5(String input) { try { MessageDigest md MessageDigest.getInstance(MD5); byte[] digest md.digest(input.getBytes(UTF-8)); StringBuilder sb new StringBuilder(); for (byte b : digest) { sb.append(String.format(%02x, b)); } return sb.toString(); } catch (Exception e) { throw new RuntimeException(e); } } }登录时把用户输入的密码md5()后再和数据库比对注册时也存 MD5 值。注意MD5 现在不算安全但课程设计里足够体现“有安全意识”。如果想再进一步可以讲“加盐”的思路不用真实现。6.2 用过滤器统一编码少写重复代码前面每个 Servlet 都写setCharacterEncoding很烦可以抽一个 EncodingFilterWebFilter(/*) public class EncodingFilter implements Filter { public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { req.setCharacterEncoding(UTF-8); resp.setContentType(text/html;charsetUTF-8); chain.doFilter(req, resp); } }这样所有请求的编码一次性解决Servlet 里不用再写。注意多个 Filter 的执行顺序由web.xml里的filter-mapping顺序决定如果用注解顺序不确定所以编码过滤器最好用web.xml配置并放在最前面。6.3 导出 Excel一个容易被忽略但很加分的功能学生列表页加一个“导出”按钮用 POI 把数据写成 Excel。核心代码// 在 Servlet 里 resp.setContentType(application/vnd.ms-excel); resp.setHeader(Content-Disposition, attachment;filenamestudents.xls); HSSFWorkbook workbook new HSSFWorkbook(); HSSFSheet sheet workbook.createSheet(学生信息); HSSFRow header sheet.createRow(0); header.createCell(0).setCellValue(学号); header.createCell(1).setCellValue(姓名); // ... 遍历 studentList 填充数据 workbook.write(resp.getOutputStream()); workbook.close();需要引入poi.jar。这个功能代码量不大但演示时很直观答辩老师看到“还能导出”通常会点头。注意Content-Disposition的文件名如果含中文要做 URL 编码否则下载下来文件名乱码。6.4 我自己的习惯先跑通一条线再复制粘贴做这类系统我一般不会一上来就把所有功能写完。先跑通“登录 → 列表 → 新增”这一条线确认数据库连通、编码正确、页面能跳转然后再复制这套模式去写删除、修改、分页。这样一旦出问题排查范围小不会在一堆代码里找 bug。另外每改完一个功能就重启 Tomcat 测一次别攒着一起测否则报错都不知道是哪一步引入的。希望帮到你。本文还有配套的精品资源点击获取
返回列表