☰
JSP+Servlet+JDBC学生实习管理系统:从数据库设计到部署避坑
2026/10/7 5:53:55 网站建设 项目流程

简介:这是一套基于JavaWeb的学生实习管理系统项目资源,采用JSP、Servlet、JDBC与MySQL实现B/S架构,主要面向计算机专业毕业设计学生及需要项目实战的Java学习者。压缩包共3个文件,约4.88MB,包含项目源码、数据库脚本(SQL)和项目说明文档,源码经过严格调试可直接运行,适合作为毕设参考或练习项目。系统围绕学生、老师、管理员三类角色设计功能模块:学生可管理实习日志与反馈,老师可进行实习评价,管理员支持班级、学生、老师、通知及实习信息的管理和删除,并支持密码修改,功能覆盖完整。资源包体量适中、结构清晰,便于用户快速部署到Tomcat环境,是理解JavaWeb三层架构和实现增删改查业务的实用素材。目前已有3372人学习下载,适合需要快速上手或二次开发的学习者。

1. 为什么是这套学生实习管理系统:能跑、能讲、能答

每年毕设季,总有人拿着一个 JavaWeb 项目源码包,第一步卡在环境,第二步卡在数据库脚本,第三步被答辩老师问“表之间什么关系”时当场沉默。这套基于 JSP、Servlet、JDBC 的学生实习管理系统,恰好把这三个问题都覆盖了:带完整的 MySQL 脚本、带全部源码,三种角色(学生、老师、管理员)的权限边界清晰,非常适合做毕设主项目,也适合刚学完 JavaWeb 的人拿来做第一个完整实战。我拆过不少这类项目,坦率说,它不算花哨,但胜在链路完整、结构直白,你能从登录一路看到管理员删学生,中间所有环节都看得懂、改得动。

2. 从 db_shixi.sql 拆权限模型:九张表和三种角色的数据边界

2.1 从 SQL 里拆出九张表的职责边界

拿到压缩包后先别急着导数据库,先把 db_shixi.sql 通读一遍。光看表名就能猜出这个系统的业务骨架:班级、学生、老师、管理员、通知、实习信息、实习日志、实习反馈、实习评价,一共九张表。我按职责把它们分成三组:基础资料组(班级、学生、老师、管理员)、业务主数据组(实习信息、通知)、过程记录组(实习日志、实习反馈、实习评价)。

这种分组的价值在于,答辩时老师问“系统怎么设计的”,你可以直接说:基础资料是静态数据,实习信息是业务主数据,日志、反馈、评价是学生在实习过程中产生的动态数据,三层分开,后续加功能不用动前面的表结构。

各组之间通过外键关联,比如学生表通过 class_id 关联班级表,实习日志通过 student_id 关联学生表,评价表通过 teacher_id 关联老师表,同时用 intern_id 或 student_id 关联到具体的实习任务。外键不是摆设,它保证了“没有这个学生就不可能插入他的日志”,这也是后面管理员删学生时会遇到约束冲突的根源。

2.2 关键表字段与外键设计拆解

我挑三张最有代表性的表展开讲。第一张是学生表,字段基本是学号、姓名、性别、班级外键、联系方式、登录密码。注意学号在这里一般是业务主键(唯一索引),而 id 是自增物理主键,两者并存是这个阶段项目的常见写法,好处是业务查询走学号,关联查询走 id。第二张是实习日志表,通常包含日志标题、内容、日期、学生外键、实习外键,其中 student_id 指向学生表,internship_id 指向实习信息表。第三张是实习评价表,包含评价内容、评价等级、评价日期、学生外键、老师外键。

这三张表的结构没有高深技巧,但非常适合答“表之间是什么关系”这个问题:学生和日志是 1 对 N,老师和评价是 1 对 N,学生和评价是 1 对 N,实习信息和日志是 1 对 N。整个系统的核心就是这三组一对多关系。再看管理员表,通常只有 id、用户名、密码,字段最少,因为它只负责登录和会话校验,不参与业务数据流通。通知表则独立于所有业务表,谁都可以读,只有管理员能写。

2.3 把 SQL 导进 MySQL:命令行与可视化两种方式

