新闻详情

新闻详情

首页 / 资讯中心 / 详情

Java+Servlet+JDBC+JSP+MySQL学生信息管理系统实战与踩坑指南

发布时间:2026/9/24 23:32:44来源:尧图网络
Java+Servlet+JDBC+JSP+MySQL学生信息管理系统实战与踩坑指南
简介一套基于服务端小程序Servlet、动态网页技术JSP与数据库连接接口JDBC并搭配MySQL数据库实现的Web学生信息管理系统。项目围绕学生信息的增删改查展开涵盖JSP页面展示、Servlet请求分发、JDBC数据访问及数据库表设计流程完整且采用MVC分层思路适合具备Java基础的Web开发初学者用于课程设计或毕业设计参考。压缩包共78个文件包含20个JSP页面、12个Java源文件、24个class编译文件以及XML配置、SQL建表脚本、JAR依赖包、示例图片和工程配置文件整体大小4.48MB目录结构清晰便于导入集成开发环境直接运行。系统在代码中完整呈现了从前端表单提交到Servlet处理、再经JDBC访问MySQL并返回结果给JSP渲染的整个过程有助于理解Java Web后端开发的核心链路。目前该资源已有6944人学习提供可直接部署运行的完整源码与数据库脚本导入数据库并修改连接信息后即可使用既适合对照学习也方便二次扩展。1. 这套JavaServletJdbcJspMysql组合为什么年年出现在毕设清单里每年三月份计算机专业的学生会收到同一道选择题基于JSP的毕设选题清单里压着一堆“学生信息管理系统”“图书管理系统”“停车管理系统”而JavaServletJdbcJspMysql就是这些课设毕设里最经典的底座。它没有Spring Boot那种一行注解就把什么都准备好的自动装配数据库连接是你自己开的、请求是你自己接的、页面是你自己拼的每一步都看得见这恰恰是它最大的价值。没有框架黑匣子把一条“浏览器发请求→Servlet处理→JDBC读写MySQL→JSP回显结果”的链路完整走一遍之后再去看SSM、SpringBoot的工程结构会顺很多。适合谁两种人一是要做课设或毕设想找一个到答辩时能讲清楚每个环节的系统框架的同学二是准备Java面试、想拿Servlet生命周期、JDBC连接、Session会话这些八股考点练手的应届生。这套组合在“跑通全流程”和“理解底层机制”之间卡得刚刚好向下能看清一次HTTP请求怎么被Tomcat接住向上能理解后来为什么会有IoC容器和ORM框架来替你省事。2. 系统功能与分层架构先想清楚四个组件各自干什么再敲代码2.1 功能模块拆解登录、学生信息增删改查、分页搜索一个能通过答辩的学生信息管理系统底盘功能其实就三块登录认证、学生信息的基础增删改查、列表分页与按学号/姓名搜索。这三块足够覆盖“有状态会话、CRUD、列表查询”这三个JavaWeb最重要的应用场景也刚好对应Servlet里HttpServletRequest/Response、Cookie与Session、JDBC里PreparedStatement这些高频考点。建议动手前先列一张功能清单每行只写一件事登录用户名密码校验成功后跳转列表页失败回到登录页并提示错误学生列表分页展示学生信息支持按学号或姓名模糊搜索新增学生表单页提交校验学号唯一性成功后回到列表页编辑学生列表页点“编辑”进入预填表单提交后更新数据库删除学生支持单条删除和勾选批量删除权限、批量导入、导出Excel、修改密码、重置密码这些先记到升级清单里不要在第一版就做。课程设计级别的系统功能边界比功能数量重要得多——做了一堆功能但每个都跑不顺答辩时反而容易被连续追问到翻车。2.2 为什么控制层是Servlet而不是Spring Boot选型背后的逻辑很多同学会问现在写Web项目不都用Spring Boot吗为什么这题目还要求Servlet答案是这个组合里的四个组件各管一段职责边界极其清晰——JSP负责展示Servlet负责流程控制JDBC负责数据库访问MySQL负责存储。Servlet在这里是唯一的请求入口它拿到参数、调DAO、把结果放进request或session、再forward到JSP整条链路没有自动配置、没有AOP切面一切都是显式调用。如果在这个题目里硬上Spring Boot项目结构是controller/service/mapper分包但对一个还没搞懂IoC容器和代理对象的人来说出问题时面对的是一个真正的黑匣子。你只知道注解一加就能用却说不清请求是怎么绑定到方法的。SpringBoot集成JSP其实也是一件麻烦事默认不支持JSP视图需要手工改application.properties、调整src/main/webapp目录结构坑比想象中多。所以这个课设题目选Servlet本质上是让你先把最原始的HTTP处理和JDBC连接走一遍这些底子在后面看SpringMVC、MyBatis时全是相通的面试被问到也能多讲出两层细节。2.3 项目目录与运行环境IDEA、Tomcat和WEB-INF/lib的约定环境上用JDK 1.8或8以上、MySQL 5.7或8.0、Tomcat 8.5或9.0IDE建议IntelliJ IDEA Community版开箱就能配置Tomcat。用VS Code写Servlet需要手动做环境配置下载Tomcat插件或者靠Maven依赖把servlet-api引进来classpath和部署目录都得自己弄对刚入门的人来说容易在环境上耗掉半天不划算。webapp/ ├── WEB-INF/ │ ├── web.xml │ └── lib/ │ └── mysql-connector-java-8.0.33.jar ├── login.jsp ├── student_list.jsp ├── student_add.jsp ├── student_edit.jsp └── css/src目录按包分com.student.util放DBUtilcom.student.dao放StudentDao、UserDaocom.student.entity放Student、User实体类com.student.servlet放各个Servlet。这里有个最容易踩的坑数据库驱动jar包必须放在WEB-INF/lib目录下。放在IDEA的external libraries里虽然编译能通过但Tomcat运行时加载不到类直接报ClassNotFoundException。这属于WEB-INF的类加载约定新手第一次遇到基本都要折腾半小时。3. 数据库表设计与JDBC连接层先让数据能在Java和MySQL之间流动3.1 建库建表两张表足够字段和索引提前设计好这套系统的数据量很小两张表就能装下全部需求一张t_user存登录账号一张t_student存学生信息。下面是完整的建库建表语句CREATE DATABASE IF NOT EXISTS student_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE student_db; CREATE TABLE t_user ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(64) NOT NULL, create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE t_student ( id INT PRIMARY KEY AUTO_INCREMENT, student_no VARCHAR(20) NOT NULL UNIQUE, name VARCHAR(50) NOT NULL, gender CHAR(2) DEFAULT 男, age INT, class_name VARCHAR(50), phone VARCHAR(20), email VARCHAR(100), create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;逻辑说明t_student用student_no做唯一索引防止同一个学号被重复插入这个唯一约束在DAO层做新增时会派上用场——插入重复学号会抛出DuplicateKeyException代码里可以据此提示“学号已存在”。gender字段用CHAR(2)而不是VARCHAR因为存储内容固定是“男/女”用CHAR更省空间。create_time给个默认值CURRENT_TIMESTAMP插入时可以少传一个字段。参数说明字符集统一用utf8mb4而不是老旧的utf8utf8mb4是utf8的超集能正确存储生僻字和emoji字符MySQL 8.0里utf8mb3已经标记为废弃新库直接选utf8mb4最稳妥。注意一个容易忽略的设计点表名用了t_user而不是user。user在MySQL里是特殊含义的保留字MySQL系统库中自带mysql.user表直接建user表虽然能建但后续写SQL时不加反引号很容易报语法错误。用t_user前缀从根源上避开了这个坑这也是很多MySQL老手带新人时的约定习惯。3.2 JDBC工具类与URL参数useSSL、serverTimezone、characterEncoding逐个拆开讲JDBC连接层是整个系统的地基我一般会先写一个DBUtil工具类把注册驱动、获取连接、关闭资源三个动作收敛到一个类里所有DAO方法复用它package com.student.util; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; public class DBUtil { private static final String URL jdbc:mysql://localhost:3306/student_db?useSSLfalseserverTimezoneAsia/ShanghaicharacterEncodingutf8; private static final String USER root; private static final String PASSWORD 123456; static { try { Class.forName(com.mysql.cj.jdbc.Driver); } catch (ClassNotFoundException e) { e.printStackTrace(); } } public static Connection getConnection() throws SQLException { return DriverManager.getConnection(URL, USER, PASSWORD); } public static void close(Connection conn, Statement stmt, ResultSet rs) { if (rs ! null) { try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } } if (stmt ! null) { try { stmt.close(); } catch (SQLException e) { e.printStackTrace(); } } if (conn ! null) { try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } }逻辑说明Class.forName(com.mysql.cj.jdbc.Driver)放在静态代码块里类加载时只执行一次。MySQL 8.0的驱动类名是com.mysql.cj.jdbc.Driver而5.x版本是com.mysql.jdbc.Driver如果沿用5.x的写法并升级到8.0类加载会直接失败。这个类名的变化是很多项目升级时翻车的第一现场。参数说明URL末尾三个参数每一个都有作用缺一个都会在运行时出问题。useSSLfalse表示本地开发不启用SSL加密连接MySQL 8.0的驱动默认反而会尝试SSL握手不显式关闭会有一大串告警日志更高版本的Connector/J还引入了sslMode参数取值包括DISABLED、PREFERRED、REQUIRED等本地测试环境直接DISABLED最干净。serverTimezoneAsia/Shanghai解决时区问题不写这个参数MySQL 8.0会报The server time zone value Öйú±ê׼ʱ¼ä这类乱码错误因为驱动拿不到服务器时区。characterEncodingutf8保证中文从Java到MySQL不会乱码配合数据库的utf8mb4字符集统一编码。另外不同数据库的JDBC URL写法出入很大比如某些国产数据库有targetServerType之类的专属参数迁移连接配置时不要照着MySQL的URL硬套否则连接能建立但行为不符合预期排查起来非常费劲。close方法的设计也值得说明一下参数用了Connection、Statement、ResultSet三个类型的接口而不是具体的类这样无论后续换成连接池还是复用驱动关闭逻辑都不用改。每个资源单独判空再关闭避免有资源为null时调用close导致NPE。3.3 DAO层设计按查询场景拆方法而不是按表拆一个万能方法新手写DAO很容易写成一个方法干所有事例如一个execute(String sql)方法接收外部拼好的SQL字符串谁调用谁拼条件。这个做法看起来省事实际上每个调用方都要自己拼SQLSQL注入风险直接拉满而且一旦字段变更所有调用点全要改。按查询场景拆方法才是更稳的做法。以StudentDao为例第一版只需要五个方法public ListStudent findPage(int page, int pageSize, String keyword) public int count(String keyword) public Student findById(int id) public int insert(Student student) public int update(Student student) public int deleteById(int id)逻辑说明查询列表和统计总数分开是因为分页查询需要先拿到总数才能计算出总页数编辑时需要有一个findById把原记录查出来回显到表单新增和更新各自独立不合并成一个“save”。每个方法内部都是固定套路写SQL、用PreparedStatement设置参数、执行、遍历ResultSet转成实体对象、finally里关闭资源。参数说明count方法返回int而不是long因为课设规模的数据量远不到int上限findPage的入参里page从1开始计数而SQL里LIMIT的偏移量从0开始换算关系是offset (page - 1) * pageSize这一点在写SQL时最容易搞反。4. ServletJSP把增删改查跑通请求怎么进、参数怎么拿、页面怎么回4.1 登录功能与Session会话doPost里做校验Filter里做拦截登录是整个系统唯一需要“记住我是谁”的地方用Session保存登录状态是Servlet时代的标准做法。下面是一个最直观的登录ServletWebServlet(/login) public class LoginServlet extends HttpServlet { Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setCharacterEncoding(UTF-8); String username req.getParameter(username); String password req.getParameter(password); UserDao dao new UserDao(); User user dao.findByUsernameAndPassword(username, password); if (user ! null) { HttpSession session req.getSession(); session.setAttribute(loginUser, user); resp.sendRedirect(req.getContextPath() /student/list); } else { req.setAttribute(error, 用户名或密码错误); req.getRequestDispatcher(/login.jsp).forward(req, resp); } } }逻辑说明req.setCharacterEncoding(UTF-8)必须放在获取任何参数之前否则POST请求里的中文参数会乱码。登录成功后的操作是把User对象放进Session再用resp.sendRedirect做重定向这样浏览器地址会变成列表页URL刷新时不会重复提交登录表单。登录失败则用forward回到login.jsp同时通过request域的error字段把错误信息带到页面显示。参数说明req.getParameter(username)取的是表单里nameusername的输入框值。注意getParameter只认name属性不认id属性新手常在JSP里用id取值结果Servlet里拿到null这个细节值得留意。拦截器也是这个环节的一部分。用Filter对“未登录用户访问列表页”做统一拦截比在每个Servlet里手动判断要优雅得多WebFilter(/*) public class LoginFilter implements Filter { Override 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.contains(/login)) { chain.doFilter(req, resp); return; } HttpSession session request.getSession(false); if (session null || session.getAttribute(loginUser) null) { response.sendRedirect(request.getContextPath() /login.jsp); return; } chain.doFilter(req, resp); } }逻辑说明这个Filter拦截所有请求放行登录相关URL其余请求一律检查Session里有没有loginUser没有就把请求重定向回登录页。这里用request.getSession(false)而不是getSession()区别在于前者不会自动创建新Session——未登录用户访问时不应该产生一个无意义的Session对象。参数说明Filter的urlPatterns配成/*表示拦截所有请求包括静态资源所以如果用了CSS、JS、图片应当在Filter头部加一段静态资源放行逻辑或者把项目里静态资源统一放在/static/前缀下单独放行。4.2 学生列表、分页与搜索LIMIT和keyword回显是核心列表页是学生信息管理系统的门面分页加搜索是最容易讲出彩的部分。先用JDBC把分页查询写出来public ListStudent findPage(int page, int pageSize, String keyword) { ListStudent list new ArrayList(); String sql SELECT * FROM t_student WHERE name LIKE ? OR student_no LIKE ? ORDER BY id DESC LIMIT ?, ?; int offset (page - 1) * pageSize; try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setString(1, % keyword %); ps.setString(2, % keyword %); ps.setInt(3, offset); ps.setInt(4, pageSize); try (ResultSet rs ps.executeQuery()) { while (rs.next()) { Student s new Student(); s.setId(rs.getInt(id)); s.setStudentNo(rs.getString(student_no)); s.setName(rs.getString(name)); s.setGender(rs.getString(gender)); s.setAge(rs.getInt(age)); s.setClassName(rs.getString(class_name)); s.setPhone(rs.getString(phone)); s.setEmail(rs.getString(email)); list.add(s); } } } catch (SQLException e) { e.printStackTrace(); } return list; }逻辑说明模糊查询用LIKE ?配合%keyword%占位符这里只对keyword做拼接SQL主体保持预编译状态从源头杜绝SQL注入。ORDER BY id DESC让新录入的学生排在最前面符合“最近操作的记录最显眼”的使用直觉这里的排序字段选择也体现了mysql排序应用的一个细节——按id排比按create_time排更稳定因为id天然唯一且自增而create_time精度到秒同一秒插入的数据顺序无法保证。参数说明LIMIT ?, ?两个参数第一个是偏移量(page - 1) * pageSize不是页码本身第二个是每页条数。LIMIT占位符在PreparedStatement中可以用setInt绑定MySQL驱动的LIMIT参数必须是int或long不能用字符串类型。pageSize一般取5或10课设演示中用5更能直观看到翻页效果。配套的还有统计总数的count方法和列表页的Servletpublic int count(String keyword) { String sql SELECT COUNT(*) FROM t_student WHERE name LIKE ? OR student_no LIKE ?; try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setString(1, % keyword %); ps.setString(2, % keyword %); try (ResultSet rs ps.executeQuery()) { if (rs.next()) { return rs.getInt(1); } } } catch (SQLException e) { e.printStackTrace(); } return 0; }逻辑说明COUNT(*)返回的是当前搜索条件下的总记录数分页的总页数公式是(total pageSize - 1) / pageSize或者(int) Math.ceil(total * 1.0 / pageSize)。4.3 新增与编辑表单提交、参数校验与编辑回显新增学生的Servlet要处理表单里所有字段并对年龄这种需要转类型的字段做判空处理WebServlet(/student/add) public class StudentAddServlet extends HttpServlet { Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setCharacterEncoding(UTF-8); String studentNo req.getParameter(studentNo); String name req.getParameter(name); String gender req.getParameter(gender); String ageStr req.getParameter(age); String className req.getParameter(className); Student s new Student(); s.setStudentNo(studentNo); s.setName(name); s.setGender(gender); if (ageStr ! null !ageStr.isEmpty()) { s.setAge(Integer.parseInt(ageStr)); } s.setClassName(className); StudentDao dao new StudentDao(); if (dao.insert(s) 0) { resp.sendRedirect(req.getContextPath() /student/list); } else { req.setAttribute(error, 学号已存在请更换后重试); req.getRequestDispatcher(/student_add.jsp).forward(req, resp); } } }逻辑说明Integer.parseInt(ageStr)可能抛NumberFormatException所以前端表单里对age输入框加typenumber限制输入内容后端再兜底判空。新增成功后重定向到列表页防止F5刷新时重复提交表单——这是PRGPost-Redirect-Get模式的核心好处。失败时回到新增页展示错误信息。编辑页复用的是同一个表单页面。进入编辑时先findById查出原数据set到request域再forward编辑页WebServlet(/student/edit) public class StudentEditServlet extends HttpServlet { Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { int id Integer.parseInt(req.getParameter(id)); StudentDao dao new StudentDao(); Student student dao.findById(id); req.setAttribute(student, student); req.getRequestDispatcher(/student_edit.jsp).forward(req, resp); } }对应的JSP表单里用EL表达式回显默认值input typetext namestudentNo value${student.studentNo} readonly input typetext namename value${student.name} input typeradio namegender value男 ${student.gender 男 ? checked : }男 input typeradio namegender value女 ${student.gender 女 ? checked : }女逻辑说明新增和编辑共用一套表单字段区别在于编辑时URL带id参数、表单里有内容预填、studentNo设为readonly防止改学号新增时以上都没有。这种“一份表单两个入口”的设计在实际项目中非常常见理解一次能省不少重复页面。4.4 删除与批量删除ids参数与IN占位符拼接删除操作涉及一个经典细节不要在前端拼SQL也不要在Java里用字符串拼接IN参数。批量删除的稳妥做法WebServlet(/student/delete) public class StudentDeleteServlet extends HttpServlet { Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String idsParam req.getParameter(ids); String[] idArr idsParam.split(,); StringBuilder sql new StringBuilder(DELETE FROM t_student WHERE id IN (); for (int i 0; i idArr.length; i) { sql.append(?,); } sql.setLength(sql.length() - 1); sql.append()); try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql.toString())) { for (int i 0; i idArr.length; i) { ps.setInt(i 1, Integer.parseInt(idArr[i])); } ps.executeUpdate(); } catch (SQLException e) { e.printStackTrace(); } resp.sendRedirect(req.getContextPath() /student/list); } }逻辑说明先把前端传来的逗号分隔ids拆成数组再动态生成IN (?,?,?)形式的占位符最后循环绑定每个id。这套写法有两个好处第一所有值都走PreparedStatement参数绑定不存在SQL注入面第二动态占位符数量由数组长度决定无论选1条还是10条都能适配。参数说明前端列表页勾选删除时用JavaScript把所有选中行id拼成逗号字符串放进隐藏域URL就长这样/student/delete?ids1,2,3。单条删除时ids就一个值删除接口可以复用同一套逻辑。这里选择GET请求是为了让删除操作能通过地址栏直接触发、方便调试在实际生产项目中会改用POST并加权限校验课设答辩时能被问到这一点反而可以展示你对安全边界的认识。5. 五个高频踩坑现场现象、原因、解决5.1 驱动类找不到ClassNotFoundException是jar包位置不对不是代码写错现象Tomcat启动后访问任何页面都报java.lang.ClassNotFoundException: com.mysql.cj.jdbc.Driver控制台堆栈指向DBUtil的静态代码块。原因驱动jar包放在了IDEA的external libraries里或者放在Tomcat的lib目录下唯独没放在项目的WEB-INF/lib里。Tomcat运行项目时用的类加载器是Web应用类加载器它只扫描WEB-INF/classes和WEB-INF/lib外部引用和Tomcat全局lib都不保证在运行时可见。解决确认mysql-connector-java-8.0.x.jar已经物理存在于webapp/WEB-INF/lib/目录下然后在IDEA里CtrlShiftF10重新构建项目部署时勾选“Build Artifacts”。如果是从Maven项目转过来的还要额外检查jar包是否被Maven排除这一步最隐蔽。5.2 中文乱码请求、JSP页面、JDBC三处都要设置UTF-8现象数据库里查出来中文正常但页面上显示???或者页面上输入中文提交后数据库里存的是乱码。原因编码链路断在三处。第一处是HTTP请求体没指定编码Tomcat默认按ISO-8859-1解析POST参数中文直接变成拉丁字符第二处是JSP文件本身的保存编码和pageEncoding不一致第三处是JDBC连接URL里少了characterEncodingutf8数据库连接层用了错误编码做转码。解决三处全量设置。Servlet处理POST请求第一行写req.setCharacterEncoding(UTF-8)JSP在顶部声明% page contentTypetext/html;charsetUTF-8 languagejava %并确保文件另存为UTF-8编码JDBC URL里追加characterEncodingutf8。三处都改完中文问题才会彻底消失。这是需要系统性排查的坑缺一处都会在刷新后的某个瞬间暴露。5.3 表名user撞上MySQL保留字SQL语法报错像玄学现象SQL语句直接写INSERT INTO user VALUES...IDE里能自动补全表名但MySQL执行时报1064 - You have an error in your SQL syntax。原因user是MySQL的保留字系统库自带mysql.user表。虽然建表时能创建但很多SQL上下文里不加反引号就会触发语法冲突报错信息往往指向完全无关的位置特别迷惑人。解决建表时避开保留字用t_user、sys_user这类带前缀的表名。如果已经建了user表每个SQL里都得给表名加反引号user治标不治本。血泪经验就是MySQL建表前先过一遍保留字清单表名统一加前缀永远不赌自己记得住哪些词被保留。5.4 操作几次后数据库拒绝连接too many connections是连接没关现象项目刚启动时一切正常点了几次增删改查后突然控制台报Too many connections重启Tomcat后又正常过一会儿再次报错。原因数据库中max_connections默认是151而代码里Connection没有在finally块中关闭。JSP-Servlet-JDBC这条链路上每次请求都新建一个连接请求结束连接不归还连接数很快被打满。这在大并发下是必然事故本地单用户环境只是晚一点暴露而已。解决DBUtil里的close方法虽然写了但DAO层一定要在finally里调用或者用try-with-resources语法让连接自动关闭。这里也顺便说一句生产环境不要用DriverManager手管连接换Druid或HikariCP连接池连接用完后归还池子而不是直接关闭这才是long-running应用的体面做法。5.5 分页搜索翻页丢失keyword点第2页时搜索条件凭空消失现象在搜索框输入“张”点查询后第一页正常显示张姓学生点击第2页结果变成所有学生。原因分页链接只带了page参数没有带keyword。点击第2页时URL变成/student/list?page2Servlet收到的keyword为nullSQL里的LIKE ?匹配%%就相当于查全部。解决生成分页链接时把当前搜索条件一并带上。JSP里翻页链接写成list?page${i}keyword${keyword}Servlet里把keyword重新set回request域。如果还有班级筛选、性别筛选等额外条件原则都一样——所有查询条件都要在翻页时回显。这是分页查询新手最容易忽略的细节也是最容易被答辩老师指出来的逻辑破绽。6. 进阶优化把这份作业升级成能写进简历的Web项目到这一步功能已经完整了但如果想让这个系统在简历上站得住脚还有四个方向必须做。第一引入Druid连接池替代DriverManager。连接池是JavaWeb面试必问题目项目里直接落地一个配置文件和一段初始化代码比空口背八股有用得多。druid.properties里配好驱动类、URL、初始连接数、最大连接数工具类里用DruidDataSourceFactory创建数据源DAO层从数据源拿连接用完交给池子回收。写完这一段回答“连接池解决了什么问题”就全是实证。第二全部SQL走PreparedStatement预编译。检查从登录到删除的所有DAO方法凡是出现字符串拼接参数的地方全部改成占位符。这一步的意义不在功能而在安全能对着答辩老师讲清楚“什么是SQL注入、为什么 or 11能绕过登录、预编译怎么化解它”就是一份实打实的面试加分回答。第三用JSTLEL替换JSP里的Java脚本片段。列表页里如果到处是% for(...) { %这样的Scriptlet页面观感差且难以维护。引入JSTL核心标签库后用c:forEach遍历集合、c:if做条件判断、${student.name}取字段值页面代码会干净很多这也是从“能跑”走向“像工程”的标志性改动。第四做一个自我验证清单数据库乱码测试、分页边界测试、未登录直接访问列表页测试。我会在演示前走一遍这五条子项确认搜索翻页参数回显、批量删除后页码不越界、Session过期后跳转登录页这三件事都正常了再打开答辩用的浏览器。做这套系统时我最初图省事把SQL直接拼在JSP里页面调用时不报错还好答辩现场老师让我当场加一个“按班级筛选”的条件我改了快三分钟还在翻代码——从那以后我再也不在视图层写业务逻辑所有数据库操作收进DAO、所有流程控制收进Servlet、页面只负责展示。这条教训后来写进了我每一段代码里也借着这套组合完整地教给了每位初次接触JavaWeb的同学希望帮到你。本文还有配套的精品资源点击获取
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

treg 本地代理实战:一段没有 Key 的代码,如何被团队凭据悄悄喂饱 2026/9/25 1:36:41

treg 本地代理实战:一段没有 Key 的代码,如何被团队凭据悄悄喂饱

后端API网关MCP 服务dsh-plugin 【免费下载链接】treg OpenRouter for agent tools. Join community here: https://discord.gg/6mQYYfFMAn 项目地址: https://gitcode.com/GitHub_Trending/treg/treg 点击查看 免费下载 导读:treg 的本地代理&#xff…

阅读更多 →
EndNote X9失效修复指南:许可证、Word插件与配置问题排查 2026/9/25 1:36:41

EndNote X9失效修复指南:许可证、Word插件与配置问题排查

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
3步搞定:codex-claude-academic-skills在Claude Code与Codex双平台安装完全指南 2026/9/25 1:36:41

3步搞定:codex-claude-academic-skills在Claude Code与Codex双平台安装完全指南

3步搞定:codex-claude-academic-skills在Claude Code与Codex双平台安装完全指南 【免费下载链接】codex-claude-academic-skills 本仓库包含三个面向学术科研人员的Skills,覆盖从文献阅读、论文写作到科学计算的完整研究工作流。office-academic-skill 负…

阅读更多 →
PPT Master 模板系统完全指南:Brand、Style、Layout、Deck 四类模板如何复用你的 PPT 设计 2026/9/25 1:36:34

PPT Master 模板系统完全指南:Brand、Style、Layout、Deck 四类模板如何复用你的 PPT 设计

PPT Master 模板系统完全指南:Brand、Style、Layout、Deck 四类模板如何复用你的 PPT 设计 【免费下载链接】ppt-master AI 把任意文档生成真正可编辑的 PowerPoint —— 原生形状与动画、演讲者备注可合成音频旁白、还能参考你自己的 .pptx 模板,而不是一张张图片 …

阅读更多 →
treg 外联内容集群构建计划:从五篇 “Rebuild X“ 草稿到 /workflows 页面的工程化落地 2026/9/25 1:36:28

treg 外联内容集群构建计划:从五篇 “Rebuild X“ 草稿到 /workflows 页面的工程化落地

后端API网关MCP 服务dsh-plugin 【免费下载链接】treg OpenRouter for agent tools. Join community here: https://discord.gg/6mQYYfFMAn 项目地址: https://gitcode.com/GitHub_Trending/treg/treg 点击查看 免费下载 本文是 treg.to 开源仓库中 marketing/rebu…

阅读更多 →
如何快速安装Skill Recorder:macOS/Windows/Ubuntu三平台一行命令安装完整教程 2026/9/25 1:36:28

如何快速安装Skill Recorder:macOS/Windows/Ubuntu三平台一行命令安装完整教程

如何快速安装Skill Recorder:macOS/Windows/Ubuntu三平台一行命令安装完整教程 【免费下载链接】skill-recorder Desktop app that records your on-screen work session and uses the GitHub Copilot CLI to reconstruct it as an intent ordered steps, then bui…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