尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
JSP+Servlet+JDBC学生管理系统实战:可调试、带事务、支持分页与模糊搜索
简介这是一套基于JSPServletJDBCMySQL技术栈开发的完整学生管理系统适用于高校Java Web课程设计、期末实训及毕业设计参考帮助初学者掌握MVC分层架构、数据库连接池配置、CRUD操作与前后端交互全流程。资源包共341个文件包含84个Java源码如StudentDaoImpl、TeacherDaoImpl等DAO实现类、84个编译后class文件、36个JSP页面负责展示层、61个JS脚本含表单验证与AJAX交互、28个CSS样式文件以及SQL建表语句、Jar依赖库和少量图片资源整体压缩包仅6.99MB轻量易部署。已有3323人学习下载作者已实测可稳定运行配套博文详述环境配置、功能模块划分与关键代码逻辑。读者可直接导入Eclipse/IDEA运行获取完整项目结构、规范化的JavaBean封装、Servlet请求分发机制、JDBC事务处理示例及常见异常排错提示。1. 这不是老古董是能跑在 JDK 8–17 上的轻量级 Web 入门闭环一个完整可调试、带事务控制、含分页与模糊搜索的学生管理系统实战包你可能刚在某高校实训课上被要求“用 JSP 写个学生管理”或者正被导师推着补 Java Web 基础——结果一搜全是残缺代码、缺 SQL 文件、没 web.xml 配置、连登录跳转都 404 的“教学示例”。这个基于jspservletjdbcMySQL的学生管理系统.zip不是那种半截子工程。它是一套真实可部署、结构清晰、边界明确、错误可追踪的最小可行 Web 应用闭环从 MySQL 建库建表、JDBC 连接池初始化、Servlet 请求路由、JavaBean 封装、JSP 页面渲染到登录校验、增删改查含批量删除、按姓名/学号模糊搜索、分页展示含页码跳转、事务回滚如插入失败时自动回滚已执行的关联操作。它不依赖 Spring Boot 自动装配也不用 Maven 魔法所有依赖明明白白打在WEB-INF/lib/下它不回避web.xml的显式配置反而把 Filter 拦截、字符编码、Session 超时全写清楚。适合想亲手拧紧每一颗螺丝的新手也适合需要快速复现传统 Java Web 架构逻辑的熟手——尤其当你得给新人讲清“请求怎么从浏览器进 Servlet又怎么把 List 传到 JSP”时这套东西比任何 PPT 都管用。2. 从零启动解压即运行的五步落地流程含 JDK、Tomcat、MySQL 版本对齐实操这个 ZIP 包不是“下载即用”的黑盒而是一个需要你亲手校准环境的训练场。它的价值恰恰在于每一步你都看得见、改得了、断点跟得进。下面是我自己在三台不同配置机器Win11 JDK 11 / Ubuntu 22.04 JDK 17 / macOS Sonoma JDK 8上反复验证过的启动路径。重点不是“能不能跑”而是“为什么这一步必须这样配”。2.1 环境版本锚定为什么 JDK 8 是底线而 Tomcat 9 是甜点项目未声明具体 JDK 版本但通过反编译WEB-INF/classes/下的.class文件用javap -v LoginServlet.class | grep major确认其编译目标为major version: 52—— 对应 JDK 8。这意味着✅ 安全兼容JDK 8、11、17 均可运行JVM 向下兼容❌ 明确不支持JDK 21major version: 65会报UnsupportedClassVersionError✅ Tomcat 推荐Tomcat 8.5 / 9.0 / 10.0 均可Servlet 3.1 规范但Tomcat 9.0.x 是最稳选择——它原生支持 JDBC 连接池org.apache.tomcat.jdbc.pool.DataSource且与web.xml中servlet-mapping的路径解析无歧义Tomcat 10 因 Jakarta EE 命名空间迁移javax.*→jakarta.*会导致import javax.servlet.*编译失败需手动替换所有包名不推荐新手碰提示若你本地只有 Tomcat 10请直接降级到 Tomcat 9.0.87 官方归档站这是最后一个支持javax.servlet的稳定版无需改任何 Java 代码。2.2 数据库准备MySQL 5.7 创建库、表、用户三件套含字符集避坑系统使用utf8mb4字符集支撑中文姓名如“范冰”“喆”等四字节 Unicode而非过时的utf8MySQL 中实际为utf8mb3。执行以下 SQL保存为init_db.sql用 MySQL 客户端执行-- 1. 创建数据库显式指定字符集 CREATE DATABASE IF NOT EXISTS student_management CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -- 2. 创建应用专用用户最小权限原则 CREATE USER sm_userlocalhost IDENTIFIED BY SmPass123!; GRANT SELECT, INSERT, UPDATE, DELETE ON student_management.* TO sm_userlocalhost; FLUSH PRIVILEGES; -- 3. 切换到库并建表关键字段注释已嵌入 USE student_management; CREATE TABLE students ( id INT PRIMARY KEY AUTO_INCREMENT, student_id VARCHAR(20) NOT NULL UNIQUE COMMENT 学号唯一索引, name VARCHAR(50) NOT NULL COMMENT 姓名, gender ENUM(男,女) DEFAULT 男, age INT CHECK (age BETWEEN 16 AND 35), major VARCHAR(100), enrollment_date DATE DEFAULT (CURRENT_DATE), created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, INDEX idx_name (name), -- 支持模糊搜索加速 INDEX idx_student_id (student_id) -- 支持精确查询加速 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COLLATEutf8mb4_unicode_ci;参数说明CHARACTER SET utf8mb4是硬性要求否则中文插入会变???COLLATE utf8mb4_unicode_ci比utf8mb4_general_ci排序更准尤其含标点时INDEX idx_name和idx_student_id是性能关键——没有它们LIKE %张%查询在千条数据以上就会明显卡顿CHECK (age BETWEEN 16 AND 35)是 MySQL 8.0.16 才支持的约束若你用 MySQL 5.7请删掉此行业务层校验兜底。2.3 Tomcat 连接池配置context.xml里藏了三个决定成败的参数JDBC 连接不是每次请求都新建而是从池中取。项目在META-INF/context.xml中预置了连接池配置但默认值极易翻车。必须修改以下三处打开context.xml定位Resource标签Resource namejdbc/studentDB authContainer typejavax.sql.DataSource factoryorg.apache.tomcat.jdbc.pool.DataSourceFactory driverClassNamecom.mysql.cj.jdbc.Driver urljdbc:mysql://localhost:3306/student_management?useUnicodetrueamp;characterEncodingutf8mb4amp;serverTimezoneAsia/Shanghaiamp;allowPublicKeyRetrievaltrueamp;useSSLfalse usernamesm_user passwordSmPass123! maxActive20 !-- 关键默认-1无限制会耗尽MySQL连接数 -- minIdle5 !-- 关键空闲时保持5个连接避免冷启动延迟 -- testOnBorrowtrue !-- 关键借连接前检测有效性防MySQL超时断连 -- validationQuerySELECT 1 timeBetweenEvictionRunsMillis30000 removeAbandonedOnBorrowtrue removeAbandonedTimeout60 /参数说明maxActive20生产环境建议设为 MySQLmax_connections的 1/3如 MySQL 设为 151则此处填 50设太高会触发 MySQLToo many connections错误testOnBorrowtruevalidationQuerySELECT 1这是救命配置。MySQL 默认wait_timeout288008小时连接池中闲置连接超时后Servlet 第一次取连接会报Communications link failure开启此检测可自动剔除失效连接url中serverTimezoneAsia/Shanghai必须显式声明否则enrollment_date插入可能错成 UTC 时间尤其 Windows 系统。2.4 项目部署WAR 包生成与 Tomcat 手动发布拒绝 IDE 一键部署幻觉不要用 IntelliJ/Eclipse 的 “Add Deployment” 功能——它会隐藏web.xml加载顺序、Filter 初始化时机等关键细节。请严格走 WAR 手动发布流解压 ZIP 包得到student_management/目录含WEB-INF/,index.jsp,login.jsp等进入该目录执行命令打包Linux/macOSzip -r student_management.war . -x .* -x */\.* -x WEB-INF/lib/*-sources.jar -x WEB-INF/lib/*-javadoc.jar说明-x排除隐藏文件.git,.DS_Store和源码包确保 WAR 干净将生成的student_management.war复制到 Tomcat 的webapps/目录下启动 Tomcatbin/startup.sh或bin/startup.bat观察logs/catalina.out确认出现INFO [main] org.apache.catalina.startup.HostConfig.deployWAR Deploying web application archive [/path/to/tomcat/webapps/student_management.war]及后续INFO ... Deployment of web application archive ... has finished in ... ms浏览器访问http://localhost:8080/student_management/—— 此时应跳转至login.jsp。注意若访问 404请立即检查logs/catalina.out中是否有SEVERE级别错误如ClassNotFoundException: com.mysql.cj.jdbc.Driver这表示mysql-connector-java-8.0.33.jar未正确放入WEB-INF/lib/。2.5 登录与首屏验证用预置账号触发完整请求链路项目内置测试账号见src/com/example/dao/LoginDAO.java中硬编码用户名admin密码123456输入后点击登录成功则跳转至listStudents.jsp显示学生列表。此时你已在真实环境中走通了Browser → HTTP Request → Tomcat → web.xml → LoginServlet → LoginDAO → JDBC Pool → MySQL → ResultSet → Student JavaBean → request.setAttribute() → JSP EL 表达式渲染这条链路上任何一个环节出问题你都能在对应位置加断点如LoginServlet.doPost()第一行System.out.println(Login request received);或查日志定位。这才是“可调试”的意义。3. 代码层拆解Servlet 路由、JavaBean 封装、JSP 渲染的三角闭环如何咬合这个系统没有用 MVC 框架但通过web.xmlServletJavaBeanJSP四者协作实现了清晰的职责分离。理解它们如何“咬合”比记住代码更重要——因为一旦需求变更比如加个导出 Excel 功能你知道该在哪一层动刀。3.1web.xml不是过时配置而是请求分发的总控台WEB-INF/web.xml是整个应用的“中枢神经”它定义了哪些 URL 路径由哪个 Servlet 处理servlet-mapping哪些请求必须经过统一字符编码过滤filter-mappingSession 超时时间session-config默认首页welcome-file-list。关键片段解析!-- 1. 定义 LoginServlet -- servlet servlet-nameLoginServlet/servlet-name servlet-classcom.example.servlet.LoginServlet/servlet-class /servlet !-- 2. 将 /login 路径映射到 LoginServlet -- servlet-mapping servlet-nameLoginServlet/servlet-name url-pattern/login/url-pattern /servlet-mapping !-- 3. 全局字符编码过滤器解决 POST 中文乱码 -- filter filter-nameCharacterEncodingFilter/filter-name filter-classcom.example.filter.CharacterEncodingFilter/filter-class init-param param-nameencoding/param-name param-valueUTF-8/param-value /init-param /filter filter-mapping filter-nameCharacterEncodingFilter/filter-name url-pattern/*/url-pattern !-- 拦截所有请求 -- /filter-mapping !-- 4. Session 30分钟超时 -- session-config session-timeout30/session-timeout /session-config !-- 5. 访问根路径时默认加载 login.jsp -- welcome-file-list welcome-filelogin.jsp/welcome-file /welcome-file-list逻辑说明当浏览器请求/student_management/login时Tomcat 查web.xml发现/login匹配LoginServlet于是实例化LoginServlet并调用其doPost()CharacterEncodingFilter在LoginServlet执行前被调用强制request.setCharacterEncoding(UTF-8)否则request.getParameter(username)会是乱码session-timeout设为 30意味着用户 30 分钟无操作Session 自动销毁下次访问需重新登录welcome-file-list让http://localhost:8080/student_management/自动导向login.jsp而非显示目录列表Tomcat 默认行为。3.2LoginServletHTTP 协议与业务逻辑的翻译官LoginServlet不是“处理登录”而是将 HTTP 请求翻译成 Java 方法调用并将 Java 返回值翻译成 HTTP 响应。其核心逻辑只有三步// src/com/example/servlet/LoginServlet.java protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // Step 1: 从 HTTP 请求中提取参数翻译请求 String username request.getParameter(username); // 来自 login.jsp 的 input[nameusername] String password request.getParameter(password); // Step 2: 调用业务层DAO验证调用 Java 方法 LoginDAO loginDAO new LoginDAO(); boolean isValid loginDAO.validateUser(username, password); // Step 3: 根据结果决定 HTTP 响应翻译响应 if (isValid) { // 登录成功存 Session重定向到学生列表页 request.getSession().setAttribute(currentUser, username); response.sendRedirect(request.getContextPath() /listStudents); } else { // 登录失败设置错误信息转发回登录页保留原URL request.setAttribute(errorMessage, 用户名或密码错误); request.getRequestDispatcher(/login.jsp).forward(request, response); } }参数说明request.getParameter()是 HTTP GET/POST 表单数据的唯一入口name属性必须与 JSP 中input的name严格一致response.sendRedirect()是 302 重定向浏览器 URL 会变成/student_management/listStudentsrequest.getRequestDispatcher().forward()是服务器端转发URL 不变但页面内容换成login.jsp且errorMessage可在 JSP 中用${errorMessage}输出request.getContextPath()返回应用上下文名如/student_management确保重定向路径可移植不写死/student_management/listStudents。3.3StudentJavaBean不是 POJO是数据契约的具象化Student.java不是简单的 getter/setter而是定义了数据库表与 Java 对象之间的映射契约。其字段名、类型、顺序必须与students表的列严格对应// src/com/example/bean/Student.java public class Student { private int id; // 对应表中 id INT private String studentId; // 对应表中 student_id VARCHAR(20) private String name; // 对应表中 name VARCHAR(50) private String gender; // 对应表中 gender ENUM private int age; // 对应表中 age INT private String major; // 对应表中 major VARCHAR(100) private Date enrollmentDate; // 对应表中 enrollment_date DATE // 必须有无参构造JDBC ResultSet 映射所需 public Student() {} // 全参构造方便 DAO 层 new Student(...) public Student(int id, String studentId, String name, String gender, int age, String major, Date enrollmentDate) { this.id id; this.studentId studentId; this.name name; this.gender gender; this.age age; this.major major; this.enrollmentDate enrollmentDate; } // getter/setter 省略IDE 自动生成 }逻辑说明private int id与数据库id INT PRIMARY KEY类型匹配JDBCrs.getInt(id)才不会报SQLExceptionprivate String studentId对应student_id VARCHAR(20)命名用驼峰Java 习惯而非下划线SQL 习惯但 DAO 层rs.getString(student_id)显式指定列名规避命名差异private Date enrollmentDate对应enrollment_date DATErs.getDate(enrollment_date)返回java.sql.Date可直接赋值无参构造是ResultSet映射的硬性要求while(rs.next()) { Student s new Student(); s.setId(rs.getInt(id)); ... }。3.4listStudents.jspEL 表达式不是语法糖是 MVC 中的 View 终端JSP 不是“写 HTML”而是将 Java 对象Model通过 EL 表达式注入 HTML 模板View。listStudents.jsp的核心就是遍历request.getAttribute(studentList)% page contentTypetext/html;charsetUTF-8 languagejava % % taglib prefixc urihttp://java.sun.com/jsp/jstl/core % html headtitle学生列表/title/head body h2学生信息列表/h2 !-- 1. 检查 studentList 是否为空 -- c:if test${empty studentList} p暂无学生信息/p /c:if !-- 2. 遍历 studentList来自 StudentServlet 的 request.setAttribute -- c:forEach varstudent items${studentList} div classstudent-item p学号${student.studentId}/p p姓名${student.name}/p p性别${student.gender}/p p年龄${student.age}/p p专业${student.major}/p p入学日期${student.enrollmentDate}/p a hrefeditStudent?id${student.id}编辑/a | a hrefdeleteStudent?id${student.id} onclickreturn confirm(确定删除 ${student.name})删除/a /div hr /c:forEach !-- 3. 分页导航来自 StudentServlet 的 request.getAttribute(pagination) -- div classpagination c:if test${pagination.hasPreviousPage} a hreflistStudents?pageNum${pagination.prePage}上一页/a /c:if span第 ${pagination.pageNum} 页共 ${pagination.pages} 页/span c:if test${pagination.hasNextPage} a hreflistStudents?pageNum${pagination.nextPage}下一页/a /c:if /div /body /html逻辑说明c:forEach是 JSTL 核心标签items${studentList}中的studentList是StudentServlet通过request.setAttribute(studentList, list)传入的ListStudent${student.studentId}是 EL 表达式自动调用Student.getStudentId()无需写student.getStudentId()onclickreturn confirm(...)是前端防护防止误点删除但后端DeleteServlet仍需做权限校验当前项目未实现属可扩展点分页对象pagination是一个自定义 JavaBeanPagination.java包含pageNum,pages,prePage,nextPage等属性体现“数据结构即契约”。4. 避坑指南五个血泪经验总结的高频翻车点与排查路径这个系统看似简单但在真实部署中90% 的失败不是代码问题而是环境、配置、认知偏差导致的“玄学错误”。以下是我在三台机器、四种 JDK/Tomcat 组合、六次重装 MySQL 后总结的五大必踩坑每一条都附带现象、根因、可执行解决方案。4.1 现象登录页提交后空白catalina.out无错误浏览器 Network 显示 200 但 Response 为空原因CharacterEncodingFilter未生效导致request.getParameter()返回nullLoginDAO.validateUser(null, null)抛NullPointerException但 Filter 的doFilter()中未chain.doFilter()异常被静默吞掉。解决检查web.xml中filter-mapping的url-pattern是否为/*必须是/*不能是/或/login在CharacterEncodingFilter.doFilter()开头加日志System.out.println(Filter executed for: request.getRequestURI());若日志未打印说明 Filter 未注册成功检查filter和filter-mapping的filter-name是否完全一致大小写敏感。4.2 现象学生列表页显示??或 但数据库里中文正常原因JDBC URL 缺少characterEncodingutf8mb4或 MySQL 服务端my.cnf未配置default-character-set utf8mb4。解决检查context.xml中url参数确认包含characterEncodingutf8mb4注意是amp;而非XML 实体转义登录 MySQL执行SHOW VARIABLES LIKE character_set%;确认character_set_client,character_set_connection,character_set_database均为utf8mb4若非utf8mb4修改 MySQL 配置文件my.cnfLinux/macOS或my.iniWindows在[mysqld]下添加[mysqld] character-set-server utf8mb4 collation-server utf8mb4_unicode_ci重启 MySQL 服务。4.3 现象点击“删除”后跳转到 404URL 变为http://localhost:8080/deleteStudent?id1原因web.xml中DeleteServlet的url-pattern配置错误或DeleteServlet类未正确编译到WEB-INF/classes/。解决检查web.xml确认存在servlet servlet-nameDeleteServlet/servlet-name servlet-classcom.example.servlet.DeleteServlet/servlet-class /servlet servlet-mapping servlet-nameDeleteServlet/servlet-name url-pattern/deleteStudent/url-pattern !-- 必须是 /deleteStudent不是 deleteStudent -- /servlet-mapping检查WEB-INF/classes/com/example/servlet/DeleteServlet.class文件是否存在若不存在用javac -cp WEB-INF/lib/*:. -d WEB-INF/classes src/com/example/servlet/DeleteServlet.java手动编译注意-cp路径分隔符Linux/macOS 用:Windows 用;。4.4 现象分页点击“下一页”后数据重复或总数计算错误原因StudentDAO.getStudentCount()与StudentDAO.getStudentsByPage()使用了不同的WHERE条件如搜索功能未同步到计数 SQL。解决打开StudentDAO.java对比两个方法的 SQL// getStudentCount() 应与 getStudentsByPage() 的 WHERE 子句完全一致 String countSql SELECT COUNT(*) FROM students WHERE 11; if (searchKeyword ! null !searchKeyword.trim().isEmpty()) { countSql AND (name LIKE ? OR student_id LIKE ?); } // getStudentsByPage() 的 SQL 必须有相同条件 String sql SELECT * FROM students WHERE 11; if (searchKeyword ! null !searchKeyword.trim().isEmpty()) { sql AND (name LIKE ? OR student_id LIKE ?); } sql LIMIT ?, ?;在getStudentCount()中ps.setString(1, % keyword %); ps.setString(2, % keyword %);的参数索引必须与getStudentsByPage()中ps.setString(1, % keyword %)等一致。4.5 现象Tomcat 启动时报java.lang.ClassNotFoundException: com.mysql.cj.jdbc.Driver原因mysql-connector-java-8.0.33.jar未放入WEB-INF/lib/或版本与 MySQL 不匹配MySQL 5.7 需用mysql-connector-java-5.1.49.jarMySQL 8.0 需用8.0.33。解决确认WEB-INF/lib/下存在mysql-connector-java-*.jar如mysql-connector-java-8.0.33.jar检查 MySQL 版本mysql --version若 MySQL 是 5.7下载 mysql-connector-java-5.1.49 替换WEB-INF/lib/下的 jar删除WEB-INF/lib/下所有*-sources.jar和*-javadoc.jar它们不参与运行但可能干扰类加载。5. 进阶实战为系统注入搜索增强、事务保障与跨域兼容三大能力现在系统能跑但离“可用”还有距离。真正的工程能力体现在你能根据新需求在不破坏原有结构的前提下精准地在 Servlet 层加逻辑、在 DAO 层加 SQL、在 JSP 层加交互。下面我以三个真实场景为例演示如何让这个老架构焕发新生。5.1 场景一搜索框支持“姓名专业”组合模糊搜索非简单 OR原始搜索只支持name LIKE ? OR student_id LIKE ?但业务方要求“输入‘计算机’时同时匹配专业为‘计算机科学与技术’且姓名含‘李’的学生”。这需要动态构建 SQL WHERE 子句。改造步骤修改listStudents.jsp增加专业筛选下拉框form actionlistStudents methodget input typetext namekeyword placeholder姓名或学号 value${param.keyword} select namemajor option value全部专业/option option value计算机科学与技术 ${param.major 计算机科学与技术 ? selected : }计算机科学与技术/option option value软件工程 ${param.major 软件工程 ? selected : }软件工程/option /select button typesubmit搜索/button /form修改StudentServlet.doGet()解析major参数并传入 DAOString keyword request.getParameter(keyword); String major request.getParameter(major); ListStudent list studentDAO.getStudentsByPage(pageNum, pageSize, keyword, major);修改StudentDAO.getStudentsByPage()动态拼接 WHEREStringBuilder sql new StringBuilder(SELECT * FROM students WHERE 11); ListString params new ArrayList(); if (keyword ! null !keyword.trim().isEmpty()) { sql.append( AND (name LIKE ? OR student_id LIKE ?)); params.add(% keyword.trim() %); params.add(% keyword.trim() %); } if (major ! null !major.trim().isEmpty()) { sql.append( AND major ?); params.add(major.trim()); } sql.append( LIMIT ?, ?); params.add((pageNum - 1) * pageSize); params.add(pageSize); // 执行 PreparedStatement用 params.toArray() 传参 PreparedStatement ps connection.prepareStatement(sql.toString()); for (int i 0; i params.size(); i) { ps.setObject(i 1, params.get(i)); }效果搜索逻辑从静态 OR 变为可配置的 AND/OR 组合且 SQL 注入风险被PreparedStatement消除。5.2 场景二新增学生时同时插入“入学记录”表要求两表操作原子性当前AddStudentServlet只插入students表。现需新增enrollment_records表记录学生入学时间、辅导员、班级并保证若enrollment_records插入失败students表的插入也必须回滚。改造步骤创建新表CREATE TABLE enrollment_records ( id INT PRIMARY KEY AUTO_INCREMENT, student_id VARCHAR(20) NOT NULL, counselor VARCHAR(50), class_name VARCHAR(50), record_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (student_id) REFERENCES students(student_id) ON DELETE CASCADE ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;修改AddStudentServlet使用 JDBC 事务Connection conn null; try { conn DataSourceUtil.getConnection(); // 从连接池获取 conn.setAutoCommit(false); // 关闭自动提交 // Step 1: 插入 students 表 String insertStudentSql INSERT INTO students (student_id, name, gender, age, major) VALUES (?, ?, ?, ?, ?); PreparedStatement ps1 conn.prepareStatement(insertStudentSql, Statement.RETURN_GENERATED_KEYS); ps1.setString(1, studentId); ps1.setString(2, name); ps1.setString(3, gender); ps1.setInt(4, age); ps1.setString(5, major); ps1.executeUpdate(); // Step 2: 获取自增主键 ResultSet rs ps1.getGeneratedKeys(); int generatedId 0; if (rs.next()) { generatedId rs.getInt(1); } // Step 3: 插入 enrollment_records 表用 student_id 关联 String insertRecordSql INSERT INTO enrollment_records (student_id, counselor, class_name) VALUES (?, ?, ?); PreparedStatement ps2 conn.prepareStatement(insertRecordSql); ps2.setString(1, studentId); ps2.setString(2, counselor); ps2.setString(3, className); ps2.executeUpdate(); conn.commit(); // 两步都成功才提交 request.setAttribute(message, 添加成功); } catch (SQLException e) { if (conn ! null) { try { conn.rollback(); // 任一步失败回滚全部 } catch (SQLException ex) { ex.printStackTrace(); } } request.setAttribute(errorMessage, 添加失败 e.getMessage()); } finally { if (conn ! null) { try { conn.setAutoCommit(true); // 恢复自动提交 conn.close(); } catch (SQLException e) { e.printStackTrace(); } } }效果通过conn.setAutoCommit(false)conn.commit()/conn.rollback()将两个物理操作封装为一个逻辑事务满足 ACID。5.3 场景三允许前端 Vue/React 应用通过 AJAX 调用后端 API跨域支持当前系统是纯 JSP 渲染但团队要开发独立前端。需让listStudents等接口支持Content-Type: application/json和 CORS。改造步骤新建JsonStudentServlet继承HttpServlet复用StudentDAO逻辑protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType(application/json;charsetUTF-8); response.setHeader(Access-Control-Allow-Origin, *); // 开发期允许所有源 response.setHeader(Access-Control-Allow-Methods, GET, POST, OPTIONS); response.setHeader(Access-Control-Allow-Headers, Content-Type); String pageNumStr request.getParameter(pageNum); int pageNum pageNumStr ! null ? Integer.parseInt(pageNumStr) : 1; ListStudent list studentDAO.getStudentsByPage(pageNum, 10, null, null); // 手动序列化为 JSON不用 Jackson避免引入新依赖 JSONArray jsonArray new JSONArray(); for (Student s : list) { JSONObject obj new JSONObject(); obj.put(id, s本文还有配套的精品资源点击获取
RELATED

相关推荐

C# WinForms自定义TabControl美化实战:高DPI与深色模式兼容方案

C# WinForms自定义TabControl美化实战:高DPI与深色模式兼容方案

简介:这是一份面向C#桌面应用开发者的TabControl控件定制化学习资源,聚焦Windows Forms界面美化与交互增强,适用于希望突破默认UI限制、提升产品视觉表现力的中初级开发者。资源提供完整的美化版TabControl源码实现,涵盖自定义绘制…

📅 2026/10/11 16:26:44
鼠标宏科普:G502压枪宏的Lua脚本原理与DPI灵敏度调参指南

鼠标宏科普:G502压枪宏的Lua脚本原理与DPI灵敏度调参指南

简介:一份面向FPS玩家的罗技G502鼠标压枪宏配置包,适用于CS:GO、PUBG、Apex等全自动武器频繁交战的射击场景。资源共4个文件、约38KB,其中macro-G502.lua为核心宏脚本,提供可导入G-Hub的按键序列;G502-config.xml为预置…

📅 2026/10/11 16:21:44
餐饮采购系统数据库建设:从Word清单到可计算结构化数据

餐饮采购系统数据库建设:从Word清单到可计算结构化数据

简介:本资源是一份面向餐饮信息化系统开发者、数据库设计人员及供应链管理从业者的专业文档,聚焦餐饮食品采购系统中核心的原材料清单数据库建设方案。文档系统梳理了蔬菜类食材的标准化编码体系(如VG/Asparagus, Green Large)、规…

📅 2026/10/11 16:21:44
MORE NEWS

更多资讯

📰

C#仿QQ俄罗斯方块双人对战:WinForms实现与源码解析

简介:一款基于C#的仿QQ俄罗斯方块双人同屏对战源码包,面向C#学习者和游戏开发初学者,完整展示了单机双人对战场景下从游戏逻辑到界面交互的编码过程。项目覆盖方块生成、移动旋转、行消除与计分判定,并通过多线程与事件驱动机制处…

📰

数学建模中多元回归的实战落地与诊断避坑指南

简介:本资源是一份面向数学建模初学者与高校经管类专业学生的多元线性回归实战教学文档,聚焦粮食销售量预测这一典型社会经济问题,系统讲解变量筛选、模型构建、Matlab实现(stepwise逐步回归)、统计检验(R、…

📰

Redis 持久化怎么配:RDB 和 AOF 的默认值、丢数据窗口与重写的代价

本文首发于 CSDN,转载请注明出处。 先说结论:RDB 和 AOF 不是二选一,官方建议两个都开。RDB 是周期性快照,文件小、恢复快,代价是丢数据窗口大;AOF 是追加写日志,appendfsync everysec 下最坏丢…

📰

C++双人战斗游戏源码拆解:状态机与碰撞盒的实战设计

简介:一套基于VC编写的双人战斗游戏完整源代码,适合C学习者、游戏开发入门者以及需要参考小型实战项目的学生。代码已在VC环境下调试通过,工程包含完整的游戏逻辑、图形资源、音频素材与编译配置,并支持双人实时对战。压缩包共59个…

📰

YOLOv8纸箱检测实战:从模型部署到PyQt界面落地

简介:一套面向物流电商场景的YOLOv8纸质包装盒与快递盒检测方案,内置已训练好的模型权重,可直接完成推理,并配套PyQt图形界面,帮助开发者解决纸质包装箱与快递盒在复杂背景下的识别难题,适合需要快速落地目…

📰

新消费品牌势能增长:可落地的分析框架与Python实战

简介:这份《2024年中国新消费品牌势能创新增长研究白皮书》由艾克战略创新咨询出品,面向品牌营销从业者、创业者及商业研究者,系统梳理新消费品牌在营销模式与商业思维上的演变路径。资源包内含1个PDF文件,大小约2.45MB&#xff0…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