导入数据库是最容易翻车的一步,我给出两种常用方式。第一种是命令行方式,适合任何环境:

mysql -u root -p CREATE DATABASE db_shixi DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE db_shixi; SOURCE /你的解压目录/db_shixi.sql; SHOW TABLES;

这段命令的含义是先登录 MySQL,然后创建名为 db_shixi 的数据库。注意我特意指定了 utf8mb4 字符集,如果不指定,很多系统默认是 latin1,后面页面显示中文全是问号。SOURCE 后面必须是 SQL 文件的绝对路径,Windows 下路径分隔符用正斜杠。最后 SHOW TABLES 确认九张表都导进去了,这一步相当于自检。

第二种是用 Navicat 或 MySQL Workbench 可视化管理工具:新建数据库,字符集选 utf8mb4,然后右键数据库选择“运行 SQL 文件”,选路径后执行。可视化方式的好处是能看到每张表的行数,便于后续核对数据。导入成功后,重点检查三张关联表的行数:学生表如果有 20 条,日志表至少该有几条对应记录,这样能验证外键数据是配套的。如果发现日志表是空的,也别慌,可能是脚本主动清空了过程数据,只保留基础资料,你登录后自己新增即可。

3. 跑起来的第一步:环境版本对齐与 Eclipse 部署全流程

3.1 版本对齐:JDK、Tomcat、MySQL 三件套的搭配逻辑

环境起不来,八成不是代码问题,是版本不匹配。这套系统是 JSP + Servlet + JDBC 的经典组合,对版本要求不高,但要遵循一个原则:JDK 和 Tomcat 的版本不能跨代太多。常见组合有两种,JDK 8 + Tomcat 8.5 或 Tomcat 9,这是最稳妥的;JDK 11 + Tomcat 9 也能跑,但一些老代码里如果有 JDBC 驱动相关写法,可能要多配 module 访问权限。

对应的 JDBC 驱动也有讲究。如果你用的是 MySQL 5.x,驱动类是 com.mysql.jdbc.Driver;如果 MySQL 8.x,驱动类必须换成 com.mysql.cj.jdbc.Driver,且连接串要加时区参数。这套系统的数据库脚本没有限定版本特性,所以两边都能兼容,但你得在代码里确认驱动类写的是哪个,否则会出现“找到类但连接失败”的诡异报错。

Tomcat 的安装路径建议不要带空格和中文,Eclipse 识别 web 项目时偶尔会在这类路径上出幺蛾子。我一般装在 D:\apache-tomcat-8.5.xx,不带版本号之外的描述文字。JDK 的话,装完务必检查 JAVA_HOME 环境变量是否指向 JDK 安装目录而不是 JRE 目录,很多人这一步错了,Tomcat 能启动但 JSP 编译不过。

3.2 Eclipse 导入项目与部署到 Tomcat

拿到 shixi.zip 解压后,打开 Eclipse,按如下流程操作。这里注意,基于 Servlet 的 Web 项目不一定有 .classpath 等配置文件,所以最好用导入 Existing Projects into Workspace 而不是 Open Projects from File System,后者的导入方式容易丢 Web 项目属性。

File -> Import -> General -> Existing Projects into Workspace Select root directory -> 选中 shixi 解压目录 勾选 Copy projects into workspace 点击 Finish

导入后,在 Package Explorer 里确认项目结构包含 src、WebContent(或 WebRoot)、WEB-INF 三个关键部分。没有 WebContent 或者没有 WEB-INF 下的 web.xml,这个项目在 Eclipse 里跑不了。确认没问题后,配置 Tomcat:Window 菜单下找到 Server,新建一个 Server 实例,选你解压的 Tomcat 版本,把项目 Add 到 Server 的配置列表里。

一个常见的理解偏差是:不是把项目放进 Tomcat 的 webapps 目录,而是通过 Eclipse 的 Server 容器把项目“发布”到 Tomcat。Eclipse 会在第一次发布时自动把项目打包,放在 Tomcat 安装目录的 wtpwebapps 文件夹下。如果你手贱把项目手动复制到 webapps,又通过 Eclipse 启动,反而可能出现两个版本不一致导致的诡异问题。

3.3 数据库连接配置与启动检查

