简介:本资源是一套高分通过的JavaWeb期末大作业项目——学生信息管理系统,面向计算机及相关专业本科生,专为课程设计、期末综合实践及Web开发入门实战打造。项目包含完整可运行源码、配套数据库脚本与详细课程设计报告,已获98分实绩验证,覆盖用户登录、学生增删改查、数据导出等核心功能模块,适合夯实Servlet、JSP、MySQL及MVC分层开发能力。压缩包共77个文件,含20个Java源文件、20个编译后Class文件、3个JSP页面、3个CSS与3个JS前端资源、9张系统界面截图(JPG)、1份SQL建表脚本及1份Word格式设计报告,整体4.03MB,结构清晰,便于快速部署与代码研读。目前已有727人学习下载,提供即开即用的完整工程环境、典型业务流程图与系统处理逻辑说明,助力初学者理解JavaWeb项目全链路开发规范与常见问题解决路径。
1. 这不是又一个“增删改查”Demo:用 JavaWeb 做出能过答辩、能跑通、能讲清楚的学生信息管理系统
你手头这份“javaweb期末作业-学生信息管理系统源码+报告”,大概率正躺在压缩包里,解压后是src/、WebContent/、lib/和一份 Word 报告。但别急着交——很多同学照着网上抄的代码一运行就报404,连登录页都打不开;改了数据库配置,Tomcat 启动成功却提示ClassNotFoundException: com.mysql.cj.jdbc.Driver;写完 Servlet 编译没问题,但表单提交后数据死活不进 MySQL;报告里写着“采用 MVC 模式”,可翻遍代码根本找不到 Controller 层在哪……这不是代码不行,是整个工程结构、依赖链、部署路径和调试逻辑没对齐。本篇不讲抽象概念,只拆解真实课堂场景下能跑通、能截图、能答辩、能讲清技术选型理由的最小可行方案:用原生 Servlet + JSP + JDBC(不引入 Spring Boot 或 MyBatis)搭建一个带登录验证、学生 CRUD、分页查询、密码加密(BCrypt)、错误统一跳转的真实 JavaWeb 系统。适合大二大三正在赶课设、对 Tomcat 部署流程模糊、被web.xml和context.xml绕晕、想靠一份干净可复现的源码稳过期末的同学。
2. 从零搭起骨架:用 IDEA 创建标准 JavaWeb 工程并配通 Tomcat
2.1 创建符合 Servlet 4.0 规范的动态 Web 项目(非 Maven)
很多同学直接新建 “Maven Project”,选maven-archetype-webapp,结果发现pom.xml里一堆插件冲突,web.xml版本错乱,IDEA 提示 “Servlet version mismatch”。这不是你的错,是模板太老。真实课堂环境最稳妥的做法,是手动创建标准 Dynamic Web Project:
# 在 IDEA 中操作路径: File → New → Project → 选择 "Java Enterprise" → 勾选 "Web Application" → 取消勾选 "Create project from archetype" → Next → 输入项目名(如 student-mgr)→ Next → 设置 "Target runtime" 为已配置好的 Tomcat 9.x(必须 9.0+,因需支持 Servlet 4.0)→ Next → 勾选 "Generate web.xml descriptor" → Finish提示:
web.xml是 JavaWeb 的“宪法”,它定义了 Servlet 映射、过滤器顺序、上下文参数。课堂作业中不写web.xml= 主动放弃答辩时解释“为什么用 Filter 做登录拦截”的机会。务必保留并手动编辑。
创建后,项目结构应为:
student-mgr/ ├── src/ # Java 源码(Servlet、DAO、Bean) ├── WebContent/ # Web 资源根目录(等价于传统 WAR 的 webapp/) │ ├── WEB-INF/ │ │ ├── web.xml # 核心配置文件(必须存在) │ │ └── lib/ # 手动放入 jar 包(mysql-connector-java-8.0.33.jar、bcrypt-3.1.0.jar) │ ├── index.jsp │ ├── login.jsp │ └── list.jsp └── out/ # IDEA 编译输出目录(对应 Tomcat 的 work/Catalina/...)2.2 Tomcat 9 配置三要素:JDK 版本、编码、Context Path
IDEA 默认用 JDK 17 启动 Tomcat,但 Tomcat 9 官方仅支持到 JDK 11( Apache Tomcat 9 Requirements )。若强行用 JDK 17,启动时会报java.lang.UnsupportedClassVersionError。血泪经验:统一用 JDK 11。
在 IDEA 中配置:
- File → Project Structure → Project → SDK 选
11 (11.0.x) - File → Project Structure → Modules → Sources → Language level 选
11 - Run → Edit Configurations → Tomcat Server → Configuration → JRE 选
11
接着解决两个高频翻车点:
① 中文乱码问题(GET/POST 全覆盖)
在web.xml最顶部<web-app>标签下插入字符编码过滤器:
<!-- web.xml --> <filter> <filter-name>CharacterEncodingFilter</filter-name> <filter-class>org.apache.catalina.filters.SetCharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> <init-param> <param-name>forceEncoding</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>CharacterEncodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>② Context Path 必须为/或/student,不能留空
在 Run → Edit Configurations → Deployment → Application context 中填/student(不要/student/,末尾斜杠会导致重定向 302 失败)。这样访问地址才是http://localhost:8080/student/login.jsp,而非http://localhost:8080/student//login.jsp。
2.3 数据库连接池:用 DBCP2 替代手写 DriverManager(避免连接泄漏)
课堂作业常犯的错误:每次 Servlet 请求都new Connection(),用完不close(),导致 MySQL 连接数爆满,第二个人登录就卡死。必须用连接池。DBCP2 是 Apache 官方维护、轻量、无依赖、兼容 Java 11 的首选。
下载commons-dbcp2-2.9.0.jar和commons-pool2-2.11.1.jar,放入WebContent/WEB-INF/lib/。
在WEB-INF/web.xml中配置初始化监听器:
<listener> <listener-class>com.student.util.DBInitListener</listener-class> </listener>新建src/com/student/util/DBInitListener.java:
package com.student.util; import org.apache.commons.dbcp2.BasicDataSource; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; import javax.servlet.annotation.WebListener; @WebListener public class DBInitListener implements ServletContextListener { private static BasicDataSource dataSource; @Override public void contextInitialized(ServletContextEvent sce) { dataSource = new BasicDataSource(); dataSource.setUrl("jdbc:mysql://localhost:3306/student_db?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true"); dataSource.setUsername("root"); dataSource.setPassword("123456"); // 实际请改为你本地 MySQL 密码 dataSource.setInitialSize(5); dataSource.setMaxTotal(20); dataSource.setMaxWaitMillis(3000); dataSource.setTestOnBorrow(true); dataSource.setValidationQuery("SELECT 1"); // 将数据源存入 ServletContext,全局可取 sce.getServletContext().setAttribute("dataSource", dataSource); } @Override public void contextDestroyed(ServletContextEvent sce) { if (dataSource != null) { try { dataSource.close(); } catch (Exception e) { e.printStackTrace(); } } } public static BasicDataSource getDataSource() { return dataSource; } }参数说明:
useSSL=false:MySQL 8.0+ 默认强制 SSL,本地开发关掉省事(生产环境必须开)serverTimezone=Asia/Shanghai:避免java.sql.SQLException: The server time zone value 'XXX' is unrecognizedallowPublicKeyRetrieval=true:解决Public Key Retrieval is not allowed错误(MySQL 8.0.21+ 新增限制)setTestOnBorrow=true+validationQuery="SELECT 1":每次从池取连接前执行校验,防失效连接
3. 核心业务落地:学生登录与 CRUD 的 Servlet-JSP 分层实现
3.1 登录模块:用 BCrypt 加密密码 + Filter 拦截未登录请求
明文存密码是答辩时老师第一句就会问的致命漏洞。必须用bcrypt(非 MD5/SHA-1,不可逆且带盐)。下载bcrypt-3.1.0.jar放入lib/。
登录 Servlet (LoginServlet.java):
package com.student.servlet; import com.student.dao.UserDao; import com.student.entity.User; import com.student.util.DBInitListener; import org.mindrot.jbcrypt.BCrypt; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import java.io.IOException; @WebServlet("/login") public class LoginServlet extends HttpServlet { private UserDao userDao = new UserDao(); @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String username = req.getParameter("username"); String password = req.getParameter("password"); User user = userDao.findByUsername(username); if (user != null && BCrypt.checkpw(password, user.getPassword())) { // 密码正确,存入 session HttpSession session = req.getSession(); session.setAttribute("user", user); resp.sendRedirect(req.getContextPath() + "/list"); } else { req.setAttribute("error", "用户名或密码错误"); req.getRequestDispatcher("/login.jsp").forward(req, resp); } } }关键点说明:
BCrypt.checkpw()是验证方法,BCrypt.hashpw()才是加密(用于注册时)req.getSession()不加参数默认true,即自动创建新 session;若只想获取已有 session,用req.getSession(false)resp.sendRedirect()是客户端重定向,URL 变;req.getRequestDispatcher().forward()是服务器端转发,URL 不变(登录失败走后者,保持在 login.jsp)
登录拦截 Filter (LoginFilter.java):
package com.student.filter; import javax.servlet.*; import javax.servlet.annotation.WebFilter; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import java.io.IOException; @WebFilter(filterName = "LoginFilter", urlPatterns = {"/list", "/add", "/edit", "/delete"}) public class LoginFilter implements Filter { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest req = (HttpServletRequest) request; HttpServletResponse resp = (HttpServletResponse) response; HttpSession session = req.getSession(false); if (session == null || session.getAttribute("user") == null) { // 未登录,重定向到登录页,并携带原始请求路径(便于登录后跳回) String originalUrl = req.getRequestURI(); if (req.getQueryString() != null) { originalUrl += "?" + req.getQueryString(); } resp.sendRedirect(req.getContextPath() + "/login.jsp?redirect=" + originalUrl); return; } chain.doFilter(request, response); } }为什么用 Filter 而不用在每个 Servlet 里判断?
因为这是横切关注点(Cross-Cutting Concern)——所有需要权限的 URL 都要检查,硬编码会重复 5 次以上,违反 DRY 原则。Filter 是 JavaWeb 原生解决方案,比 Spring AOP 更轻量、更易向老师解释。
3.2 学生列表页:JSP + JSTL + 分页封装(避免 scriptlet)
课堂作业常见反模式:JSP 里写<% while(rs.next()) { %>,混杂 Java 逻辑与 HTML。答辩时老师会问:“如果换数据库,这段代码要不要改?”——答“要”,就暴露了紧耦合。正确做法:用 JSTL + EL 表达式 + 自定义分页工具类。
引入 JSTL(下载jstl-1.2.jar放入lib/):
在list.jsp顶部声明:
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>StudentListServlet.java查询逻辑(含分页):
package com.student.servlet; import com.student.dao.StudentDao; import com.student.entity.PageBean; import com.student.entity.Student; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.List; @WebServlet("/list") public class StudentListServlet extends HttpServlet { private StudentDao studentDao = new StudentDao(); @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // 获取当前页码,默认第1页 String pageNumStr = req.getParameter("pageNum"); int pageNum = 1; if (pageNumStr != null && !pageNumStr.trim().isEmpty()) { pageNum = Integer.parseInt(pageNumStr); } // 每页显示条数 int pageSize = 5; PageBean<Student> pageBean = studentDao.findPage(pageNum, pageSize); req.setAttribute("pageBean", pageBean); req.getRequestDispatcher("/list.jsp").forward(req, resp); } }PageBean.java(通用分页模型):
package com.student.entity; import java.util.List; public class PageBean<T> { private List<T> list; // 当前页数据 private int pageNum; // 当前页码 private int pageSize; // 每页条数 private int total; // 总记录数 private int totalPage; // 总页数 public PageBean(List<T> list, int pageNum, int pageSize, int total) { this.list = list; this.pageNum = pageNum; this.pageSize = pageSize; this.total = total; this.totalPage = (int) Math.ceil((double) total / pageSize); } // getter/setter 省略(IDEA 自动生成) }list.jsp中使用 JSTL 渲染:
<c:forEach items="${pageBean.list}" var="stu"> <tr> <td>${stu.id}</td> <td>${stu.name}</td> <td>${stu.gender}</td> <td><fmt:formatDate value="${stu.birthday}" pattern="yyyy-MM-dd"/></td> <td>${stu.major}</td> <td> <a href="${pageContext.request.contextPath}/edit?id=${stu.id}">编辑</a> | <a href="${pageContext.request.contextPath}/delete?id=${stu.id}" onclick="return confirm('确定删除 ${stu.name}?')">删除</a> </td> </tr> </c:forEach>分页 URL 构造技巧:
<a href="?pageNum=${pageBean.pageNum - 1}">上一页</a><a href="?pageNum=${pageBean.pageNum + 1}">下一页</a>
注意:?pageNum=会覆盖原有参数,若需保留搜索条件(如按专业筛选),应拼接完整 URL,课堂作业可简化处理。
4. 避坑指南:这 4 个错误让 80% 的同学卡在部署前
4.1 现象:启动 Tomcat 后访问http://localhost:8080/student/login.jsp报 404
原因:
login.jsp不在WebContent/目录下,而在src/或其他错误路径;- IDEA 未将
WebContent/设为 Web Resource Directory(右键 → Mark Directory as → Resources Root); web.xml中<welcome-file-list>未配置login.jsp,且未通过 Servlet 映射访问。
解决:
- 确认
login.jsp物理路径为student-mgr/WebContent/login.jsp; - 在
web.xml中添加欢迎页:<welcome-file-list> <welcome-file>login.jsp</welcome-file> </welcome-file-list> - 若仍 404,打开 Tomcat 日志
logs/catalina.out,搜索deploy关键字,确认应用是否成功部署。
4.2 现象:登录时控制台报java.lang.ClassNotFoundException: com.mysql.cj.jdbc.Driver
原因:
mysql-connector-java-8.0.33.jar未放入WebContent/WEB-INF/lib/(放错位置如src/或out/无效);- Jar 包版本与 MySQL 服务端不匹配(MySQL 5.7 用
mysql-connector-java-5.1.49.jar,MySQL 8.0+ 必须用8.0.x); web.xml中未配置<resource-ref>(DBCP2 不需要,此为旧式 JNDI 配置,课堂作业禁用)。
解决:
- 删除
lib/下所有 mysql jar,只留一个mysql-connector-java-8.0.33.jar; - 在
DBInitListener.java的setUrl()中确认协议为jdbc:mysql://(非jdbc:mysql:thin:@); - 重启 Tomcat(必须重启,热部署不加载新 jar)。
4.3 现象:添加学生后页面空白,数据库无数据,无任何报错
原因:
StudentAddServlet.java中doPost()方法内未调用studentDao.add(student);add.jsp表单method="get",但 Servlet 只写了doPost();request.setCharacterEncoding("UTF-8")未设置,中文字段存入数据库为???(虽不报错,但数据损坏)。
解决:
- 在
StudentAddServlet.java的doPost()开头强制设置编码:req.setCharacterEncoding("UTF-8"); - 检查表单:
<form action="/student/add" method="post">; - 使用浏览器开发者工具(F12)→ Network → 查看 Form Data 是否有值,确认前端传参正确。
4.4 现象:登录成功后点击“学生列表”报 404,但直接输入 URLhttp://localhost:8080/student/list可访问
原因:
login.jsp中跳转链接写死为<a href="/list">,缺少 Context Path;web.xml中url-pattern写成/list/*,但 Servlet 映射为/list,导致 Filter 拦截失败。
解决:
- 所有链接必须用 EL 表达式:
<a href="${pageContext.request.contextPath}/list">学生列表</a> - Filter 的
@WebFilter(urlPatterns = {"/list", "/add", ...})必须与 Servlet 的@WebServlet("/list")完全一致,不能多/也不能少。
5. 报告撰写核心:用三张图讲清技术选型、数据流与安全设计
5.1 技术选型对比表:为什么不用 Spring Boot?为什么用 DBCP2?
课堂报告不能只写“采用了 MVC 模式”,要体现思考过程。用表格呈现决策依据:
| 对比项 | 本方案(Servlet+JSP+JDBC) | Spring Boot 方案 | 课堂适用性分析 |
|---|---|---|---|
| 学习成本 | 零额外框架,直面 HTTP 协议、Servlet 生命周期 | 需理解 IOC/AOP/自动配置,入门门槛高 | ✅ 本方案更利于理解底层机制 |
| 部署复杂度 | Tomcat 单容器,war 包一键部署 | 需嵌入 Tomcat 或打 jar 包,端口/配置易冲突 | ✅ 符合高校机房统一环境要求 |
| 数据库连接 | DBCP2 连接池,手动管理 DataSource | HikariCP + Spring JDBC Template | ✅ DBCP2 依赖少,jar 包体积 < 500KB,无反射黑盒 |
| 安全实践 | BCrypt 密码加密 + Filter 权限控制 | Spring Security 配置繁杂 | ✅ BCrypt 是 OWASP 推荐算法,Filter 是 JavaEE 标准方案 |
答辩话术:
“老师,我们选择原生 JavaWeb 是为了精准匹配课程教学目标——掌握 Servlet 生命周期、HTTP 请求响应模型、JDBC 连接管理这些核心能力。Spring Boot 虽好,但它把太多细节封装掉了,反而不利于建立知识树。”
5.2 数据流转图:从浏览器到数据库的 7 步闭环(附关键代码行号)
画一张横向流程图(可用 PPT 或 draw.io),标注每一步的触发者与关键代码:
[浏览器] ↓ GET /student/login.jsp [login.jsp] → 渲染登录表单 ↓ POST /student/login (表单提交) [LoginServlet.java:22] → req.getParameter("username") ↓ 调用 UserDao.findByUsername() [UserDao.java:35] → PreparedStatement.executeQuery("SELECT * FROM user WHERE username=?") ↓ 返回 User 对象 [LoginServlet.java:28] → BCrypt.checkpw() 验证密码 ↓ 成功则 req.getSession().setAttribute("user", user) [LoginFilter.java:25] → 检查 session 中 user 是否存在 ↓ 存在则放行至 StudentListServlet [StudentListServlet.java:32] → studentDao.findPage() 查询分页数据 ↓ setAttribute("pageBean", pageBean) [list.jsp] → JSTL <c:forEach> 渲染表格价值点:这张图让老师一眼看清你是否真正理解请求生命周期,而非只会复制粘贴。
5.3 安全加固清单:3 项实操级防护(非口号)
很多报告写“系统安全性高”,却无具体措施。以下 3 条必须写进报告,并在代码中体现:
- 密码存储安全:使用
BCrypt.hashpw(password, BCrypt.gensalt(12))生成带盐哈希,盐值随哈希一起存入数据库password字段(长度需设为VARCHAR(255))。 - SQL 注入防护:所有 DAO 方法均使用
PreparedStatement,杜绝字符串拼接 SQL(如WHERE name = '+ name + '')。 - XSS 防护:JSP 中输出用户数据时,用 JSTL
<c:out value="${stu.name}"/>替代${stu.name},自动转义<,>,&等字符。
实测效果:在姓名字段输入
<script>alert(1)</script>,页面显示纯文本,不执行 JS。
6. 答辩前最后一遍检查:5 个必做动作与我的血泪习惯
6.1 本地 Clean & Rebuild:清除所有编译残留
IDEA 的Build → Clean and Rebuild Project不等于真正干净。必须手动删除三个目录:
out/(IDEA 编译输出)target/(若误建了 Maven 项目)Tomcat/work/Catalina/localhost/student/(Tomcat 的 JSP 编译缓存,常驻内存导致修改 JSP 不生效)
我的习惯:写一个
clean.bat(Windows)或clean.sh(macOS/Linux)放在项目根目录:# clean.sh rm -rf out/ target/ rm -rf ~/apache-tomcat-9.0.83/work/Catalina/localhost/student/ echo "Clean done. Now restart Tomcat."每次答辩前双击运行,比点菜单快 10 秒,且不会漏删。
6.2 数据库初始化脚本:一行命令还原测试数据
别再手动在 Navicat 里一条条 INSERT。在项目根目录放init.sql:
CREATE DATABASE IF NOT EXISTS student_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; USE student_db; CREATE TABLE user ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) UNIQUE NOT NULL, password VARCHAR(255) NOT NULL ); INSERT INTO user VALUES (1, 'admin', '$2a$12$ZqHbQvYxWzRtUoIjKlMnOpQrStUvWxYzAbCdEfGhIjKlMnOpQrStU'); -- bcrypt('123456') CREATE TABLE student ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(100) NOT NULL, gender ENUM('男','女') DEFAULT '男', birthday DATE, major VARCHAR(100), create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); INSERT INTO student VALUES (1,'张三','男','2002-05-10','计算机科学与技术','2024-03-01'), (2,'李四','女','2001-11-22','软件工程','2024-03-02');答辩演示技巧:提前在 MySQL 中执行
DROP DATABASE student_db;,然后当着老师面运行source /path/to/init.sql,展示“从零构建”的可控性。
6.3 报告中的源码截图规范:只截关键 3 行,带行号与注释
老师不会看你 200 行的StudentDao.java。报告中只放 3 类截图:
- 架构图:用 draw.io 画的 MVC 分层图(Controller/Serlvet、View/JSP、Model/Entity+DAO)
- 安全代码:
BCrypt.hashpw()调用行 + 注释“密码加密,符合 OWASP 密码策略” - 异常处理:
try-catch(SQLException e)中e.printStackTrace()→ 改为log.error("DB error", e)(用java.util.logging即可,无需 log4j)
我的血泪教训:曾因报告里贴了整页
web.xml,被老师问“这个load-on-startup值 1 和 2 有什么区别”,当场卡壳。现在只贴load-on-startup所在行,并手写注释:“值越小优先级越高,此处设为 1 确保 DB 初始化监听器最先加载”。
6.4 答辩演示 checklist(打印出来逐项打钩)
| 项目 | 检查方式 | 是否完成 |
|---|---|---|
✅ Tomcat 启动后localhost:8080/student跳转到login.jsp | 浏览器直接访问 | ☐ |
✅ 输入 admin/123456 登录成功,URL 变为/student/list | 手动输入 | ☐ |
| ✅ 点击“添加学生”,填完提交后列表页立即出现新学生 | 新增一条数据 | ☐ |
| ✅ 修改学生姓名,保存后列表页显示新名字 | 编辑功能 | ☐ |
| ✅ 删除学生后,刷新列表页该学生消失 | 删除功能 | ☐ |
✅ 在姓名栏输入<script>alert(1)</script>,页面显示原文本不弹窗 | XSS 防护 | ☐ |
✅ 关闭浏览器再打开localhost:8080/student/list,自动跳回登录页 | Session 失效验证 | ☐ |
6.5 我的终极习惯:答辩前夜,用手机拍下所有关键界面
不是为了存档,是为了强迫自己用第三人视角审视系统。打开手机相机,对准屏幕拍下:
- 登录页(URL 栏可见
:8080/student/login.jsp) - 列表页(显示至少 2 条学生数据,分页控件可见)
- 添加页(表单完整,提交按钮可点击)
- 浏览器开发者工具 Console 标签页(确保无红色报错)
- Navicat 中
student表数据(证明数据真实落库)
看着这 5 张照片,你会突然意识到:“哦,原来我的系统真的跑起来了,不是幻觉。” 这种确定感,比背 10 遍原理都管用。希望帮到你。
本文还有配套的精品资源,点击获取