简介:面向Java Web课程设计场景,这套学生信息管理系统项目包整合了源码、数据库脚本与说明文档,适合高校学生作为课程设计或项目实战参考。内容覆盖功能结构、项目架构、包及Java类说明、数据库设计等核心资料,功能上完整实现登录、系统主界面、添加学生信息、学生信息维护、按学号查找、更新与删除学生信息、更改管理员密码以及退出系统等业务流程,能够直观呈现基于Servlet、JSP与数据库的分层实现方式。包体共78个文件,以Java源码、JSP页面、JAR依赖、JS/CSS前端资源及SQL脚本为主,另含docx格式的设计说明文档,整体压缩包约2.53MB,目录结构规整,便于导入Eclipse或MyEclipse等Java Web开发环境阅读与调试。Java类与JSP页面分层明确,配合SQL初始化脚本,可清晰看到业务逻辑与数据持久化过程。目前已有922人学习,适合在课程设计或毕业设计中借鉴,按文档说明完成部署后可快速掌握学生表、管理员表设计及JDBC交互逻辑,并基于现有功能做进一步扩展。
1. 学生信息管理系统到底在做什么:一个课程设计如何变成可运行的Web项目
临近毕业季或者学期末,很多计算机专业的同学都会拿到一个类似标题的任务——基于Java web的学生信息管理系统设计,压缩包里装着源码、数据库脚本和一份说明文档。这个项目听起来像是教科书里的经典例子,但真正动手把它跑起来,你会发现它不是一个“看懂了”就行的理论作业,而是一整套需要你亲手验证的Web开发链路:前端页面怎么和后端交互,请求怎么被Servlet或Controller接住,数据怎么落到MySQL里再查出来渲染回页面。这套系统的核心价值在于,它强制你走完一个Web应用的完整生命周期:设计表结构、写增删改查、处理登录状态、解决中文乱码、最后部署到Tomcat。适合的人群很明确:正在做Java web课程设计的学生、需要快速复现一个可演示系统的从业者,以及那些想从“照着抄代码”过渡到“能解释清每一行在干什么”的初级开发者。这篇笔记会把标题里的三样东西——源码、数据库、说明文档——分别拆开,告诉你每一步怎么做、参数怎么设,以及哪些地方最容易被忽略。
2. 先想清楚再动手:学生信息管理系统的技术选型与表结构设计
2.1 为什么这个题目用SSH或SSM都行,但建议优先选Servlet+JSP的纯Java web方案
打开压缩包,你大概率会看到两种风格的项目结构:一种是老派的Servlet + JSP + JDBC,另一种是Spring + Spring MVC + MyBatis(SSM)。很多人一上来就纠结要不要用框架,担心不用框架显得技术落后。但课程设计的本质是“把课堂上学的东西串起来”,而不是比拼框架数量。Servlet + JSP这套组合能让你亲手写出请求从浏览器到数据库再回来的完整过程,中间没有黑匣子——你配置了web.xml里的映射,写一个继承HttpServlet的类,重写doGet和doPost,在里面用JDBC的Connection、PreparedStatement执行SQL,最后把结果setAttribute到request里转发给JSP渲染。这个过程你全部能看见,出错了也知道去哪里查。
相比SSM里Spring容器帮你管理对象、MyBatis帮你映射结果集,纯Servlet方案的关键路径短得多。如果你的说明文档里写的是“基于Java web”,而不是“基于SSM框架”,那么评审老师对你的预期也是前者。我建议你优先把Servlet + JSP + JDBC这套跑通,如果后续时间充裕,再往上面套框架做对比,这样课程设计的“设计”二字才站得住,因为你真能说出为什么这样选:Servlet负责控制流程,JSP负责展示,JDBC负责数据访问,各司其职,完全可以覆盖一个学生管理系统所有业务。
2.2 学生信息表至少要有这几张:学生表、用户表、成绩表,关联关系别乱设计
数据库是这套系统的地基。你看到的“数据库”字段,通常是一个SQL文件,里面包含了建库和建表语句。常见的库名是student_manager或sems。我见过不少翻车案例,都是因为表结构设计得过于简单或者过于复杂:过于简单的是只有一张student表,用户登录名和密码全塞在里面;过于复杂的是给每张表都加了七八个外键,插入一条数据要层层校验,课堂演示时反而频繁报错。
一个稳妥的设计是拆成三张核心表。用户表(t_user)存登录账号、密码和角色,角色分为管理员和普通用户,密码用MD5加密而非明文;学生表(t_student)存学号、姓名、性别、班级、出生日期、联系电话;成绩表(t_score)存学号、课程名、分数,并通过学号关联到学生表。这张成绩表可以做成不做外键约束,只在业务层用JOIN查询,这样既演示了多表查询,又避免了外键约束带来的插入顺序问题。学生表和成绩表之间用学号关联,这个学号用varchar而不是int,因为学号可能包含前导零,比如“20230001”,int类型一存就直接丢零。
2.3 从zip解压开始:源码目录结构怎么读,入口类在哪里
解压后先别急着用IDE打开。先看目录,理想结构是这样的:
student-management/ ├── src/ │ ├── com/example/dao/ # JDBC操作层 │ ├── com/example/model/ # 实体类(Student、User、Score) │ ├── com/example/servlet/ # Controller层 │ └── com/example/util/ # 数据库连接工具类 ├── web/ │ ├── jsp/ # 页面文件 │ ├── WEB-INF/ │ │ ├── web.xml │ │ └── lib/ # jar包,比如mysql-connector-java │ └── index.jsp └── sql/ └── student_manager.sql看到这样的结构,你首先要定位三个东西:数据库连接工具类(通常在util包下)、登录相关的Servlet、以及web.xml里的欢迎页配置。这三个点是排查问题时最先要检查的地方。如果你的压缩包里没有lib目录,而是用Maven的pom.xml管理依赖,那你需要联网下载依赖包;如果lib目录里有jar包,优先用lib里的,版本和你本机MySQL要能匹配上,比如MySQL 5.7对应mysql-connector-java 5.1.x,MySQL 8.0对应8.0.x系列驱动。
2.4 数据库脚本里常见的坑:导出时的编码和自增主键
SQL文件打开后,第一行通常是SET NAMES utf8mb4或SET FOREIGN_KEY_CHECKS=0。前者的作用是让客户端和服务器之间的通信使用UTF-8编码,后者是屏蔽外键检查。如果你发现导入后中文全部变成问号,先检查这个SQL文件的编码是不是UTF-8,用Notepad++或VS Code打开右下角看编码,若不是UTF-8,另存为UTF-8后再导入。自增主键这个点也比较关键:导入数据后,如果你自己测试插入一条新学生记录,发现id不是从最大值继续,而是从1重新开始,这是因为导出的SQL里写死了INSERT语句,使用AUTO_INCREMENT=1建表。解决方法是导入后执行:
ALTER TABLE t_student AUTO_INCREMENT = (SELECT MAX(id) FROM t_student) + 1;这段SQL的作用是把自增起点重新对齐到表内最大id加1,避免新增记录主键冲突。注意这里如果表里没有数据,子查询返回NULL,那整个表达式会变成NULL,所以稳妥做法是先查一下最大id再手动改。
3. 把源码跑起来的完整步骤:环境配置、数据库导入与IDE部署
3.1 版本组合怎么选:JDK8、Tomcat8.5、MySQL5.7是最不折腾的搭配
先说版本匹配问题,这是环境搭建的第一步,也是最容易踩坑的地方。JDK版本太高,老项目的javax.servlet包可能被Jakarta命名空间替代,编译直接报错;Tomcat版本太高,可能不再支持旧的Servlet规范;MySQL版本太高,JDBC驱动连接时会有SSL和时区问题。常见的稳妥组合是JDK 1.8、Tomcat 8.5.x、MySQL 5.7。这三者配合了几乎所有的课程设计项目,原因在于:Java web课程一般在大二大三开设,教材和实验环境基本都是这个配置,你的源码包也大概率是按这个版本写的。
如果你本机装的是JDK 11或更高版本,打开项目后可能遇到无法解析javax.servlet的问题。这不是代码错,而是Tomcat 8.5自带的servlet-api.jar没有在你的项目构建路径里。解决方案是在IDE里把Tomcat关联到项目,让servlet-api.jar作为库引入。在IntelliJ IDEA里是File -> Project Structure -> Libraries -> 加号 -> Java -> 选择Tomcat目录下的lib/servlet-api.jar。如果你用的Eclipse,右键项目 -> Build Path -> Add Library -> Server Runtime -> 选中Tomcat。这样做会在编译期把Servlet相关类提供给你,运行期由Tomcat自己管理。
3.2 导入数据库:先建库再导表,命令行和Navicat两种方式
数据库导入看起来简单,但很多人直接在Navicat里双击SQL文件,结果一堆报错。现象多数是“Table already exists”或“Unknown database”。标准顺序是先在MySQL里创建一个空库,再在这个库里执行SQL文件。用命令行操作的话,步骤是:
mysql -uroot -p123456 -e "CREATE DATABASE student_manager DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;" mysql -uroot -p123456 student_manager < sql/student_manager.sql第一行命令创建数据库,指定默认字符集utf8mb4,排序规则utf8mb4_general_ci。注意utf8mb4能完整支持中文和Emoji,比utf8更稳。第二行导入SQL脚本,使用重定向符号<,把文件内容输入到mysql客户端。如果你用Navicat,操作路径是右键连接 -> 运行SQL文件 -> 选择文件,然后在弹出的对话框里选对目标数据库。导入完成后,用下面这条命令验证核心表是否建好:
USE student_manager; SHOW TABLES;3.3 改三个配置文件:数据库连接、Tomcat端口和项目访问路径
源码跑不起来,绝大多数问题出在配置没改。第一个要改的是数据库连接工具类,通常在src/com/example/util/DBUtil.java里,你会看到类似这样的代码:
private static final String URL = "jdbc:mysql://localhost:3306/student_manager?useSSL=false&characterEncoding=utf8"; private static final String USER = "root"; private static final String PASSWORD = "123456";这里的URL要关注的参数有三个:端口(默认3306)、库名(student_manager)、useSSL和characterEncoding。useSSL=false是告诉驱动不要用SSL加密连接,MySQL 8.0驱动默认会对本地连接启用SSL校验,不关掉会报SSLHandshakeException。characterEncoding=utf8是让驱动从数据库取出数据时按UTF-8解码,否则页面显示问号。USER和PASSWORD改成本机MySQL的用户名密码,注意必须是这个用户有权限访问student_manager库。
第二个要改的是web.xml里的Servlet映射。旧项目里经常会写:
<servlet-mapping> <servlet-name>LoginServlet</servlet-name> <url-pattern>/login</url-pattern> </servlet-mapping>这段配置的意思是访问http://localhost:8080/项目名/login时,由LoginServlet处理。如果你的项目没有配置项目名,直接用根路径访问,还需要去确认Tomcat的conf/server.xml或IDE里Deployment的Application context是不是/。在IDEA里,Run Configuration -> Deployment -> Application context如果是空或/,访问路径就是http://localhost:8080/login;如果填了/student-management,路径就是http://localhost:8080/student-management/login。
第三个要改的是如果你的项目用了C3P0或Druid连接池,那么配置会在resources目录下的c3p0-config.xml或druid.properties里,字段名大同小异,同样需要同步用户名密码。这里提醒一点,连接池的initialPoolSize、minPoolSize这些参数不要为了追求性能改大,课程设计场景下保持默认即可,调大反而可能因为连接释放不及时被MySQL拒绝。
3.4 部署到Tomcat:从IDE运行到war包,两种方式都要会
开发时直接在IDE里配置Tomcat运行即可,但课程设计验收时老师可能让你打包成war部署。两种方式有细微差别。IDE方式:在IDEA里选择Run -> Edit Configurations -> 加号 -> Tomcat Server -> Local,在Deployment选项卡里点加号Artifact,选择war exploded或war包。war exploded是展开的目录,开发调试方便;war是压缩后的文件,部署更接近生产。第二种方式:用Maven的package命令打war:
mvn clean package打包完成后war文件在target目录下,把war复制到Tomcat的webapps目录,启动Tomcat,它会自动解压。用这种方式要注意,Tomcat解压后的目录名就是项目名,访问路径要带这个目录名。如果之前配置过Application context,两个路径要对应上。有个加速小技巧:如果是熟悉的项目,可以把war包改成ROOT.war再扔进webapps,Tomcat会把该项目挂到根路径,访问时就不需要带项目名了。
4. 核心功能从结构到代码:登录、增删改查、分页、权限四件事
4.1 登录模块:表单提交到Servlet再到数据库校验,一条链路走完
登录是整个系统的门面,也是第一个演示功能。它的流程是:用户在login.jsp输入用户名密码,点击提交,表单以POST方式发到/login这个url-pattern,被LoginServlet的doPost方法接收。Servlet拿到参数后,调用UserDao里的queryUser方法,用JDBC查t_user表,比对用户名和密码。如果匹配,就把user对象setAttribute到session里,然后重定向到主页面;不匹配就返回登录页并携带错误提示。核心代码长这样:
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String username = request.getParameter("username"); String password = MD5Util.md5(request.getParameter("password")); UserDao dao = new UserDao(); User user = dao.findByUsernameAndPassword(username, password); if (user != null) { request.getSession().setAttribute("user", user); response.sendRedirect("index.jsp"); } else { request.setAttribute("errorMsg", "用户名或密码错误"); request.getRequestDispatcher("login.jsp").forward(request, response); } }这段代码的核心逻辑在于三个点:密码存的是MD5值,传入的明文密码要先经过MD5Util.md5()进行一次哈希再比对,数据库里不能存明文;查询成功后的状态保存在Session里,后续页面通过判断session里有没有user来决定是否跳转登录页;失败时的处理是forward而非redirect,这样request域里的errorMsg才能传到login.jsp页面上显示。注意这里的resetValue,如果你登录失败后返回页面用el表达式${errorMsg}取值,要确保JSP顶部导入了JSTL标签库,否则表达式不生效。
4.2 增删改查:PreparedStatement防注入,变更操作后要redirect
学生信息的管理是系统的核心操作,包括新增、修改、删除和列表查询。这个模块的代码结构非常固定,CRUD是面试官和评审老师最常看的地方。先看新增和更新的DAO层写法:
public boolean addStudent(Student student) throws SQLException { String sql = "INSERT INTO t_student (student_no, name, gender, class_name, birth_date, phone) " + "VALUES (?, ?, ?, ?, ?, ?)"; try (Connection conn = DBUtil.getConnection(); PreparedStatement ps = conn.prepareStatement(sql)) { ps.setString(1, student.getStudentNo()); ps.setString(2, student.getName()); ps.setString(3, student.getGender()); ps.setString(4, student.getClassName()); ps.setDate(5, new java.sql.Date(student.getBirthDate().getTime())); ps.setString(6, student.getPhone()); return ps.executeUpdate() > 0; } }这里有一个必须养成的习惯:永远使用PreparedStatement而不是Statement拼接SQL,因为字符串拼接是SQL注入的温床,课程设计里可能没人攻击你,但老师会看你的代码规范。setDate(5, ...)这行的参数,如果你的实体类birthDate是java.util.Date类型,需要转成java.sql.Date才能set进去,这是新人最容易编译报错的地方。查询列表时通常还会带一个keyword参数做模糊搜索:
SELECT * FROM t_student WHERE name LIKE ?对应Java里要用ps.setString(1, "%" + keyword + "%"),注意百分号是写在参数里而不是SQL里,否则SQL语义不变但可读性差。执行完增删改操作后,Servlet端应该用response.sendRedirect("listStudentServlet")跳回列表页,而不是直接返回JSP。原因是如果直接转发,浏览器地址栏还是处理表单的URL,用户按F5刷新会再次提交上一次的数据,造成重复插入。
4.3 分页查询:page和limit两个参数怎么算出来,总页数如何不丢最后一页
数据一多,分页就来了。分页的SQL是LIMIT offset, size,其中offset = (page - 1) * size,size是每页显示的条数。页面请求会带一个page参数,Servlet接住后传给DAO。在DAO层你需要先count一下总记录数,再查当前页数据。核心代码:
public List<Student> findStudents(int page, int pageSize) throws SQLException { String sql = "SELECT * FROM t_student LIMIT ?, ?"; int offset = (page - 1) * pageSize; ... ps.setInt(1, offset); ps.setInt(2, pageSize); } public int countStudents() throws SQLException { String sql = "SELECT COUNT(*) FROM t_student"; ... }分页最容易出问题的是总页数计算。totalPages = totalCount % pageSize == 0 ? totalCount / pageSize : totalCount / pageSize + 1。很多同学直接写totalCount / pageSize,当总数恰好整除时没问题,但总数为21、pageSize为10时,算出来是2页,而实际需要3页,最后一页的数据就被吞了。注意LIMIT的参数是offset和size,不是page和pageSize,千万别直接把page传给数据库。页码按钮在JSP里生成时,还要处理“上一页”和“下一页”的边界,第一页禁用上一页,最后一页禁用下一页,这个逻辑一般写在JSP的c:if标签里。还有一个细节:当删除操作导致某一页数据变空时(比如最后一页只有一条,删完之后当前页越界),Servlet端要做一次page调整,判断当前页是否大于总页数,若是则把page赋值为totalPages。
4.4 权限控制:用过滤器拦一下,admin能删、普通用户只能看
学生管理系统的权限通常分两级:管理员和学生。管理员可以增删改,普通用户只能查询。实现方式有两种:一种是在每个Servlet里判断session.getAttribute("user")的角色再决定是否放行;另一种是写一个Filter统一拦截。过滤器是更专业的实现,也更方便你写进说明文档里。代码逻辑:
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest req = (HttpServletRequest) request; HttpSession session = req.getSession(); User user = (User) session.getAttribute("user"); String uri = req.getRequestURI(); if (user == null) { response.sendRedirect("login.jsp"); } else if (uri.endsWith("deleteStudentServlet") && !"admin".equals(user.getRole())) { response.sendError(403); } else { chain.doFilter(request, response); } }这个Filter要写在web.xml里声明,把url-pattern设为/*,也就是拦截所有请求。实现有三个要点:第一,用户未登录时直接重定向登录页,不要走后续链条;第二,对敏感的删除、添加操作检查角色,管理员才放行;第三,注意放开login.jsp和静态资源(css、js、图片)的访问,否则登录页的样式全丢。在web.xml里通过init-param配置需要放行的路径,比在Filter代码里写死更灵活。用过滤器有个坑:如果你的JSP还引用了外部图片或CSS,而这些路径也被/*拦截,session里没有用户就会跳登录页,导致页面样式错乱。需要在Filter里判断uri是否以.css、.js、.png等结尾,若是直接chain.doFilter放行。
5. 避坑指南:学生管理系统最容易翻车的6个经典场景
5.1 中文乱码:从JSP到数据库全链路排查
现象:页面上显示“姓名”变成“å§“å”,“或全部是问号。原因可能出现在三个环节。第一,JSP页面本身的charset没设置:<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>这三件套必须齐。第二,GET请求的Tomcat默认编码是ISO-8859-1,URL里带中文参数会乱,需要在Tomcat的server.xml里给Connector加URIEncoding="UTF-8"属性。第三,数据库表字段的collation如果是latin1,哪怕页面和连接都是UTF-8,存进去的也是乱码。解决:前端、Servlet(setCharacterEncoding("UTF-8")要放在getParameter之前)、JDBC连接URL、数据库建表语句四级统一UTF-8。有一点特别容易被忽略:在web.xml里配置CharacterEncodingFilter同样能解决POST请求的编码问题:
<filter> <filter-name>encodingFilter</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> </filter>如果你用的是普通Servlet项目而不是Spring,也可以写一个自己的编码过滤器,doFilter里直接request.setCharacterEncoding("UTF-8")再放行。
5.2 数据库连接失败:时区、驱动版本和useSSL三个参数
现象:Tomcat启动正常,JS正常打开,但一点登录就报Communications link failure或Access denied for user。排查顺序:先命令行测试能否连上MySQL,mysql -uroot -p,能连说明MySQL服务没挂。再检查驱动包版本,MySQL 8.0的驱动类名是com.mysql.cj.jdbc.Driver,MySQL 5.7及以前是com.mysql.jdbc.Driver,如果你的代码或配置文件还写着老类名,会报ClassNotFoundException。之后看URL,MySQL 8.0必须加serverTimezone=Asia/Shanghai,否则驱动报SQLException: The server time zone value 'Öйú±ê׼ʱ¼ä' is unrecognized。完整一条URL通常是:
jdbc:mysql://localhost:3306/student_manager?useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=utf8另外注意:在Navicat里能连上,不代表Tomcat所在的JDK环境下能连上,检查是不是把ojdbc打成了mysql-connector,或者在classpath里混入了多个版本的驱动jar包,多个驱动版本共存时类加载顺序不可控,会出现奇怪报错。
5.3 修改密码后登录失效:Session还是Cookie的锅
现象:在个人中心修改完密码,退出系统再登录,用新密码怎么也登不进去。原因:很多管理系统把密码存在Session里用于后续操作校验,或者前端JS缓存了旧密码。你的UserDao里updatePassword更新了t_user表,但Session里的user对象里的password字段还是旧值。当系统某些模块取出session里的user去和数据库比对时,因为password不匹配,认为是非法操作。解决:修改密码成功后,要主动刷新Session里的用户信息,有两种做法:把session里原来的user属性remove掉,重新查库set进去;或者调用session.invalidate()强制用户重新登录。后续操作都基于数据库数据而不是Session里的旧字段,就不会有这个问题。
5.4 Tomcat端口被占用:8080默认端口在实验室环境经常被抢
现象:启动Tomcat时控制台报Port 8080 required by Tomcat v9.0 Server at localhost is already in use。实验室电脑很容易有多个同学同时开IDEA,或者之前崩溃的Java进程没杀掉。解决方案,第一优先找到占用程序并结束:
netstat -ano | findstr 8080 taskkill /PID 进程号 /F第二步才是修改Tomcat端口。如果不想杀别的同学进程,在conf/server.xml里改:
<Connector port="8080" protocol="HTTP/1.1" />改成8081或8082之后,访问路径也要相应变化:http://localhost:8081/项目名/。注意如果项目里有用到webService或RMI,还有可能占用8005和8009端口,轻则启动慢,重则无法启动,本地测试时改三个端口或只保留8080协议也能用。检查的时候可以执行catalina.bat run,这会直接在前台运行Tomcat并打印日志,比看eclipse控制台更直观。
5.5 重新部署后数据全没了:MySQL数据和项目分离的认知要建立
现象:你在IDEA里重新部署了war包,项目能开,但登录进去后发现之前测试的数据全没了,学生列表空荡荡。原因:有两种可能。第一种是项目里用了内存数据库(比如H2)或者手动在JVM里存List,重启后数据当然没了;第二种是数据库和数据文件分开,你重建了项目但没重建数据库,而连接工具类指向的库名变了,或者MySQL服务没启动。课程设计项目的数据库默认是MySQL,只要你没有执行DROP DATABASE,数据不会因为重新部署丢失。排查思路是直接在Navicat里看student_manager库里的t_student表有没有数据。如果表里没数据,说明你当初可能是通过页面添加的学生数据,这些数据在MySQL里,正常都在。如果数据库是好的,再看代码里DBUtil连接的库名是不是写错。我的习惯是每次重新部署前后先备份一下数据库:
mysqldump -uroot -p123456 student_manager > backup_$(date +%Y%m%d).sql这条命令会把student_manager全库导出成一个SQL文件,出问题时用source命令恢复即可,是课程设计阶段最便宜的后悔药。
5.6 表单提交第二次报错:隐藏域、刷新和重定向
现象:新增一条学生记录后,浏览器刷新,又弹出一个确认框,点确定后同一条数据被插了两次。这是典型的“表单重复提交”问题。原因:用户提交后,Servlet执行INSERT,然后forward到成功页,此时浏览器的URL还是处理表单的Servlet路径,按F5就再次执行同样的INSERT。预防方案在我的4.2节已经提到,正确写法是PRG模式:POST -> Redirect -> GET。提交成功后使用response.sendRedirect()让浏览器重新发起GET请求到列表页。除此之外,还可以在表单里放一个隐藏的token,每次刷新时重置,服务端校验token是否一致,课程设计做到PRG模式已经完全够用,不会被老师深挖。
6. 进阶技巧:拿什么验证系统“真的没问题”,以及一次性能日志排查
演示时系统能跑,不代表系统足够稳。你可以用三个维度的验证来给自己兜底。第一个维度是SQL边界:翻遍所有DAO层,确认每个查询都用了PreparedStatement,没有一处字符串拼接SQL;分页参数正确计算,count和list两次查询的数据对得上。第二个维度是权限边界:直接用浏览器访问deleteStudentServlet、updateStudentServlet这些URL,未登录时是否被拦回登录页;以普通用户登录后,尝试通过URL直接删除,是否返回403。第三个维度是并发边界:一个JSP页面同时被三个浏览器打开,分别操作不同学生的记录,观察是否有重复学号插入成功的现象。如果出现重复,说明你的业务层没有做主键或唯一索引校验,在t_student表的student_no上加上唯一索引就能解决。
除了功能验证,我每次交付前还会做一次最小化的性能排查,方式不是用压测工具,而是给DBUtil打一条日志。在获取连接和关闭连接之间,用System.currentTimeMillis记录耗时:
long start = System.currentTimeMillis(); // 执行查询 long cost = System.currentTimeMillis() - start; if (cost > 100) { System.out.println("[SQL慢查询] " + sql + " 耗时 " + cost + "ms"); }一般课程设计的查询耗时都在几十毫秒以内,如果发现某条SQL耗时超过200ms,优先看是不是没有给t_student表的student_no和name字段建索引。列表页作为模型数据,还是要保持在t_student表的查询上。这行排查习惯让我有两次在验收前发现本地数据库查询100ms、部署到老师电脑上查询直接飙到2秒的问题——原因是教学机房机器性能较差,而列表页一次性查询了全表且未分页。你可以在演示前把列表页默认大小设成10条,既能证明你会分页,也会避免一次拉取几百条数据造成卡顿。这也是我希望帮到你的一点:课程设计不追求炫技,但追求“每一步都有据可查、每个参数都改得明白”,照着这个方向走,你和别人代码的差距会体现在细节里。
本文还有配套的精品资源,点击获取