项目跑起来之前,必须改数据库连接配置。配置文件名常见的有 db.properties、jdbc.properties,或者直接在某个 DBUtil.java 里写死。我先给一个典型的 db.properties 内容:

driver=com.mysql.jdbc.Driver url=jdbc:mysql://localhost:3306/db_shixi?useUnicode=true&characterEncoding=utf8 username=root password=123456

参数说明:url 里的 db_shixi 是数据库名,必须和 2.3 节创建的一致;useUnicode 和 characterEncoding 一起用,是为了保证 Java 程序和 MySQL 之间传输中文不乱码。driver 这行取决于你的 MySQL 版本,如果你本地是 MySQL 8,这里要改成 com.mysql.cj.jdbc.Driver,并且 url 结尾追加 serverTimezone=Asia/Shanghai。改好后,启动 Tomcat,在浏览器里输入http://localhost:8080/shixi,看到登录页就说明环境通了。如果出现 404,先检查项目名是不是 shixi,再看控制台有没有报错日志,这两步能解决 70% 的同类问题。

4. 核心代码走查:从登录分流到实习日志落库的真实链路

4.1 登录鉴权:一个 Servlet 如何分流三种角色

这个系统最值得看的一段代码是登录逻辑,它决定了用户进去后看到哪个页面。典型的实现是:LoginServlet 接收表单提交的用户名密码和角色类型,然后查对应的表,查成功后把用户对象塞进 session,再根据角色类型跳转不同的首页。我写一个简化版的结构示意:

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String username = request.getParameter("username"); String password = request.getParameter("password"); String role = request.getParameter("role"); if ("student".equals(role)) { Student student = studentDao.findByUsernameAndPwd(username, password); if (student != null) { request.getSession().setAttribute("user", student); request.getSession().setAttribute("role", "student"); response.sendRedirect("student/index.jsp"); } else { request.setAttribute("msg", "用户名或密码错误"); request.getRequestDispatcher("login.jsp").forward(request, response); } } // teacher 和 admin 的逻辑结构相同,只是查不同的表 }

逻辑说明:这个 Servlet 先做参数接收,再按角色调用不同的 DAO 方法,查询成功则写入 session,跳转对应首页,失败则回到登录页并携带错误信息。这里有两个要点值得注意。第一,session 里同时存了 user 对象和 role 字符串,role 的作用是在后续页面菜单栏里动态显示功能入口,学生看到日志和反馈,老师看到评价,管理员看到全部管理项。第二,else 分支用 forward 而不是 redirect,这样页面能通过request.getAttribute("msg")显示错误提示,URL 不变,体验符合传统 JSP 项目的习惯。

需要点出的是,这套代码没有用过滤器拦截未登录用户,你完全可以直接访问student/index.jsp。这是毕设项目里普遍存在的真实问题,答辩提到时可以说自己后续打算加一个 LoginFilter 补上权限校验,这反而是一个不错的加分项。我在 6.2 节会给出具体的过滤器写法。

4.2 学生写实习日志:JSP 表单到 JDBC 入库的完整链路

学生最核心的操作是写实习日志。流程是:日志列表页点新增,进入一个 Form 页面填写标题和内容,提交后由 LogServlet 接收并调用 DAO 插入数据。我贴一下这个 Servlet 的核心插入逻辑:

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding("UTF-8"); String title = request.getParameter("title"); String content = request.getParameter("content"); HttpSession session = request.getSession(); Student student = (Student) session.getAttribute("user"); InternshipLog log = new InternshipLog(); log.setTitle(title); log.setContent(content); log.setStudentId(student.getId()); log.setCreateTime(new Date()); LogDao dao = new LogDao(); boolean success = dao.insert(log); if (success) { response.sendRedirect("listLogServlet"); } else { request.setAttribute("msg", "保存失败,请重试"); request.getRequestDispatcher("logAdd.jsp").forward(request, response); } }

逻辑说明:学生登录后 session 中已经有 user 对象,这里是直接从 session 拿当前用户 id,而不是让用户再填一次学号。这样设计既省了表单字段,也避免了用户伪造学号插入别人日志的安全问题。注意第一行request.setCharacterEncoding("UTF-8")必须放在读取任何参数之前,否则中文内容在第一次 getParameter 后已经按默认编码解析,后面再设置就晚了。

