简介面向课程设计、实训与毕业设计场景的Java Web学生信息管理系统适合具备Java基础、正在学习Servlet/JSP/JDBC与数据库开发的学生及初级开发者用于实现登录鉴权与信息增删改查。rar压缩包约1.21MB文件总数为0上游未提供主要文件类型明细无法按类型展开说明。已有3007人学习或下载可作为同类课程项目的参考样本。内容围绕登录表单校验、Servlet请求处理、Session身份保存、JSP数据展示与JDBC连接展开涵盖增删改查SQL操作同时涉及学生表字段、主键外键与索引设计PreparedStatement防注入以及MD5/SHA密码存储等安全思路便于读者梳理分层结构、数据库设计与代码组织方式。1. 从开学季改一份名单说起学生信息管理系统到底解决什么问题学生信息管理系统是 Java Web 练手项目里最容易被低估的一类看起来就是几张表的增删改查真跑起来却会卡在登录、分页、中文乱码和权限判断上。假设你现在接到一个需求教务老师发来一份 Excel 名单要求能按学号搜索、按班级筛选、改完马上看到结果还要区分管理员和普通老师能看的数据。这套基于 Java Web 的学生信息管理系统就是把这类需求拆成学生、班级、课程、成绩和用户几条主线用 Servlet/JSP 或 SSM 一类分层结构落地。它适合课程设计、毕业设计起步也适合已经会 Java 基础、想补全 Web 请求链路的人。读这套源码别先盯页面先看一次查询从浏览器到数据库再回来的路径后面改字段、加权限才有下手的地方。2. 技术栈与工程骨架Java Web 学生信息管理系统怎么分层落地2.1 技术选型先看源码包Servlet/JSP 与 SSM/Spring Boot 的对应关系打开源码包先做三个判断有没有web.xml、WEB-INF目录和 JSP 页面lib目录里有没有 Servlet 容器相关的依赖pom.xml里有没有 Spring Boot 或 Spring MVC 的启动依赖。看到 JSP、Servlet 映射和web.xml基本就是传统 Java Web 路线看到mapper接口、application.yml和 Controller 注解基本是 SSM 或 Spring Boot 路线。两条线的分层目标一样页面只管展示Servlet 或 Controller 接参数Service 管业务规则DAO 管 SQL。不要一上来就换框架先把源码包现有链路跑通否则登录页还没通就重构成 Spring Boot血泪经验是越改越找不到错在哪。传统 Servlet/JSP 的请求链路很直白浏览器请求/student/list容器按WebServlet或web.xml找到 ServletServlet 读request.getParameter调 ServiceService 调 DAODAO 用 JDBC 查数据库结果塞进request.setAttribute再forward到 JSP。SSM/Spring Boot 只是把 Servlet 换成DispatcherServlet把WebServlet换成RequestMapping把 JDBC 换成 MyBatis 或 JPA分层边界没有变。学生信息管理系统的核心不是框架名而是学生、班级、课程、成绩之间的关联条件以及分页、搜索、权限这些重复逻辑放在哪一层。这套源码包如果带pom.xml看依赖里有没有mysql-connector、连接池、Servlet API、JSP API如果没有 Maven就看WEB-INF/lib下的 jar。连接池常见做法是 Druid 或 HikariCP老项目也可能直接用DriverManager每次拿连接。DriverManager不是不能跑但并发一上来连接数会不受控二次开发时至少换成连接池。数据库驱动类名和连接串参数要以源码包里的配置为准不要凭记忆改驱动版本不同类名和时区参数会翻车。提示先确认源码包是“传统 Servlet/JSP”还是“Spring Boot 模板/前后端分离”两者的启动方式和改法完全不同别把web.xml项目当 Spring Boot 项目直接java -jar。2.2 目录结构controller、service、dao、entity、jsp 各自管什么学生信息管理系统的目录结构通常不难认难的是改一个字段时不知道要动几层。下面这张表是按常见 Java Web 分层整理的职责清单源码包目录名可能叫servlet也可能叫controller叫bean也可能叫entity但职责对应得上。目录或文件职责改动频率entity/bean学生、班级、课程、成绩、用户实体类字段与表对应加字段时必改dao/mapperSQL、分页查询、按条件统计总数查询条件变化时改service分页参数修正、空值处理、权限判断、事务边界业务规则变化时改servlet/controller接收请求参数、调用 Service、转发 JSP 或返回 JSON接口变化时改WEB-INF/viewsJSP 页面放WEB-INF下防止被浏览器直接访问页面展示变化时改filter/interceptor登录校验、编码过滤、权限拦截权限和编码问题时改util数据库连接、分页对象、字符串工具底层公共逻辑db.properties/application.yml数据库连接、账号密码、连接池参数换环境时改sql建库建表、初始化账号数据首次导入和加表时改目录看清后先找PageResult或PageBean这类分页对象。学生信息列表几乎一定用到分页分页对象里一般有page、size、total、rows、totalPages几个字段。再看 DAO 里有没有count和list两个方法很多分页错乱不是前端页码传错而是总数查询和列表查询用了不同的where条件。Service 层要负责把page从 1 开始修正、把size限制在合理范围、把关键字去空格并截断过长输入这些规则不放在 Service后面加导出和统计时会到处复制。JSP 放在WEB-INF/views下是常见做法浏览器不能直接访问只能由 Servletforward进去。如果源码包把 JSP 放在根目录也能跑但用户可以直接敲地址绕过登录二次开发时建议挪进WEB-INF。实体类字段用驼峰数据库字段用下划线DAO 里做映射如果源码包用 MyBatis检查resultMap或mapUnderscoreToCamelCase配置别加完字段发现页面上永远是空。2.3 数据库表设计学生、班级、课程、成绩、用户五张核心表学生信息管理系统的表不多但关系要清楚。学生属于班级成绩关联学生和课程用户表管登录和角色。下面是一份常见的建表 SQL表名和字段名可以按源码包调整重点是唯一索引、逻辑删除和关联字段。class是 SQL 关键字班级表用class_info更稳。CREATE TABLE sys_user ( id BIGINT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE COMMENT 登录名, password VARCHAR(100) NOT NULL COMMENT 存哈希不要明文, role VARCHAR(20) NOT NULL DEFAULT TEACHER COMMENT ADMIN/TEACHER, status TINYINT NOT NULL DEFAULT 1 COMMENT 1启用 0停用, create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE class_info ( id BIGINT PRIMARY KEY AUTO_INCREMENT, class_name VARCHAR(50) NOT NULL COMMENT 班级名称, grade VARCHAR(20) NOT NULL COMMENT 年级, head_teacher VARCHAR(50) COMMENT 班主任, status TINYINT NOT NULL DEFAULT 1 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE student ( id BIGINT PRIMARY KEY AUTO_INCREMENT, student_no VARCHAR(20) NOT NULL COMMENT 学号, name VARCHAR(50) NOT NULL COMMENT 姓名, gender CHAR(1) NOT NULL DEFAULT U COMMENT M/F/U, class_id BIGINT NOT NULL COMMENT 关联class_info.id, phone VARCHAR(20), email VARCHAR(100), status TINYINT NOT NULL DEFAULT 1 COMMENT 逻辑删除, create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, UNIQUE KEY uk_student_no (student_no), KEY idx_student_class (class_id), KEY idx_student_name (name) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE course ( id BIGINT PRIMARY KEY AUTO_INCREMENT, course_name VARCHAR(80) NOT NULL, credit DECIMAL(3,1) NOT NULL DEFAULT 0, teacher VARCHAR(50), status TINYINT NOT NULL DEFAULT 1 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE score ( id BIGINT PRIMARY KEY AUTO_INCREMENT, student_id BIGINT NOT NULL, course_id BIGINT NOT NULL, score DECIMAL(5,1) NOT NULL DEFAULT 0, term VARCHAR(20) NOT NULL COMMENT 学期, UNIQUE KEY uk_score_student_course_term (student_id, course_id, term), KEY idx_score_course (course_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;这份 SQL 里student_no加了唯一索引改学号重复时数据库会直接报错比在页面上用 JS 校验可靠。class_id加了普通索引按班级筛选学生信息时不会全表扫。score用student_id course_id term做唯一键防止同一学期同一门课重复录入成绩。status是逻辑删除字段学生转班或毕业时不建议物理删除否则成绩记录会断掉。password字段给 100 长度常见做法是存 BCrypt 或加盐哈希老源码包如果明文存密码二次开发第一件事就是换掉。物理外键不是必须的。学生信息管理系统里删除班级、删除课程经常有争议物理外键会让删除顺序很麻烦常见做法是逻辑关联加 Service 层校验删除班级前先查student.class_id是否还有有效学生。索引不要乱加student表按姓名模糊搜索用LIKE %关键字%时普通索引帮不上太多数据量大再考虑全文索引或独立搜索表课程设计阶段先把唯一索引和班级索引加对就够了。2.4 分层边界为什么不要在 JSP 里写 SQLJSP 里写 JDBC 的源码不是不能跑是二次开发会变成黑匣子。页面里混着 HTML、Java 小脚本和 SQL改一个查询条件要翻三个 JSP登录判断也可能散落在每个页面顶部。学生信息管理系统再小也建议守住一条线JSP 只负责把request里的数据渲染成表格和表单最多做必填、长度这类前端校验参数接收放在 Servlet/Controller业务规则放在 ServiceSQL 放在 DAO。分层之后加一个“按班级筛选”的功能才有固定路径JSP 搜索框多一个classId参数Servlet 多读一个getParameterService 把classId传给 DAODAO 在where里拼s.class_id ?最后 JSP 回显选中的班级。每一步都能单独测不用打开浏览器猜是哪一层丢了参数。反过来如果 SQL 写在 JSP 里加字段时容易漏掉分页总数查询页面上就会出现“列表有数据但页码不对”的玄学问题。3. 核心链路拆解一次学生信息查询从 JSP 到数据库怎么走3.1 请求入口Servlet 如何接收关键字、页码和每页条数学生信息列表的入口通常是一个 GET 请求参数包括keyword、page、size可能还有classId。Servlet 的职责不是查数据库而是把 HTTP 参数转成 Service 能理解的值再把结果转发给 JSP。下面这段代码是传统 Servlet 路线的写法Spring Boot 项目可以把doGet换成GetMapping参数读取换成方法参数逻辑一样。WebServlet(/student/list) public class StudentListServlet extends HttpServlet { private final StudentService studentService new StudentService(); Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // 关键字可能为 null先保留原始值Service 层再做 trim 和截断 String keyword req.getParameter(keyword); int page parsePage(req.getParameter(page)); int size parseSize(req.getParameter(size)); PageResultStudent pageResult studentService.page(keyword, page, size); req.setAttribute(pageResult, pageResult); req.setAttribute(keyword, keyword null ? : keyword.trim()); // 转发到 WEB-INF 下的 JSP浏览器不能直接访问该 JSP req.getRequestDispatcher(/WEB-INF/views/student/list.jsp).forward(req, resp); } private int parsePage(String raw) { try { return Math.max(Integer.parseInt(raw), 1); } catch (Exception e) { return 1; // 页码非法时回到第一页不要让 500 暴露给用户 } } private int parseSize(String raw) { try { int size Integer.parseInt(raw); return Math.min(Math.max(size, 5), 50); // 限制每页条数防止一次拖太多数据 } catch (Exception e) { return 10; } } }这段代码的关键在参数兜底。page从 1 开始用户传0、-1、空串或非数字时都回第一页size限制在 5 到 50避免有人把size100000拼到地址栏把数据库拖慢。keyword不在 Servlet 里做复杂处理只把原始值交给 Service是因为搜索规则以后可能加“按学号精确匹配”或“按姓名模糊匹配”规则收在一层更好改。forward的路径以/开头表示从当前应用根路径开始不要写成相对路径否则换个部署目录就 404。如果源码包用 Spring Boot对应写法是RequestParam(defaultValue 1) Integer page但参数兜底不能省RequestParam只解决缺省值不解决page0和size9999。学生信息查询这种列表接口参数校验放在 Controller 或 Service 都行关键是别让非法值直接进 SQL 的LIMIT。3.2 实体与 DAO分页查询和总数统计怎么写DAO 层要解决两个查询符合条件的总条数以及当前页的数据。两个查询必须共用同一套where条件否则会出现“总数 30 条、列表只查出 2 条”的错位。下面用 JDBC 写法示意MyBatis 项目可以把 SQL 搬到 XML 或注解里逻辑不变。public class StudentDao { public PageResultStudent page(String keyword, int page, int size) { String kw keyword null ? : keyword.trim(); String like % kw %; // 条件只写一次count 和 list 复用避免总数与列表不一致 String where WHERE s.status 1 AND (s.student_no LIKE ? OR s.name LIKE ?) ; Object[] params new Object[]{like, like}; long total queryCount(SELECT COUNT(*) FROM student s where, params); int offset (page - 1) * size; String listSql SELECT s.id, s.student_no, s.name, s.gender, s.phone, c.class_name FROM student s LEFT JOIN class_info c ON c.id s.class_id where ORDER BY s.id DESC LIMIT ? OFFSET ?; ListStudent rows queryList(listSql, like, like, size, offset); return new PageResult(rows, page, size, total); } }where里用s.status 1过滤逻辑删除的学生LIKE ?的占位符参数是%关键字%不要用字符串拼接否则关键字里带单引号会直接报错也容易留下注入风险。LIMIT ? OFFSET ?是 MySQL 常见写法参数顺序是“每页条数、偏移量”偏移量等于(page - 1) * size。LEFT JOIN class_info是为了列表上直接显示班级名如果班级被逻辑删除class_name可能为空JSP 里要做空值展示。COUNT(*)查询不要带LEFT JOIN除非确实要按班级表字段过滤多一个 join 会变慢而且班级表一对多时可能把总数放大。排序用s.id DESC比按姓名排序稳定分页时不会因为同名同姓导致某条记录跨页重复。深分页在课程设计数据量下不是问题二次开发遇到几万条学生信息时再考虑用“上一页最后 id”做游标不要一上来就上复杂方案。3.3 服务层把分页参数、空值和权限判断收口Service 层是学生信息管理系统里最容易被写薄的一层。很多源码包 Servlet 直接调 DAOService 只做透传后面加“班主任只能看自己班”时就到处改。下面这段 Service 把页码修正、条数限制、关键字截断和空结果处理收在一起。public class StudentService { private final StudentDao studentDao new StudentDao(); public PageResultStudent page(String keyword, int page, int size) { page Math.max(page, 1); size Math.min(Math.max(size, 5), 50); if (keyword ! null) { keyword keyword.trim(); if (keyword.length() 30) { keyword keyword.substring(0, 30); // 防止超长关键字拖慢模糊查询 } } long total studentDao.count(keyword); if (total 0) { return PageResult.empty(page, size); } int totalPages (int) Math.ceil(total * 1.0 / size); if (page totalPages) { page totalPages; // 删除最后一页数据后页码自动回退避免空白页 } return studentDao.page(keyword, page, size); } }这里的顺序很重要先修正page和size再截断关键字再查总数最后根据总数修正页码。用户在第 3 页删掉最后一条学生信息后第 3 页可能已经不存在Service 把page拉回最后一页页面不会显示空表格。PageResult.empty要带上正确的page和size否则 JSP 分页条会算出负数页。权限判断也适合放在 Service 或更上面的拦截层。比如TEACHER角色只能看自己班级Service 接收当前登录用户和角色DAO 的where追加class_id ?。不要把角色判断只写在 JSP 的c:if里前端隐藏按钮挡不住直接敲 URL 的人后端必须再查一次。3.4 JSP 渲染表格、搜索框和分页条怎么接后端数据JSP 只做渲染但渲染也有坑搜索关键字要转义分页链接要带上下文路径每页条数要回传。下面是一段简化的列表页写法使用 JSTL 和 EL源码包如果用小脚本建议逐步换掉。form action${pageContext.request.contextPath}/student/list methodget input typetext namekeyword value${fn:escapeXml(keyword)} placeholder学号或姓名/ input typehidden namesize value${pageResult.size}/ button typesubmit搜索/button /form table c:forEach items${pageResult.rows} vars tr td${fn:escapeXml(s.studentNo)}/td td${fn:escapeXml(s.name)}/td td${fn:escapeXml(s.className)}/td td${fn:escapeXml(s.phone)}/td /tr /c:forEach /table c:if test${pageResult.totalPages 1} c:forEach begin1 end${pageResult.totalPages} varp a href${pageContext.request.contextPath}/student/list?page${p}size${pageResult.size}keyword${fn:escapeXml(keyword)} ${p} /a /c:forEach /c:iffn:escapeXml用来转义姓名、学号里的特殊字符防止页面被注入脚本pageContext.request.contextPath保证应用部署在非根路径时链接仍然正确。搜索表单里用隐藏域回传size否则用户搜一次每页条数就回到默认值。分页链接里的keyword也要带上不然翻到第二页搜索条件就丢了这是学生信息列表最常见的“翻页后数据变全量”问题。表格里的className来自 DAO 的联表查询实体类如果没有这个字段要么在实体里加一个展示字段要么用Map接收。不要为了省事在 JSP 里根据classId再查一次数据库一个页面循环查十几次数据一多就慢得明显。分页条只显示页码在课程设计里够用二次开发可以加“上一页/下一页”和当前页高亮但链接参数要复用同一套拼装逻辑别在 JSP 里复制粘贴几十行。4. 跑起来与改配置数据库、连接池、登录账号和分页参数4.1 环境准备JDK、容器、数据库和构建工具怎么对齐先把源码包要求的运行环境对齐不要急着改代码。看pom.xml里的maven.compiler.source或项目属性里的 JDK 设置看WEB-INF/lib下有没有 Servlet API、JSP API、数据库驱动看README或sql目录里有没有建库脚本。学生信息管理系统常见组合是 JDK Servlet 容器 MySQL源码包如果带 Maven就用 Maven 拉依赖如果是普通 Web 项目就把 jar 放对位置。java -version mvn -v mysql --version这三条命令只是确认工具在 PATH 里输出版本以源码包要求为准。JDK 大版本不要凭感觉升级老项目用低版本编译换高版本可能因为模块化或反射限制启动失败。mvn -v如果报找不到命令说明构建工具没装或没配环境变量先解决这个不要手动一个个下 jar。mysql --version确认客户端能连数据库后面导入 SQL 要用。Servlet 容器的版本也要和web.xml的version对得上。web.xml里声明的是较新的 Servlet 规范容器太老会不认注解或 JSP 标签。源码包如果自带容器配置比如context.xml或部署描述符先按原样跑别一上来改成自己的配置。提示环境准备阶段只做“能启动”这一件事先不要改包名、不要换数据库、不要升级框架变量一次只动一个。4.2 导入 SQL 与修改连接配置先让登录页通数据库是学生信息管理系统的第一道门槛。先建库再导入表结构和初始化数据最后改连接配置。下面命令里的库名和路径要换成源码包sql目录里的实际值不要照抄。mysql -u root -p CREATE DATABASE student_mgr DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE student_mgr; SOURCE /path/to/schema.sql; SELECT id, username, role, status FROM sys_user;utf8mb4是为了存中文姓名和特殊字符SOURCE后面跟 SQL 文件绝对路径路径里有空格要加引号。导入完先查sys_user表确认初始化账号存在再看student、class_info有没有数据。如果SOURCE报错先看 SQL 文件里有没有CREATE DATABASE和USE重复执行可能撞表必要时删库重来但别在已有数据的库上直接重跑。连接配置常见是一个db.properties或application.yml。下面用 properties 示意字段名以源码包为准。jdbc.drivercom.mysql.cj.jdbc.Driver jdbc.urljdbc:mysql://127.0.0.1:3306/student_mgr?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/ShanghaiuseSSLfalse jdbc.usernameroot jdbc.passwordchange_mejdbc.driver的类名和数据库驱动版本有关老驱动可能是com.mysql.jdbc.Driver新驱动是com.mysql.cj.jdbc.Driver以lib或pom.xml里的驱动为准。characterEncodingutf8配合数据库utf8mb4能解决大部分中文乱码serverTimezone不配在某些驱动下会报时区错误。useSSLfalse只是本地开发省事生产环境不要照搬。密码不要提交到公开仓库源码包里如果是明文密码改成环境变量或本地配置文件。4.3 启动方式IDE 部署与 Maven 打包两种路径传统 Java Web 项目在 IDE 里通常配一个 Servlet 容器把项目作为 Web 应用部署启动后访问http://127.0.0.1:端口/应用名/。Maven 项目可以打包成 war再放进容器部署。mvn clean package -DskipTestsclean清掉旧产物package打包-DskipTests在第一次跑通时跳过测试避免测试环境没配好挡住启动。打包后看target目录下有没有 war名字通常是artifactId-version.war。把 war 放进容器部署目录启动容器看日志里有没有Server startup一类成功信息。Spring Boot 项目则可能是mvn spring-boot:run或java -jar以pom.xml的打包方式为准。启动失败先看控制台最下面的Caused by不要只看第一行。学生信息管理系统常见的启动失败是数据库连不上、驱动类找不到、web.xml解析失败、端口被占用。数据库连不上就回去查 4.2 的连接串和账号驱动类找不到就看WEB-INF/lib或pom.xml依赖端口占用就换端口或停掉占用进程。第一次启动成功后再访问登录页别直接跳到学生列表登录过滤器可能把你拦回登录页。4.4 登录账号、角色和分页参数第一次改哪里不容易翻车登录页通了之后先用sys_user表里的初始化账号登录。密码如果是哈希不能直接在数据库里改明文要用项目里的加密工具生成如果是明文先登录成功再改密码策略。角色字段常见是ADMIN和TEACHER登录成功后把用户对象放进session过滤器从session取不要每个 Servlet 重新查一次用户。分页参数第一次改建议只改默认每页条数位置通常在 Service 的size默认值或 JSP 的隐藏域。默认 10 条适合课程设计改成 20 条要同步改size上限别只改前端不改后端。搜索关键字长度限制也在这层30 个字符对学号和姓名够用改大之前先想清楚数据库模糊查询的成本。登录后立刻失效是常见问题检查session.setAttribute的 key 和过滤器getAttribute的 key 是否一致检查过滤器有没有把登录页和静态资源也拦了。角色越权则要看后端有没有再判断只在 JSP 里隐藏“删除”按钮不算权限控制。第一次改配置不要同时动数据库、连接池和权限改完一处就重启验证一次出问题才知道是哪一步引入的。5. 避坑与排查学生信息管理系统最容易翻车的 5 个点5.1 启动与环境类问题404、500、端口占用现象浏览器访问登录页返回 404控制台没有明显异常。原因多半是访问路径少了应用上下文或者 Servlet 映射路径和表单action不一致。传统 Java Web 部署后地址通常是http://127.0.0.1:端口/应用名/login不是http://127.0.0.1:端口/login。解决看容器启动日志里的 context pathJSP 里统一用${pageContext.request.contextPath}拼链接检查WebServlet(/login)或web.xml的url-pattern是否和表单action对得上。JSP 放在WEB-INF下时不能直接敲地址访问必须走 Servlet 转发。现象启动报 500堆栈很长。原因常见是数据库连接失败、驱动类找不到、db.properties没被加载。解决看堆栈最底部的Caused by如果是ClassNotFoundException就查驱动 jar如果是Access denied就查账号密码如果是时区错误就在连接串补serverTimezone。不要一上来改代码先把异常第一行读明白。现象端口被占用容器起不来。原因上一次启动的进程没退干净或者本机其他服务占了同一个端口。解决换一个端口或者找到占用进程停掉。换端口后记得同步改访问地址别在旧地址上反复刷新。5.2 数据与权限类问题中文乱码、登录失效、越权访问现象学生姓名在页面显示成问号或乱码。原因请求编码、JSP 页面编码、数据库字符集三处不一致。解决在读取参数前设置request.setCharacterEncoding(UTF-8)用过滤器统一做JSP 顶部pageEncodingUTF-8数据库和表用utf8mb4连接串带characterEncodingutf8。注意setCharacterEncoding要在第一次getParameter之前调用顺序错了不生效。现象登录成功后又跳回登录页或者刷新一下会话就没了。原因session属性名不一致过滤器判断的 key 和登录 Servlet 存的 key 不是同一个也可能是过滤器把静态资源或登录请求也拦了。解决把 session key 抽成常量登录、过滤器、JSP 统一引用过滤器里放行登录页、登录接口、CSS/JS/图片。检查session.setAttribute(loginUser, user)和getAttribute(loginUser)是否完全一致。现象普通老师能直接访问管理员页面。原因角色判断只写在 JSP 的按钮显示上后端接口没有校验。解决在过滤器或拦截器里按 URL 前缀做角色校验比如/admin/*只允许ADMINService 层再按数据范围校验一次比如老师只能操作自己班级的学生。前端隐藏按钮只是体验不是安全边界。5.3 页面与分页类问题表格空、页码错乱、导出失败现象搜索后列表有数据但总数显示 0或者页码只有一页。原因count查询和list查询的where条件不一致常见是count忘了加status 1或忘了加班级条件。解决把where和参数抽成同一个方法或同一个字符串常量两个查询复用改完条件后同时看总数和列表。不要只测第一页翻到最后一页看总数是否对得上。现象删除最后一页的最后一条数据后页面空白。原因页码还停留在已经不存在的页。解决Service 查完总数后计算totalPages如果当前页大于总页数就把page修正到最后一页总数为 0 时返回空分页对象JSP 显示“暂无数据”。这个修正放在 Service不要指望每个 JSP 自己判断。现象翻页后搜索条件丢失或者每页条数变回默认值。原因分页链接只带了page没带keyword和size。解决分页链接统一拼上搜索条件和每页条数最好用c:url生成参数自动编码。导出功能也同理导出时要带当前搜索条件不要导全表导出失败先看响应头是不是被 JSP 空白行污染% page %前面不要留空行。6. 二次开发从能跑到能改的三个改造方向6.1 加字段学生表新增一列怎么贯穿前后端加字段是学生信息管理系统最常见的二次开发。以“紧急联系人”为例先在数据库加列再改实体、DAO、Servlet 和 JSP少一步页面上就看不到。数据库变更用ALTER TABLE位置放在phone后面字段允许为空避免历史数据导入失败。ALTER TABLE student ADD COLUMN emergency_contact VARCHAR(50) NULL COMMENT 紧急联系人 AFTER phone;加完列后实体类Student加emergencyContact字段和 getter/setterDAO 的SELECT列表加上s.emergency_contact插入和更新 SQL 加上对应占位符Servlet 的保存方法多读一个getParameter(emergencyContact)JSP 表单加输入框列表按需展示。MyBatis 项目要检查resultMap或驼峰映射JPA 项目要检查实体注解。改完先新增一条学生信息再编辑一次最后看列表和详情三处都通过才算完。6.2 换权限从单角色到班主任/管理员分权单角色项目里sys_user.role只有一个值改成管理员和班主任分权关键在后端数据范围。管理员看全部学生班主任只看自己班级。可以在class_info表加head_teacher_id关联用户登录后把用户 id 和角色放进 sessionService 查询时根据角色追加class_id条件。过滤器只做“能不能进这个 URL”Service 做“能看哪些数据”两层都要有。6.3 做统计班级人数和成绩分布怎么查统计功能不要在前端循环列表算直接让数据库聚合。班级人数用LEFT JOIN保留没有学生的班级成绩分布按分数段CASE WHEN分组。下面这条 SQL 查每个班的有效学生人数status 1放在 join 条件里避免把逻辑删除的学生算进去。SELECT c.id, c.class_name, COUNT(s.id) AS student_count FROM class_info c LEFT JOIN student s ON s.class_id c.id AND s.status 1 GROUP BY c.id, c.class_name ORDER BY student_count DESC;统计 SQL 先确认索引student.class_id有索引按班级聚合不会太慢。成绩分布要注意分数为空或课程被停用的情况统计口径先和业务确认别页面做完了才发现“缺考”和“0 分”被算成一类。改完统计后拿一条已知数据手算对一遍再和列表页总数交叉验证。从那以后我每次接手这类学生信息管理系统都先跑通登录再改一条学生信息最后才动表结构顺序反了后面每个报错都像在黑匣子里猜。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?