
简介一份用Java编写、基于Web的学生成绩管理系统源码包面向初学JavaWeb的学生与开发者帮助理解以Servlet/JSP JDBC操作MySQL实现教务管理增删改查的基本流程。压缩包共36个文件其中10个java源码对应后端控制与模型9个jsp页面负责动态展示与交互10张jpg及相关png截图可直接查看各功能界面另有xml配置、README说明等辅助文件整体仅2.94MB适合下载后快速阅读与测试。截至目前已有55人在CSDN学习浏览内容紧凑、结构完整。从预览内容看系统包含系统首页、数据表设计、成绩录入与修改、条件查询、排序筛选及统计页面能够呈现一个完整JavaWeb项目的目录结构与开发脉络。入门者可将源码作为参考模板对照截图梳理请求到响应的执行路径有助于培养Servlet/JSP项目的代码组织能力和调试意识。项目虽标记为简单但已覆盖Web开发常用环节可作为课程设计或期末项目的基础版本。 每年到毕业季或者期末课设节点总能看到有人在群里问有没有Java Web项目十个里面有八个是学生成绩管理系统。我手头这个用Java写的基于Web学生成绩管理系统文件名还带个简单渣渣的自我评价——说实话这个自我评价挺准确的它确实谈不上什么高并发、微服务、分布式就是个标准的课设级CRUD项目。但恰恰是这种渣渣项目把Java Web开发最核心的那条链路完整走通了浏览器发请求、Servlet接请求、JDBC连数据库、JSP渲染页面。这篇文章就把这个系统的设计思路、核心代码、数据库结构和部署排坑全部拆开讲一遍适合正在做课设的在校生、刚学完Java基础想练Web的新手以及想快速回忆ServletJSPJDBC底层的朋友。1. 需求拆解这个渣渣系统到底在做什么1.1 课设级系统的核心需求定位打开压缩包之前我先说清楚这个系统的定位。学生成绩管理系统是一个被做烂了的经典题目但正因为烂才适合练手——它足够简单又覆盖了一个管理系统的所有标准动作登录验证、信息展示、增删改查、条件查询。这个系统只有两种角色管理员和普通用户一般就是老师或学生本人功能上围绕学生信息和成绩数据两个核心实体展开。我建议拿到任何课设项目第一步先画一个功能清单而不是急着看代码。这个系统的功能清单大概是用户登录与Session会话管理学生信息的添加、编辑、删除、列表展示按学号或姓名模糊查询学生成绩的录入与维护分数、科目简单的成绩统计展示平均分、及格率之类的简单计算看着平平无奇但一个Web项目的肉体全在这了。你把这个系统的流程走通后面学Spring Boot无非是把Servlet换成了Controller把JSP换成了模板引擎或前后端分离底层的HTTP交互逻辑和数据库操作思路是完全一样的。1.2 为什么学生管理系统是练手首选有人会问每年这么多人做学生成绩管理系统不腻吗说实话腻但作为练手项目它优势很明显。第一业务逻辑足够清晰不需要你去理解复杂的领域模型学生和成绩的关系、用户和权限的关系都是日常能感知到的概念可以集中精力在技术上。第二数据量级小单机MySQL完全带得动不需要考虑缓存、分库分表这些进阶话题最基础的原生JDBC就够了。第三需求稳定不会出现产品经理改需求的情况你从网上任何一个教程出发都能把功能补齐。我自己带过几个实习生让他们做这个题目的目的很简单看他们能不能独立完成数据库设计→后端接口→前端页面这完整的一环。从结果来看凡是能把这种渣渣系统老老实实写完的基础都不会差反而一上来就想搞Spring Cloud的往往写出来的代码连事务都没搞明白。2. 技术选型与后台架构ServletJSPJDBC的经典组合2.1 这套技术栈各自扮演什么角色这个系统没有用任何高级框架技术栈就是最原始的Java Web三件套加一个数据库Servlet处理请求逻辑JSP负责页面展示JDBC操作MySQL数据库部署在Tomcat容器上。很多人第一次接触这套东西会很懵我用一个送快递的例子来解释浏览器是你家Servlet是小区驿站JSP是驿站里的货架JDBC是快递员手里的送货单。用户在前端点了一下查询成绩这个请求会被Tomcat根据URL匹配到对应的Servlet类Servlet调用Service层或直接调用DAO层DAO通过JDBC拼SQL查询MySQL拿到结果后把数据塞进request域再forward到一个JSP页面JSP把数据渲染成HTML返回给浏览器。整个过程看起来绕但每一步的职责非常清晰Servlet不写SQLJSP不写业务逻辑JDBC不管页面样式。这套分层思想就是后面MVC模式的雏形。2.2 项目目录结构长什么样解压之后的项目结构大概是下面这样src/ ├── com.example.dao # 数据库访问层 │ ├── BaseDao.java # JDBC基础操作封装 │ ├── StudentDao.java # 学生表操作 │ └── ScoreDao.java # 成绩表操作 ├── com.example.entity # 实体类 │ ├── Student.java │ └── Score.java ├── com.example.servlet # Servlet控制器 │ ├── LoginServlet.java │ ├── StudentServlet.java │ └── ScoreServlet.java └── com.example.util # 工具类 └── DBUtil.java web/ ├── index.jsp # 登录页 ├── student_list.jsp # 学生列表 ├── score_list.jsp # 成绩列表 └── WEB-INF/web.xml # 部署描述文件新手最容易犯的错是把所有代码堆在Servlet里一个类写几百行这样跑起来没问题但是后面改需求会非常痛苦。我建议哪怕是最简单的系统也把实体类-数据库操作-请求处理三层分开这个习惯越早建立越好。实体类就是数据库表在Java里的映射一张表对应一个类类里的字段对应表的列这在后面的框架学习里也是一样的套路。2.3 一次完整请求的流转过程我把登录请求的完整流转路线写一遍这是理解整个系统的关键。用户在登录页输入账号密码点击提交浏览器把数据以HTTP POST请求的方式发送到服务端TomcatTomcat根据web.xml里配置的URL映射找到LoginServlet。LoginServlet从request对象里取出账号密码参数调用DBUtil获取数据库连接执行一条带条件的SELECT语句去user表里查记录查到就把用户信息放到session里然后重定向到主页Servlet查不到就返回登录页并提示账号或密码错误。注意这里的 dao、service 层如果暂时分不清先不用纠结名字核心是你要知道每一层各自干什么。哪怕代码写得渣流程走通、逻辑自洽就已经达到课设的基本要求了。3. 数据库设计与核心代码三张表串起全部业务3.1 表结构设计不要一上来就搞复杂关联这个系统的数据库设计特别朴素一共三张表。user表存登录账号student表存学生基础信息score表存成绩数据。有的版本会再加一个course表存课程但在这个渣渣版本里课程只是score表里的一个varchar字段。CREATE DATABASE IF NOT EXISTS student_db DEFAULT CHARSET utf8mb4; CREATE TABLE user ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(32) UNIQUE NOT NULL, password VARCHAR(64) NOT NULL ); CREATE TABLE student ( id INT PRIMARY KEY AUTO_INCREMENT, stu_no VARCHAR(20) UNIQUE NOT NULL COMMENT 学号, name VARCHAR(32) NOT NULL, gender CHAR(1), major VARCHAR(64) COMMENT 专业, class_name VARCHAR(32) COMMENT 班级 ); CREATE TABLE score ( id INT PRIMARY KEY AUTO_INCREMENT, stu_no VARCHAR(20) NOT NULL, course_name VARCHAR(32) NOT NULL, score DECIMAL(5,1), FOREIGN KEY (stu_no) REFERENCES student(stu_no) ) COMMENT 成绩表;这个设计有几个值得学习的点。第一个学生表用学号作为唯一标识成绩表通过学号外键关联学生表这是最简单的一对多关系模型。第二个字段命名用下划线风格Java实体类里对应的是驼峰命名这种映射关系在后面学MyBatis时会大量接触。第三个成绩字段用DECIMAL而不是FLOAT或DOUBLE避免浮点数精度问题这个细节很多初学者注意不到。3.2 JDBC连接封装与配置文件这个系统没有用连接池直接使用最原始的DriverManager获取连接。代码不复杂但有一个关键点必须放在前面说一定要用Class.forName加载MySQL驱动否则会报ClassNotFoundException。另外数据库连接信息我建议放在src目录下的db.properties配置文件里而不是硬编码在Java类中这样换数据库、改密码不用重新编译。jdbc.drivercom.mysql.cj.jdbc.Driver jdbc.urljdbc:mysql://localhost:3306/student_db?useSSLfalseserverTimezoneAsia/ShanghaicharacterEncodingutf8 jdbc.usernameroot jdbc.password123456一个非常常见的坑是MySQL 8.x版本的驱动类名是com.mysql.cj.jdbc.Driver老版本的教程会让你写com.mysql.jdbc.Driver如果驱动包和驱动类名不匹配启动项目时才不会报错一执行查询马上抛ClassNotFoundException或者SQLException。我建议直接使用MySQL 8.x和对应的mysql-connector-java-8.0.x.jar千万不要为了省事下载老版本驱动。DBUtil的写法是所有版本里最经典的public class DBUtil { private static String driver; private static String url; private static String username; private static String password; static { try { Properties props new Properties(); props.load(DBUtil.class.getClassLoader().getResourceAsStream(db.properties)); driver props.getProperty(jdbc.driver); url props.getProperty(jdbc.url); username props.getProperty(jdbc.username); password props.getProperty(jdbc.password); Class.forName(driver); } catch (Exception e) { e.printStackTrace(); } } public static Connection getConnection() throws SQLException { return DriverManager.getConnection(url, username, password); } }从这套代码里能看到静态代码块、配置文件读取、受检异常处理这几个基础知识点你把这几十行代码吃透JavaSE里的IO和反射基础也就复习了一遍。3.3 登录校验与Session会话管理登录功能是整个系统的入口也是最容易写出问题的环节。最基础的实现是先查数据库确认账号密码是否正确正确就把用户名存入Session然后在每个需要权限的页面或Servlet里判断Session是否为空为空就跳回登录页。这个逻辑虽然渣但它是理解HTTP无状态特性的最好案例。WebServlet(/login) public class LoginServlet extends HttpServlet { private UserDao userDao new UserDao(); 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); User user userDao.findByUsernameAndPassword(username, password); if (user ! null) { HttpSession session req.getSession(); session.setAttribute(loginUser, username); resp.sendRedirect(req.getContextPath() /student/list); } else { req.setAttribute(error, 用户名或密码错误); req.getRequestDispatcher(/index.jsp).forward(req, resp); } } }这里有两个坑需要提醒新手。第一POST请求一定要在读取参数之前设置req.setCharacterEncoding(UTF-8)否则中文用户名、密码在传递过程中会变成乱码MySQL那边比对肯定失败。第二登录成功后用sendRedirect而不是forward这是为了避免表单重复提交——用户按F5刷新页面如果用的是forward浏览器会重新提交一次表单产生重复数据。4. 关键页面与流程实现从登录到成绩查询4.1 分页查询最容易忽略但经常被问到的功能这个系统里学生列表其实没做分页因为数据量很小一个表格页全展示没有压力。但我觉得既然写了成绩管理系统最好还是在查询接口上加一个分页参数哪怕是最简单的LIMIT偏移量分页。分页的核心只有三句话第一计算出总记录数第二根据当前页码和每页条数算出偏移量第三把查询结果和总页数封装起来传到页面。// 核心分页计算 int page Integer.parseInt(req.getParameter(page) null ? 1 : req.getParameter(page)); int pageSize Integer.parseInt(req.getParameter(pageSize) null ? 10 : req.getParameter(pageSize)); int offset (page - 1) * pageSize; String sql SELECT id, stu_no, name, gender, major, class_name FROM student LIMIT ?, ?; PreparedStatement ps conn.prepareStatement(sql); ps.setInt(1, offset); ps.setInt(2, pageSize);LIMIT后面的两个问号一定要用PreparedStatement的占位符不能直接拼接字符串原因有两点一是防SQL注入二是占位符方式可以让数据库缓存执行计划性能更好。很多人的课设里用字符串拼接的方式写SQL老师测试的时候输入一个正常学号没问题但在搜索框里输入 OR 11就能看到全部数据这是非常严重的漏洞答辩时被指出来会很难看。4.2 PreparedStatement给SQL穿上防弹衣我一直强调哪怕是渣渣系统也绝对不能直接用Statement拼接SQL。先看反面教材// 绝对不要这样写 String sql SELECT * FROM student WHERE name keyword ; Statement st conn.createStatement(); ResultSet rs st.executeQuery(sql);这样写的问题在于用户输入被直接当成了SQL的一部分。正确的写法是使用PreparedStatementString sql SELECT * FROM student WHERE name LIKE ?; PreparedStatement ps conn.prepareStatement(sql); ps.setString(1, % keyword %); ResultSet rs ps.executeQuery();用占位符的好处是JDBC驱动会自动对特殊字符进行转义从根本上杜绝注入风险。这个习惯现在就要养成因为到了MyBatis里如果你在XML里写的是${}而不是#{}同样会触发SQL注入。4.3 JSP页面的实操细节这个系统的页面基本就是纯JSP加一点原生CSS没有引入任何前端框架。登录页就是一个form表单学生列表页用一个表格展示数据每一行后面跟着编辑和删除两个链接。对于课设项目这样足够了但有一些细节会让你的项目看起来更专业列表页的删除操作建议用POST表单提交而不是一个简单的get链接虽然get方式也能触发Servlet的doGet方法但语义上删除属于写操作用POST更规范。页面顶部展示当前登录用户退出登录按钮对应一个session.invalidate()的Servlet这个细节非常简单但很多同学会忘记。JSP页面里不要写Java脚本片段% %尽量用JSTL标签或EL表达式这样代码可读性高很多。就算不引入JSTL简单的EL表达式${list}也比满屏的% for(...) %干净。table border1 cellspacing0 cellpadding6 tr th学号/th th姓名/th th性别/th th专业/th th班级/th th操作/th /tr c:forEach varstu items${studentList} tr td${stu.stuNo}/td td${stu.name}/td td${stu.gender}/td td${stu.major}/td td${stu.className}/td td a hrefedit?id${stu.id}编辑/a a hrefdelete?id${stu.id} onclickreturn confirm(确定删除吗)删除/a /td /tr /c:forEach /table如果项目用的JSP版本不支持JSTL又不想引入额外依赖那也可以用最原始的% %输出但格式会比较难看。在课设答辩时老师通常会问你为什么用EL表达式而不是脚本片段答案其实就一句话脚本片段把Java代码和HTML混在一起维护困难而EL表达式只负责取值职责单一。5. 本地运行与环境避坑课设实操记录5.1 JDK、Tomcat、IDEA的版本搭配很多同学下载这个压缩包以后第一反应是代码打不开第二反应是项目启动报错其实大概率是环境版本不匹配。我建议直接用以下版本组合实测最稳组件建议版本说明JDK8或11JDK8最稳JDK11也兼容Tomcat8.5或9.0对应Servlet 3.1/4.0规范MySQL5.7或8.0注意驱动类名差异IDEA2021以上社区版就够了Maven3.6如果项目是Maven结构Tomcat 8.5对应JDK 8以上如果你本机装了JDK 17还拿着Tomcat 7那启动必报错。还有一个细节是IDEA里Project Structure中的Project SDK要和Modules里的Language Level一致否则某些期编译不通过编译器提示invalid source release。5.2 IDEA中从零配置一个Web项目如果你拿到的是源码而不是可以直接导入的war包配置步骤大概是新建一个空项目把src目录标记为Sources Root把web目录标记为Web Resources Directory再添加Tomcat服务器。我自己经常遇到的一个问题是刚导入时servlet-api.jar没有加进依赖导致所有Servlet类全部报红。解决办法是把Tomcat的lib目录下的servlet-api.jar添加到Libraries。具体操作路径是File → Project Structure → Libraries → 点击加号 → Java → 选择tomcat目录下的lib文件夹或者直接选择servlet-api.jar。这一步做完以后代码里的javax.servlet.*和jakarta.servlet.*相关依赖才算真正就位。这里顺便提醒一句Servlet 5.0以后包名变成了jakarta.servlet如果你的Tomcat版本很新而教程是老的类名的包导入会不一样这需要根据具体的Tomcat版本灵活处理。5.3 高频报错速查表我把这一两年帮人排过的问题汇总成一张速查表基本覆盖了这个项目90%的启动和运行坑报错现象常见原因解决建议启动时端口被占用Tomcat默认8080被其他进程占用了改Tomcat conf/server.xml里的port或用命令查占用进程java.lang.ClassNotFoundException: com.mysql.cj.jdbc.DriverMySQL驱动jar没放进WEB-INF/lib把驱动jar拷贝到WEB-INF/lib下或通过Maven引入依赖数据库中文乱码JDBC连接串没加characterEncodingURL末尾加上characterEncodingutf8数据库建库时指定utf8mb4HTTP 404Servlet映射路径写错或web.xml配置不对检查浏览器的访问URL是否和WebServlet里的路径一致页面能开但点登录没反应表单method写成getServlet里只实现了doPost要么把method改成post要么让doGet里调用doPostgetConnection()返回null配置文件路径写错或配置内容有空格检查db.properties是否在classpath下密码不要带空格每次排错的时候我最喜欢说的一句话是先把整个调用链路从头看一遍。404就查URL映射类找不到就查jar包查不到数据就查SQL和数据库连接不要一上来就怀疑是Tomcat坏了。这种从现象定位到原因的思维比任何具体技术都重要。6. 从渣渣到生产级系统改造的演进方向6.1 架构层面的重新审视如果你的目标不只是交一个课设这个系统有非常清晰的演进路径。第一步把JDBC换成MyBatis或MyBatis-Plus你会发现原来写的那些DAO代码瞬间少了三分之二。第二步把Servlet换成Spring MVC或直接用Spring Boot的Controller路由、参数绑定、JSON序列化都由框架处理。第三步引入Maven管理依赖不再手动下载jar包——我相信你第一次手动把mysql-connector复制到WEB-INF/lib时一定想过有没有更优雅的方式。第四步加一层Service层把事务控制从Servlet里剥离开。每次做这些改造时我都建议保留一份最原始的ServletJSP版本因为这是你理解后面所有框架的基石。Spring Boot再神奇底层还是一个Servlet容器在跑一个 RequestMapping 注解再方便本质还是帮你生成了一个Servlet映射。6.2 功能可以怎么加从功能角度这个系统可以加的东西太多了。给成绩加一个课程表变成标准的多表联查给分数加一个批量导入Excel的功能后端用POI解析上传的文件在事务里批量插入增加一个简单的成绩分析页面用ECharts画柱状图和折线图这部分很多课设加分项都是从这个点来的。甚至可以做简单的权限分级管理员能改成绩普通教师只能查学生只能看自己的分数——到这里其实你已经探索到了RBAC权限模型的一角。但我还是那句话加功能的前提是你把基础版的每个逻辑都吃透了。如果连PreparedStatement和Statement的区别都讲不清楚那加再多功能在面试官眼里也只是纸老虎。最后再分享一段个人体会。我带过不少人也看过很多份课设代码真正能把这个渣渣学生成绩管理系统从头到尾、不抄不贴地写完的人几乎都能在短时间内适应真实的业务开发。原因很简单这个系统虽小但HTTP协议、关系型数据库、面向对象设计、分层架构、异常处理这些Web开发的核心要素一样不少。所以别嫌它简单也别嫌题目老套把每一行代码、每一个流程都弄明白这个项目带给你的收获远超它渣渣的定位。你甚至可以把源码里的注释改成自己的理解然后在关键方法上多加几个System.out.println亲眼看一看数据在每层之间是怎么流转的——这个动作比任何教程都管用。本文还有配套的精品资源点击获取