对应的 DAO 插入方法是用 PreparedStatement 做参数化查询:

String sql = "INSERT INTO t_internship_log (title, content, student_id, create_time) VALUES (?, ?, ?, ?)"; PreparedStatement ps = conn.prepareStatement(sql); ps.setString(1, log.getTitle()); ps.setString(2, log.getContent()); ps.setInt(3, log.getStudentId()); ps.setTimestamp(4, new Timestamp(log.getCreateTime().getTime())); ps.executeUpdate();

参数说明:用 PreparedStatement 而不是拼接字符串,主要目的是防止 SQL 注入,其次也能省去手动加引号和转义单引号的麻烦。这里参数类型有 String、int、Timestamp 三种,位置必须和 SQL 里的问号一一对应。JDBC 获取连接的代码在这个项目里是写在 BaseDao 或者 DBUtil 里的,通常是 static 块加载驱动、getConnection 返回连接。理解到这个粒度,答辩时被问“你怎么连数据库的”就不会只回一句“用 JDBC”。

4.3 老师点评与管理员删除背后的数据联动

老师角色的核心操作是给学生的实习情况写评价。从功能描述看,老师是“评价信息的添加、查看”,所以在数据模型上,评价表的 student_id 指向学生,teacher_id 指向老师。老师在评价列表里看到的是分配到本班或本组的学生,选中一个学生填写评价内容,提交后插入评价表,同时可以按时间排序查看历史评价记录。

管理员删除学生的操作更值得留意。因为有外键约束,删除一个学生时如果他的日志、反馈、评价记录还存在,数据库会直接报错。这也是为什么避坑章节里我单独强调删除顺序。实际项目里,管理员删除学生的 Servlet 一般会拆成三步:先按 student_id 删日志表记录,再删反馈和评价,最后才删学生表记录,且三步套在同一个事务里。如果不在事务里,删到第三步失败时,前两步已经提交,数据就成了孤儿。

4.4 分页与查询的小细节

这个系统的整体规模不大,但实习日志和评价列表如果数据多了,老项目一般会有分页。分页实现常见做法是用 LIMIT 和总条数 COUNT,页面里通过 request 传参 pageNo、pageSize,底部分页栏用 JSP 标签循环生成页码。如果你拿到手的源码里没实现分页,只有“查看”功能列表,想自己加一版也很简单,核心就是两条 SQL:

SELECT * FROM t_internship_log WHERE student_id = ? ORDER BY create_time DESC LIMIT ?, ?; SELECT COUNT(*) FROM t_internship_log WHERE student_id = ?;

前一条是取当前页数据,后一条是取总条数算出总页数。LIMIT 的第一个参数是起始偏移,等于 (页码-1) * 每页条数,第二个参数是每页条数。答辩时如果老师问“数据量大了怎么办”,讲清这两条 SQL 就够用了。没必要在这个项目里硬上 MyBatis 分页插件,反而是把原生分页写法吃透更符合这套系统的定位。

5. 避坑指南:这套源码放本地跑,最容易翻车的五个地方

5.1 Tomcat 启动成功但访问 404

现象:Tomcat 控制台显示 Server startup,浏览器访问http://localhost:8080/shixi却始终 404。

原因:项目没有被正确发布到 Tomcat 的容器上下文。常见情况是 Eclipse 的 Server 列表里项目没被 Add 进去,或者项目名的 context path 不是 shixi。

解决:双击 Server 实例,查看 Modules 标签页,确认项目被添加且 Path 是 /shixi。如果没有,点击 Add Module 添加,保存后重新启动 Tomcat。访问路径必须和 path 完全一致,大小写也不能错。

5.2 MySQL 8 驱动报错 Public Key Retrieval is not allowed

现象:启动项目后页面能打开,但一旦执行数据库查询就抛异常,提示Public Key Retrieval is not allowed。

原因:MySQL 8 默认使用 caching_sha2_password 认证插件,客户端首次连接时如果需要获取公钥,而 JDBC 连接串没允许获取公钥,就会报这个错。

