简介:这份资源是面向JavaWeb初学者与课程设计学生的《JavaWeb学生成绩管理系统》完整实战项目,适合在掌握Servlet、JSP、JDBC等基础后,通过一个真实可运行的系统把知识串联起来。项目采用MVC设计模式与动态代理模式,基于Java与MySQL开发,涵盖学生、成绩、课程、专业等核心业务模块,可帮助读者理解分层架构、DAO实现与Action调度的完整流程。压缩包共271个文件,约23.41MB,包含32个java源码、32个class编译文件、22个jsp页面、78个jar依赖、20个xml配置以及js、css、图片等前端资源,另附数据库sql脚本与说明文档,结构完整便于对照学习。目前已有6978人学习下载,适合作为课程设计、毕业设计或JavaWeb综合练习的参考案例,读者可从中获取项目搭建思路、代码组织方式与常见功能实现方法。
1. Java 学生成绩管理系统:从一份 rar 压缩包到能跑起来的课程设计
拿到「java 学生成绩管理系统(源码+数据库脚本+文档).rar」这种压缩包,多数人第一反应是解压、找 main 方法、点运行,然后被一堆红色报错劝退。这个标题背后其实是一套完整的课程设计交付物:Java 源码负责业务逻辑,数据库脚本负责建库建表,文档负责说明设计思路和接口。它解决的是「不想从零写一个 CRUD 项目、又需要一份能讲清楚原理的作业」这个真实需求,适合 Java 基础刚学完、要交课程设计或者准备面试八股文里「项目经历」那一段的人。我见过太多人把源码跑起来就完事,结果答辩时被问「成绩表为什么这么设计」直接卡壳。这篇就按我实际拆包、改配置、跑通、再讲清楚每个环节的顺序写,让你不光能跑,还能说明白。
2. 拆包先看结构:源码、脚本、文档各管什么
2.1 一个典型课程设计包的目录长什么样
解压之后别急着用 IDEA 打开,先在文件管理器里把目录树看一遍。常见的结构是三层:最外层是项目名,里面分src、sql、doc三个文件夹,有的还会多一个lib放 jar 包。src下面是标准的 Java 包结构,比如com.xxx.score,再往下是entity、dao、service、controller、util这几个包。sql文件夹里通常只有一个.sql文件,命名可能是score_db.sql或者init.sql。doc里放的是 Word 或 PDF,内容一般是需求分析、E-R 图、表结构说明、接口列表。
我一般会先打开sql文件扫一眼,因为数据库结构决定了后面所有代码能不能跑通。重点看三件事:建库语句的字符集是不是utf8mb4,建表语句里有没有外键约束,初始数据INSERT有没有包含中文。这三样任何一样出问题,后面都会以各种奇怪的方式报错。
2.2 源码里必须确认的四个文件
在 IDEA 导入之前,用文本编辑器打开这几个文件确认内容,比导入后再排查省事得多。
第一个是src根目录下的db.properties或jdbc.properties。里面是数据库连接四要素:驱动类名、URL、用户名、密码。课程设计包里的密码经常是作者本机的,比如root/123456,你本机 MySQL 密码不一样就得改。
第二个是pom.xml(Maven 项目)或者lib文件夹(普通项目)。看依赖里有没有mysql-connector-java,版本是多少。MySQL 8 以上必须用8.0.x的驱动,用5.1.x会报Unknown system variable或者时区错误。
第三个是web.xml或者 Spring Boot 的application.yml。这决定了项目是传统 Servlet 项目还是 Spring Boot 项目,启动方式完全不同。
第四个是util包里的DBUtil或BaseDao。这里能看到连接是怎么获取的,是每次DriverManager.getConnection还是用了连接池。课程设计里手写DriverManager的居多,这种写法在并发下会出问题,但单机跑演示够用。
# 在解压目录下快速定位关键文件 find . -name "*.properties" -o -name "pom.xml" -o -name "*.sql" -o -name "web.xml" # 查看 sql 文件前 50 行,确认建库语句和字符集 head -50 sql/*.sql上面两条命令的作用是:第一条把配置文件和脚本位置列出来,避免在多层目录里迷路;第二条直接看 SQL 开头,确认CREATE DATABASE语句里的CHARACTER SET是不是utf8mb4。如果看到latin1或者没写字符集,后面中文成绩单导出大概率乱码。
提示:如果
sql文件里没有CREATE DATABASE语句,只有USE xxx;,说明作者假设你已经手动建了库。这种情况先自己建一个同名库,字符集选utf8mb4,再执行脚本。
3. 数据库脚本怎么跑:建库、建表、灌数据的顺序不能乱
3.1 用命令行导入比图形化工具更可控
很多人习惯用 Navicat 右键「运行 SQL 文件」,但课程设计的脚本里经常有DROP TABLE IF EXISTS和CREATE TABLE混在一起,图形化工具执行到一半报错就停了,你也不知道哪句成功了。我一般用命令行,因为报错会精确到行号。
# 登录 MySQL 并指定字符集 mysql -u root -p --default-character-set=utf8mb4 # 在 MySQL 交互界面里执行 source /path/to/score_db.sql;第一行命令的--default-character-set=utf8mb4是关键参数,它保证客户端发送和接收的数据都用 utf8mb4 编码。第二行source后面跟绝对路径,Windows 下路径用正斜杠或者双反斜杠。执行完用SHOW TABLES;确认表都建出来了,再用SELECT COUNT(*) FROM student;看初始数据有没有灌进去。
如果脚本执行到一半报ERROR 1215: Cannot add foreign key constraint,说明外键引用的字段类型或字符集不一致。常见原因是主表用utf8mb4,从表用utf8,或者主键是int而外键写成了bigint。解决办法是统一改成int和utf8mb4,或者干脆在课程设计里去掉外键约束,用程序逻辑保证一致性。
3.2 表结构里必须看懂的三个设计点
成绩管理系统的核心表一般就四张:student、course、score、user。student存学号、姓名、班级;course存课程号、课程名、学分;score存学号、课程号、分数;user存登录账号和角色。
第一个设计点是score表的主键。有的作者用自增id做主键,有的用(student_id, course_id)联合主键。联合主键的好处是天然防止同一个学生同一门课录两次成绩,坏处是后面想改成绩得先删再插。我一般会保留联合主键,但在service层加一个「先查后插或更新」的逻辑。
第二个设计点是分数字段的类型。用int还是decimal(5,2)?如果只存整数分,tinyint就够;如果要存 89.5 这种,必须用decimal,不能用float,因为float在比较和求和时会出现89.499999这种玄学值。
第三个设计点是user表的密码字段。课程设计里明文存密码的占多数,这在实际项目里是血泪教训级别的错误。你可以在文档里说明「演示用明文,生产环境应加盐哈希」,答辩时反而是加分项。
-- 检查表结构和字符集 SHOW CREATE TABLE score; -- 检查初始数据的中文是否正常 SELECT * FROM student LIMIT 5;第一条语句会输出完整的建表语句,重点看ENGINE=InnoDB和DEFAULT CHARSET=utf8mb4。第二条看姓名列有没有变成问号,如果有,说明导入时字符集没对上,需要重新用--default-character-set=utf8mb4导入。
4. 源码跑起来:从导入 IDEA 到登录成功的完整路径
4.1 普通 Java Web 项目和 Spring Boot 项目的启动差异
先判断项目类型。如果根目录有pom.xml且src/main/java下有带@SpringBootApplication注解的类,就是 Spring Boot 项目,直接运行那个类即可。如果根目录有web文件夹或者WebContent,并且有web.xml,就是传统 Servlet 项目,需要配置 Tomcat。
Spring Boot 项目的配置文件通常是application.properties或application.yml,里面数据库连接写法和db.properties略有不同。重点看spring.datasource.url里的时区参数,MySQL 8 必须加serverTimezone=Asia/Shanghai,否则启动时报The server time zone value错误。
传统 Servlet 项目在 IDEA 里的配置步骤:File→Project Structure→Modules→ 选中项目 →Sources标签页把src标记为 Sources Root;再到Facets里添加 Web,指定web.xml路径;最后到Artifacts里添加Web Application: Exploded,输出目录选target/项目名。Tomcat 配置里 Deployment 选这个 Artifact。
<!-- 传统项目 web.xml 里必须有的 servlet 映射示例 --> <servlet> <servlet-name>LoginServlet</servlet-name> <servlet-class>com.xxx.score.controller.LoginServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>LoginServlet</servlet-name> <url-pattern>/login</url-pattern> </servlet-mapping>上面这段配置的作用是把/login这个 URL 映射到LoginServlet类。如果web.xml里没有这段,访问登录页面会报 404。参数说明:servlet-name可以随便起,但要和servlet-mapping里的一致;servlet-class必须是完整包名加类名;url-pattern决定浏览器访问的路径。
4.2 登录跑通后先别急着点其他菜单
登录成功只说明数据库连接和用户表没问题。接下来按「学生管理 → 课程管理 → 成绩录入 → 成绩查询」的顺序逐个点。每点一个菜单,看 IDEA 控制台有没有异常堆栈。常见的报错和对应原因:
NullPointerException出现在service层,多半是dao查询返回了null,而service没做判空。比如查一个不存在的学号,studentDao.findById返回null,后面直接调student.getName()就崩了。
SQLSyntaxErrorException说明 SQL 语句拼错了。课程设计里用字符串拼接 SQL 的很多,比如"SELECT * FROM student WHERE name = '" + name + "'",如果name里带单引号就会报语法错误。这也是 SQL 注入的入口,答辩时被问到可以主动说「这里应该用 PreparedStatement」。
ClassNotFoundException: com.mysql.cj.jdbc.Driver说明驱动 jar 没放到WEB-INF/lib下,或者 Maven 依赖没刷新。传统项目必须手动把mysql-connector-java-8.0.x.jar复制到web/WEB-INF/lib目录,IDEA 的 Artifact 才会把它打进部署包。
// 一个典型的 DAO 查询方法,注意 PreparedStatement 的用法 public Student findById(int id) { String sql = "SELECT * FROM student WHERE id = ?"; try (Connection conn = DBUtil.getConnection(); PreparedStatement ps = conn.prepareStatement(sql)) { ps.setInt(1, id); try (ResultSet rs = ps.executeQuery()) { if (rs.next()) { Student s = new Student(); s.setId(rs.getInt("id")); s.setName(rs.getString("name")); return s; } } } catch (SQLException e) { e.printStackTrace(); } return null; }这段代码的逻辑是:用?占位符代替字符串拼接,通过ps.setInt(1, id)把参数传进去,这样即使id是特殊字符也不会破坏 SQL 结构。参数说明:1表示第一个问号的位置,从 1 开始计数;rs.getString("name")里的"name"必须和数据库列名完全一致,大小写不敏感但拼写不能错。返回null表示没查到,调用方必须判空。
5. 避坑与排查:课程设计包里最常见的五个翻车点
5.1 中文乱码:从数据库到浏览器要过四道关
现象:学生姓名在数据库里看是正常的,但在网页上显示成??或者æŽå¥½。
原因:字符集在四个环节可能不一致——数据库建库时用了latin1、JDBC URL 没加characterEncoding=utf8、Tomcat 的server.xml里 Connector 没加URIEncoding="UTF-8"、JSP 页面没写<%@ page contentType="text/html;charset=UTF-8" %>。
解决:按顺序检查这四处。数据库用SHOW CREATE DATABASE 库名;确认;JDBC URL 改成jdbc:mysql://localhost:3306/score_db?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai;Tomcat Connector 加URIEncoding="UTF-8";JSP 页面头部加 page 指令。四道关都过了,中文就不会乱。
5.2 驱动版本不匹配:MySQL 8 配 5.1 驱动必报错
现象:启动时报Unknown system variable 'query_cache_size'或者The server time zone value '?D1ú±ê×?ê±??' is unrecognized。
原因:MySQL 8 的默认认证插件是caching_sha2_password,而mysql-connector-java 5.1.x不支持这个插件。时区错误则是因为 8.0 驱动要求显式指定时区。
解决:把驱动换成mysql-connector-java-8.0.28或更高版本,JDBC URL 加上serverTimezone=Asia/Shanghai。如果不想换驱动,也可以在 MySQL 里执行ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '密码';把认证插件改回旧的,但不推荐,因为新项目迟早要升。
5.3 端口占用:Tomcat 启动一半就停
现象:IDEA 控制台显示Address already in use: JVM_Bind,Tomcat 启动失败。
原因:8080 端口被其他程序占了,常见的是另一个 Tomcat 实例、或者某个后台服务。
解决:Windows 下netstat -ano | findstr 8080找到 PID,再到任务管理器结束进程。或者直接改 Tomcat 的conf/server.xml,把 Connector 的port="8080"改成8081。改完记得 IDEA 里 Deployment 的 URL 也要跟着改。
5.4 外键约束导致删数据失败
现象:删除一个学生时报Cannot delete or update a parent row: a foreign key constraint fails。
原因:score表里有这个学生的成绩记录,外键约束阻止删除。
解决:两种思路。一是先删score里对应记录再删student,在service层用事务包起来。二是建表时把外键写成ON DELETE CASCADE,删学生时自动删成绩。课程设计里我倾向第一种,因为能体现你懂事务,答辩时有的说。
5.5 文档和代码对不上:E-R 图里的字段在表里找不到
现象:文档里写student表有email字段,但实际建表语句里没有,导致注册功能报错。
原因:作者先写文档后改代码,或者反过来,两边没同步。
解决:以代码为准,把文档里的字段说明改成和SHOW CREATE TABLE输出一致。如果代码里确实需要email,就手动ALTER TABLE student ADD COLUMN email VARCHAR(50);。答辩前一定把文档和实际表结构对一遍,被老师发现不一致很尴尬。
6. 把课程设计变成面试素材:三个能讲深的改造点
跑通只是及格线。如果你想让这个项目在面试里撑起十分钟的「项目经历」环节,得做三处小改造,每处都能引出一个八股文考点。
第一处改造:把DBUtil里的DriverManager换成 Druid 连接池。加一个druid.properties,用DruidDataSourceFactory.createDataSource创建数据源。这样面试官问「数据库连接池原理」时,你可以从「频繁创建销毁连接的开销」讲到「Druid 的保活和监控」。改造代码不超过二十行,但话题深度完全不一样。
第二处改造:给成绩录入加事务。在service层的方法上加@Transactional(Spring 项目)或者手动conn.setAutoCommit(false)+commit/rollback(传统项目)。面试官问「Java 怎么保证数据一致性」时,你可以从「成绩录入要同时写score表和更新student表的学分绩点」这个场景切入,讲 ACID 和隔离级别。
第三处改造:把登录密码从明文改成 MD5 加盐。在user表加一个salt字段,注册时生成随机盐,存MD5(password + salt)。登录时用同样的盐算一遍再比对。面试官问「密码怎么存才安全」时,你可以顺势聊到彩虹表、哈希碰撞、为什么 MD5 不够要用 bcrypt。
// 加盐 MD5 的简单实现,课程设计够用 public static String md5WithSalt(String password, String salt) { try { MessageDigest md = MessageDigest.getInstance("MD5"); md.update((password + salt).getBytes(StandardCharsets.UTF_8)); byte[] digest = md.digest(); StringBuilder sb = new StringBuilder(); for (byte b : digest) { sb.append(String.format("%02x", b)); } return sb.toString(); } catch (NoSuchAlgorithmException e) { throw new RuntimeException("MD5 算法不可用", e); } }这段代码的逻辑是:把密码和盐拼接后做 MD5,输出十六进制字符串。参数说明:password是用户输入的原始密码,salt是每个用户独立的随机字符串,存在user表的salt列。String.format("%02x", b)保证每个字节输出两位十六进制,不足补零。注意 MD5 在安全领域已经不够强,但课程设计里用来演示「加盐」这个概念足够了。
我自己的习惯是,每拿到一个课程设计包,先花二十分钟把上面三处改造做完再开始写文档。这样文档里的「系统优化」章节有真东西可写,答辩时老师问「你这个和别人的有什么不同」也有话可说。希望帮到你。
本文还有配套的精品资源,点击获取