解决:驱动类改为 com.mysql.cj.jdbc.Driver,连接串末尾追加allowPublicKeyRetrieval=true&useSSL=false,并补上 serverTimezone=Asia/Shanghai。这一行配置只影响连接行为,不影响业务代码,改完重启 Tomcat 即可。

5.3 JSP 页面中文全部显示乱码

现象:登录页、列表页里的中文全是问号或者乱码,但数据库里中文正常。

原因:浏览器收到的响应里 Content-Type 头没有指定 charset,或者 request 参数读取时的编码和服务端不一致。最常见的是 JSP 文件顶部没有声明 pageEncoding,而项目文件本身是 UTF-8 编码。

解决:检查每个 JSP 第一行的指令,把 contentType 里加上 charset=UTF-8,pageEncoding 设为 UTF-8。同时,页面里所有 form 表单可以加 accept-charset,但根治还是在 Servlet 的 doGet 和 doPost 开头都写上 request.setCharacterEncoding("UTF-8")。

5.4 管理员删除学生时数据库报外键约束错误

现象:管理员在删除一个已有日志记录的学生时,页面提示删除失败,后台日志显示Cannot delete or update a parent row: a foreign key constraint fails。

原因:日志、反馈、评价表都通过外键引用学生表的 id,直接删主表记录会被约束拦住。

解决:在删除逻辑里按顺序删子表,先删 t_internship_log、t_internship_feedback、t_internship_evaluation 中 student_id 等于该学生的记录,最后再删 t_student。删除操作包在一个事务里,如果项目里没写事务,可以在 Service 层用同一个 Connection 分步执行,任一步失败就回滚。

5.5 Tomcat 端口被占用导致启动失败

现象:启动 Tomcat 时控制台报Port 8080 required by Tomcat v8.5 Server at localhost is already in use。

原因:之前有残留的 Tomcat 进程没退出,或者别的程序占用了 8080 端口。

解决:打开命令行执行netstat -ano | findstr :8080,找到占用端口的 PID,再执行taskkill /PID 对应PID /F杀掉进程。如果 8080 对你很重要,也可以直接改 Tomcat 的 server.xml 里 Connector 的 port 属性,比如改成 8081,然后访问路径同步改为http://localhost:8081/shixi。

6. 一个拿来即用的验证清单:从登录到实习日志的闭环自测

6.1 按角色走一遍完整业务流

不管你改了什么代码,最后提交毕设前,强制按这个顺序自测一遍。管理员登录,进班级信息管理,新增一个班级,再新增一个学生,密码设成 test123。然后退出,用这个新学生账号登录,添加一条实习日志,标题写“第一次巡检”,内容写中文长句,提交后确认列表能看到。退出,用老师账号登录,对学生进行评价,再退出,用管理员登录,尝试删除刚才那个学生,确认能干净删除。这一套闭环走通,系统的 CRUD、角色分流、外键联动全都验证过了。

6.2 答辩前值得补的三个加固点

如果时间富余,我建议补三个小动作,会在答辩时明显提升“完成度”的观感。第一个是加一个登录过滤器,让未登录用户无法直接访问内部页面,这个作用显著,代价极低:

public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) resp; HttpSession session = request.getSession(false); String uri = request.getRequestURI(); if (uri.endsWith("login.jsp") || uri.contains("LoginServlet") || session != null && session.getAttribute("user") != null) { chain.doFilter(req, resp); } else { response.sendRedirect(request.getContextPath() + "/login.jsp"); } }

参数说明:过滤器放行登录页和登录请求本身,其他请求校验 session 里有没有 user,没有就强制回登录页。这段代码能避免直接输入 URL 绕过登录的问题。第二个是给删除操作加一个确认提示,在 JSP 页面用一段简单的 JavaScript confirm 拦一道。第三个是统一错误页面,在 web.xml 里配置 404 和 500 跳转到友好提示页,避免答辩时浏览器里出现一大段异常堆栈。

从那以后,我拿到任何打包好的 JavaWeb 项目,第一件事不是点 Run,而是先读 SQL 脚本里的表结构和配置文件里的连接串,确认版本和字符集,再谈启动。这套学生实习管理系统我已经按这个顺序拆过两遍,每次都能在稳定复现后快速定位到业务核心。把环境配齐、把三条角色的数据流走通,它就真正属于你了,希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询