news 2026/9/29 1:34:20

SSM学生成绩管理系统:数据库设计与权限控制完整方案

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
SSM学生成绩管理系统:数据库设计与权限控制完整方案

做学生成绩管理系统的人很多,但大多数版本要么只有增删改查,要么权限控制形同虚设,要么数据库设计乱到一塌糊涂。我前后带过十几个学生做这类课题,也接手过不少半成品项目,今天干脆把一套在毕业设计和课程设计里都能直接用的完整方案整理出来。这个系统看着不难,但真正要把需求分析、表结构设计、后端分层、成绩统计逻辑、部署上线这一整条链路走通,里面值得抠的细节非常多。文章会结合一套附源码的学生成绩管理系统的真实设计过程来讲,你拿到手既能看懂设计思路,也能直接把项目跑起来,更知道每一步为什么要这么设计。

这套系统的核心业务很简单——学校或者培训机构需要把学生信息、课程信息、考试成绩统一管起来。最传统的做法是教务老师用Excel登记,期末的时候一份一份汇总,遇到补考、重修、调课,表格改来改去,数据经常对不上。换成系统之后,管理员维护基础数据,任课老师录入成绩,学生查自己的分数和排名,每个角色各干各的事,权限边界清楚,数据也只有一个来源,不会出现三个老师手里有三个版本的成绩单。

项目技术栈采用的是Java体系下非常经典的SSM组合,Spring管理对象、Spring MVC处理请求、MyBatis操作数据库,前端用JSP加Bootstrap渲染页面。这套组合到今天依然有大量存量项目在跑,而且对新手特别友好,框架的每一层都能看得很清楚,不像Spring Boot把很多配置自动完成之后,反而不知道背后发生了什么。文章后面我也会单独说一句,怎么把这套项目平滑升级到Spring Boot版。

1. 项目整体定位与需求拆解

1.1 这个系统到底要解决什么问题

拿到需求之后先别急着写代码,先想清楚使用这个系统的三类人分别要什么。

教务管理员最痛的是Excel管理方式下的数据孤岛和误操作。一个学生的学籍变动,比如休学、复学、转班,需要在学生表、班级表、成绩表里同时改,手改很容易漏。系统里的管理员端要能统一维护学生、教师、课程和班级数据,并且这些数据变动要实时反映到关联表中。

任课老师期末考试结束之后,课程组最常做的事情是把成绩单导来导去。有的老师习惯用百分制,有的老师习惯用A/B/C/D等级制,汇总到教务那里又要换算。系统要支持成绩录入、修改、批量导入、按班级查看,最好能自动把人数的分布按分数段统计出来,挂科率高的课程直接预警,方便教学组复盘。

学生的诉求最简单,考完试能查到成绩、能看到自己在班级里的排名,补考通知能及时看到。所以在做学生端的时候,不需要让页面多华丽,入口清晰、查询响应快最重要。

这几点合在一起,就构成了系统的功能边界:基础信息管理、成绩管理、统计查询,外加每个角色分开的登录入口和操作范围限制。

1.2 功能清单与角色权限设计

功能不要贪多,把每个模块做扎实比堆功能有价值得多。这套项目里的权限设计方式是经典的“单表用户 + 角色字段”,也就是一张用户表存账号密码和角色类型,登录之后由后端拦截器判断当前角色能访问哪些URL。

具体功能矩阵如下表所示:

功能模块管理员教师学生
登录/退出是是是
学生信息管理增删改查只读只读(仅本人)
教师信息管理增删改查无无
课程信息管理增删改查只读只读
班级信息管理增删改查只读只读
成绩录入/修改全部操作仅本人授课课程无
成绩查询全部所授课程仅本人
成绩统计分析全部所授课程本人成绩排名
修改密码是是是

角色权限表设计好之后,后面写拦截器的时候就是照着这个表来配URL白名单。

1.3 非功能需求,答辩和文档里容易被问到的点

很多人在做系统的时候只盯着功能,忽略了非功能需求,结果答辩的时候被老师一个问题问住就尴尬了。这套系统在非功能层面主要考虑四点。

性能方面,单个学校的数据量一般不会超过几万条,属于典型的小体量项目,所以不需要引入Redis这种缓存中间件,MySQL的普通查询加索引就够了,而且MyBatis自带的一级缓存和Spring的声明式事务在并发量不大的场景下完全扛得住。

安全方面,用户的明文密码一定不能直接存数据库,要用MD5加盐方式加密存储。同时要防止SQL注入,MyBatis里能用#{}就不要用${},这条规则要在代码审计的时候重点检查。

兼容性方面,前端用Bootstrap本身就是响应式框架,Chrome、Firefox、Edge这些主流浏览器渲染基本没问题。有一点要注意,JSP页面里的JS代码浏览器兼容性容易翻车,尤其是一些老版本的IE,所以我们一般会在页面底部加一个jquery.min.js的压缩版本固定住版本号,不要随便升级,避免一个升级把页面全部搞挂。

可维护性方面,后端的包结构严格按controller、service、dao、entity分层,每个层只做自己的事。这个在答辩的时候经常被问到,你要能解释清楚为什么Controller里不能直接写JDBC代码。

2. 技术选型:为什么选SSM

2.1 技术栈总览

后端:Spring 5.x,Spring MVC 5.x,MyBatis 3.5.x。数据库:MySQL 8.x。前端:JSP 2.3 + Bootstrap 4.x + jQuery 3.x。构建工具:Maven 3.6+。服务器:Tomcat 8.5/9.x。JDK版本:1.8,这是整个组合最稳定的搭配。

Spring负责把各个对象之间的关系管理起来,比如说Service层要调Dao层,不需要自己new,Spring容器自动注入。Spring MVC负责接收浏览器请求,把URL映射到具体的Controller方法上。MyBatis负责把Java对象和数据库表记录互相映射,你在Mapper.xml里写SQL,MyBatis帮你执行并把结果集转成对象。

这三个框架合在一起,就是非常经典的Java Web后端开发组合,到今天仍然大量存在于企业遗留系统中。

2.2 Spring Boot化改造:学过SSM之后下一步怎么走

如果实际开发环境不是学校里的课设要求,我会建议你学完这套SSM之后,尽快把项目改成Spring Boot版本。

Spring Boot本质上是对Spring家族的封装,它把以前Spring和Spring MVC里那一大堆XML配置全部用自动配置替代了。你可以对比着看,同样一个项目,SSM版可能要五六个配置文件,Spring Boot版一个application.yml就把数据源、MyBatis、端口、日志全部搞定。

改造的时候记住几个核心点。原来的web.xml换成Spring Boot里的WebMvcConfigurer,用来注册拦截器和静态资源映射。原来的spring-mvc.xml里的组件扫描,改为在启动类上加@SpringBootApplication,它会自动扫描当前包及其子包。原来的sqlMapConfig.xml里的MyBatis配置,改成在application.yml里写mybatis.mapper-locations: classpath:mapper/*.xml。

具体可以参考下方关键配置:

server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/student_score?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&useSSL=false username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver mybatis: mapper-locations: classpath:mapper/*.xml type-aliases-package: com.school.entity logging: level: com.school.dao: debug
2.3 分层架构设计的理由

很多新手写代码喜欢把所有逻辑都堆在Servlet或者Controller里,这样写项目做出来也能跑,但后续只要加一个小功能就得把整个类重新看一遍,改一个字段可能在好几个地方都要动。

这套系统严格分了四层。Controller层只负责接收参数和返回视图,参数校验放在这一层做最简单,比如成绩的分数范围校验。Service层负责业务规则,比如学生修改成绩是否受限、删除学生的同时要删除关联成绩记录,这些事务逻辑都在这里,@Transactional注解也标在Service方法上。Dao层每一个方法对应Mapper.xml里的一条SQL,是纯数据访问,不做任何业务判断。Entity层是数据库表的Java映射,字段和表结构一一对应。

分层最大的好处是出了bug能很快定位在哪一层。页面参数拿不到,查Controller;业务逻辑不对,查Service;SQL写错,查Mapper.xml。我刚工作的时候带过一个新人,他把SQL查询逻辑全部写在JSP页面里,改一个需求全项目到处报错,这就是谁破坏分层谁就要还债的典型案例。

3. 数据库设计:这个项目的重中之重

3.1 E-R模型与设计思路

数据库设计是整个项目里最值得花时间的地方。如果表设计不合理,后面写SQL会非常痛苦。这套系统的实体包括学生、教师、课程、班级、成绩、用户,它们之间的关系如下:学生属于班级,一个班级有多个学生,是一个一对多关系;教师和课程是授课关系,一个教师可以教多门课,一门课也可能由多个老师教(不同班级),所以这里实际上也是一个多对多关系,需要有一张关联表;学生和课程通过成绩关联,成绩表本质上是学生和课程之间的关联表,同时带上考试信息和分数数据。

实体关系确定好后,就可以开始建表。这里我强烈建议画一张E-R图再动手写SQL,尤其在答辩的时候,E-R图是基本必问项。画图工具可以用ProcessOn、draw.io或者Navicat自带的模型功能,重在把主外键关系标清楚,不需要画得多精美。

3.2 建表SQL与核心字段说明

下面我给出核心表结构的参考SQL,字段命名采用下划线风格,Java实体类里用驼峰命名,MyBatis配置打开mapUnderscoreToCamelCase之后自动映射。

-- 学生表 CREATE TABLE student ( id INT PRIMARY KEY AUTO_INCREMENT COMMENT '主键', student_no VARCHAR(20) NOT NULL UNIQUE COMMENT '学号', name VARCHAR(50) NOT NULL COMMENT '姓名', gender TINYINT DEFAULT 1 COMMENT '性别 1男 0女', birthday DATE COMMENT '出生日期', class_id INT COMMENT '所属班级ID', phone VARCHAR(20) COMMENT '联系电话', create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间' ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- 教师表 CREATE TABLE teacher ( id INT PRIMARY KEY AUTO_INCREMENT, teacher_no VARCHAR(20) NOT NULL UNIQUE COMMENT '教师工号', name VARCHAR(50) NOT NULL, title VARCHAR(20) COMMENT '职称', phone VARCHAR(20) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- 课程表 CREATE TABLE course ( id INT PRIMARY KEY AUTO_INCREMENT, course_no VARCHAR(20) NOT NULL UNIQUE COMMENT '课程编号', course_name VARCHAR(100) NOT NULL, credit DECIMAL(3,1) DEFAULT 2.0 COMMENT '学分', teacher_id INT COMMENT '任课教师ID' ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- 班级表 CREATE TABLE class_info ( id INT PRIMARY KEY AUTO_INCREMENT, class_name VARCHAR(50) NOT NULL, grade VARCHAR(10) COMMENT '年级', major VARCHAR(50) COMMENT '专业' ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- 成绩表 CREATE TABLE score ( id INT PRIMARY KEY AUTO_INCREMENT, student_id INT NOT NULL, course_id INT NOT NULL, exam_name VARCHAR(50) COMMENT '考试名称,如2024年期末考试', score DECIMAL(5,1) COMMENT '百分制成绩', remark VARCHAR(200) COMMENT '备注', UNIQUE KEY uk_stu_course_exam (student_id, course_id, exam_name) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
3.3 成绩表设计里的几个关键约定

成绩表里有一个容易忽略的细节,就是UNIQUE KEY uk_stu_course_exam,它是针对“一个学生同一门课同一场考试只能有一条成绩”这个业务规则的数据库层保证。如果不加这个约束,程序里万一漏写了重复判断,就会出现同一个学生同一门考试录了两遍甚至更多遍。加了唯一键之后,重复插入会直接报错,程序里捕获到重复键异常,再转成“成绩已存在”的友好提示即可。

另外exam_name字段我建议单独设计而不是直接写死在程序里,好处是系统可以支持多次考试记录。一个学期有期中、期末、补考,每次都是独立的记录,统计的时候通过exam_name做条件过滤即可。再往上一层做扩展,可以拆出一张考试表exam,字段包括考试名称、考试类型、考试时间,然后成绩表里存exam_id,这在数据库层面更规范。实际课设项目用exam_name字符串也够,我见过很多正式系统也这么干,不丢人。

score字段类型用DECIMAL(5,1),支持最大999.9分,保留1位小数。有人习惯用INT,这会导致录入成绩时小数全部被截断,比如87.5录进去变成87,等到期末排名时一分之差排名差好几名,学生来找你理论就很尴尬了。所以这里坚决用DECIMAL。

4. 核心功能实现与关键代码

4.1 登录认证与权限拦截

登录逻辑看着简单,但这里涉及整个系统的安全门槛。密码存储一律使用MD5加盐的方式,数据库中不存明文。加盐是个简单的做法,就是在原始密码后面拼一个固定字符串再做哈希,避免用户弱密码直接撞库被破。如果项目后续要上线到生产环境,建议再把MD5升级为BCrypt,这里为了课设演示方便就先用MD5。

登录成功之后,把用户ID、用户名、角色放到Session里,同时记录最后登录时间。退出的时候调用session.invalidate()把会话销毁,防止别人用同一台电脑退掉之后还能通过浏览器的会话记录访问本人数据。

Spring MVC的拦截器是实现URL权限控制最干净的方式。自定义一个LoginInterceptor,实现HandlerInterceptor接口,在preHandle方法里校验Session。如果没有登录,直接重定向到登录页,如果登录了但是访问了角色不允许的URL,跳转到一个403提示页面。

public class LoginInterceptor implements HandlerInterceptor { private static final List<String> ADMIN_URLS = Arrays.asList("/admin/**", "/user/delete", "/user/add"); @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { Object loginUser = request.getSession().getAttribute("loginUser"); if (loginUser == null) { response.sendRedirect(request.getContextPath() + "/login.jsp"); return false; } // 简单角色判断示例 if (request.getRequestURI().contains("/admin/")) { return "1".equals(((User) loginUser).getRole()); } return true; } }

在Spring MVC配置文件中注册拦截器,注意放行登录接口和静态资源。有一个很常见的坑,就是页面上的CSS和JS因为被拦截器拦截导致样式全部失效,大部分情况下都是注册拦截器时把静态资源的映射路径给拦掉了。正确做法是excludePathPatterns里把/static/**、/css/**、/js/**、/images/**全部放行。

4.2 成绩录入、修改与批量管理

成绩管理是整个系统的核心业务。录入成绩的时候,页面需要根据当前登录的教师用户查询出他所教的课程列表,然后选择课程之后加载该课程对应的学生列表,再逐个录入分数。这里要注意性能问题,一个班四五十人,用Ajax一次加载全部学生没有问题,但如果一个课堂上百人,就要考虑分页查询和键盘输入提交,减少页面卡顿感。

录入数据在提交到后端的时候,用List<Score>作为参数接收,Controller的方法签名可以写成接收JSON数组或表单参数列表。MyBatis批量插入使用<foreach>标签,一次性插入,避免循环单条执行SQL,性能提升非常明显。

<insert id="batchInsertScore" parameterType="java.util.List"> INSERT INTO score(student_id, course_id, exam_name, score, remark) VALUES <foreach collection="list" item="item" separator=","> (#{item.studentId}, #{item.courseId}, #{item.examName}, #{item.score}, #{item.remark}) </foreach> </insert>

成绩修改的场景多发生在录入错误或者学生申请复核之后。修改时要判断当前登录用户是否有权限修改这门课成绩,管理员可以改任何课,教师只能改自己教的课。这里的事务边界要注意,更新成绩的同时可以考虑记录一条操作日志,包括操作人、时间、旧值、新值,方便追溯。虽然课设没硬性要求,但加上这个会显得思路很完整。

4.3 成绩统计与可视化展示

成绩录进去之后,统计报表才是亮点功能。至少要包含平均分、最高分、最低分、及格率、优秀率、分数段分布这几项。

平均分和及格率一条SQL就能查出来:

SELECT COUNT(*) AS total_count, ROUND(AVG(score), 2) AS avg_score, MAX(score) AS max_score, MIN(score) AS min_score, ROUND(SUM(CASE WHEN score >= 60 THEN 1 ELSE 0 END) / COUNT(*) * 100, 2) AS pass_rate, ROUND(SUM(CASE WHEN score >= 90 THEN 1 ELSE 0 END) / COUNT(*) * 100, 2) AS excellent_rate FROM score WHERE course_id = #{courseId} AND exam_name = #{examName}

分数段分布使用分组统计,比如0-59、60-69、70-79、80-89、90-100:

SELECT CASE WHEN score < 60 THEN '不及格' WHEN score < 70 THEN '60-69' WHEN score < 80 THEN '70-79' WHEN score < 90 THEN '80-89' ELSE '90-100' END AS score_range, COUNT(*) AS count FROM score WHERE course_id = #{courseId} AND exam_name = #{examName} GROUP BY score_range ORDER BY MIN(score)

前端展示的时候,把统计结果渲染成ECharts柱状图或者饼图。ECharts是最常见的图表库,JSP引入ECharts的CDN之后,在页面加载时把后端返回的JSON数据塞进去就能画出来。注意JSP往JS里传数据的时候一定要用JSON.toJSONString先序列化,否则很容易出现字符串拼接错误。

学生端查询成绩时,除了展示各科分数,还可以查自己在班级里的排名。排名SQL使用窗口函数,MySQL 8.0已经支持:

SELECT student_id, score, RANK() OVER (ORDER BY score DESC) AS rank_no FROM score WHERE course_id = #{courseId}

MySQL 5.7及以下版本不支持窗口函数,需要改用用户变量模拟,写法会绕一些。所以项目如果用的是MySQL 5.7,这里我建议直接用ORDER BY score DESC查询出来后端计算排名,反正数据量不大,完全没有必要为了用窗口函数去控制SQL难度。

4.4 分页查询:一个细节决定体验好坏

学生、成绩等列表页面,数据量大了之后必须分页。传统方式是手写LIMIT语句,LIMIT #{offset}, #{pageSize},再单独查一条SELECT COUNT(*)。这没有问题,但代码里要记得每次页面上点击页码的时候把当前页码、每页条数、查询条件三个参数一起传给后端。

如果不想手写分页,可以引入PageHelper插件,它是MyBatis的分页插件,使用方式极简。在Mapper查询前调用PageHelper.startPage(pageNum, pageSize),紧接着的一条查询会自动变成带LIMIT的语句,返回的结果用PageInfo包装就能拿到总条数、总页数、当前页数据。

不过我个人建议做课设的时候先自己手写一遍分页SQL,搞清楚LIMIT和COUNT的原理,再去用PageHelper,这样写起来心里有底,答辩的时候被问到底层原理也答得出来。

5. 拿到源码之后怎么跑起来

5.1 项目结构说明

这套附源码项目的目录结构按照Maven标准布局组织,核心部分如下。

student-score-system ├── pom.xml ├── src │ ├── main │ │ ├── java │ │ │ └── com.school │ │ │ ├── controller │ │ │ ├── service │ │ │ ├── dao │ │ │ ├── entity │ │ │ ├── interceptor │ │ │ └── util │ │ ├── resources │ │ │ ├── mappers │ │ │ ├── jdbc.properties │ │ │ ├── spring-context.xml │ │ │ ├── spring-mvc.xml │ │ │ └── mybatis-config.xml │ │ └── webapp │ │ ├── static │ │ ├── WEB-INF │ │ └── login.jsp

拿到源码以后,不要急于启动,先按下面的步骤把环境准备齐全。

5.2 本地IDEA启动步骤

前提条件:JDK 1.8、Maven 3.6+、MySQL 8.x、Tomcat 8.5+、IDEA集成开发环境。

第一步,用IDEA的Open功能打开项目,选择pom.xml,等待Maven下载依赖。建议Maven配置阿里云镜像,否则依赖下载速度会让人崩溃。

第二步,创建数据库student_score,然后把项目里的student_score.sql脚本导入,初始化表结构和测试数据。

第三步,修改数据库连接配置。在jdbc.properties或者application.yml中修改用户名和密码,特别关注URL中的serverTimezone=Asia/Shanghai这个参数。MySQL 8.0的驱动类名是com.mysql.cj.jdbc.Driver,如果写成com.mysql.jdbc.Driver会报错,原因是新驱动类名改了。这个坑很经典,很多人第一次跑MySQL 8项目都卡在这。

第四步,配置Tomcat。在IDEA里添加Tomcat Server,Deployment选war exploded,Application context建议设置成/,这样访问的时候直接http://localhost:8080就能到登录页,不用带项目名。

第五步,启动Tomcat。如果能正常看到登录页,并且用默认账号登录成功,说明环境已经通了。默认账号一般是管理员admin/admin123,具体以源码里的SQL脚本为准。

5.3 浏览器兼容性层面的准备工作

项目里用了Bootstrap 4和jQuery,启动之后建议同时在Chrome、Edge、Firefox里各打开一次页面,重点看表格的排列、弹窗的显示、下拉框的样式。Bootstrap 4对现代浏览器支持已经非常完善,通常不会有什么问题。真正容易翻车的是页面编码,如果出现中文乱码,检查JSP文件头部是否设置了pageEncoding="UTF-8",还有Tomcat连接器是否配置了URIEncoding="UTF-8"。

<Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" URIEncoding="UTF-8" />

这个URIEncoding很重要,不改的话,URL中带中文参数的GET请求会出现乱码。虽然在现代Spring MVC里可以通过配置CharacterEncodingFilter解决POST乱码,但GET请求的中文参数必须靠这个连接器配置。

6. 常见问题与排错实录

6.1 数据库连接失败的N种情况

数据库连不上是出现频率最高的启动错误,典型症状就是Tomcat启动日志里报Cannot create PoolableConnectionFactory或者Access denied for user。遇到这种问题,排查顺序固定为三步。

先看MySQL服务是否启动。Windows下打开服务管理器,找到MySQL服务确认状态,Linux下用systemctl status mysql。然后看账号密码和权限,用命令行mysql -uroot -p手动连一次,能够连上就排除账号问题。最后看URL配置,useSSL=false不要去掉,serverTimezone=Asia/Shanghai不能丢,MySQL 8的驱动类名必须是com.mysql.cj.jdbc.Driver。

如果报错信息里有Public Key Retrieval is not allowed,在JDBC URL里加allowPublicKeyRetrieval=true,这是MySQL 8的认证方式导致的常见问题。

6.2 Maven依赖下载失败或太慢

用Maven构建项目时,依赖下载失败通常表现为项目报错,IDEA里提示找不到org.springframework相关的包。最直接的原因是本地仓库里没有这个依赖,中央仓库又访问超时。解决办法是在settings.xml里配置阿里云镜像:

<mirror> <id>aliyunmaven</id> <mirrorOf>central</mirrorOf> <name>阿里云公共仓库</name> <url>https://maven.aliyun.com/repository/public</url> </mirror>

镜像配置好后,在IDEA里执行mvn clean,等待依赖全部下载完毕。

6.3 JSP页面改了代码不生效

JSP页面修改后需要重新部署才会生效。如果你在IDEA里直接修改JSP然后刷新浏览器,看到的内容还是旧版本,通常是部署模式没有选对。Tomcat配置里选择war exploded模式开发调试,选中Deploy的Update resources选项,修改JSP的时候IDEA会热更新资源,不需要重启Tomcat。

Java代码的修改则需要重启,IDEA自带Update classes and resources功能,可以只重编译修改的类,不需要重启整个Tomcat,但遇到新增方法或修改方法签名的时候还是重启最稳妥。

6.4 404和500状态码的排查思路

页面访问返回404,先看URL和Controller里的@RequestMapping路径是否完全一致,包括大小写,/admin和/Admin是两个完全不同的路径。

再检查IDEA里Project Structure的Artifacts配置,如果缺少lib目录下的依赖就会导致访问时ClassNotFoundException,表现形式是页面500,控制台报错。

如果遇到NoClassDefFoundError,多数是Maven依赖冲突或者scope配置错误。比如Servlet API的依赖scope被配成provided,在本地Tomcat运行没问题,但打包部署到其他环境就报类找不到。

6.5 登录成功后页面跳回登录页

这个问题的根源在Session失效或Cookie未保存。检查登录成功后是否调用了request.getSession().setAttribute保存用户信息,拦截器里是否对登录URL放行,浏览器是否禁用了Cookie。

还有一个非常隐蔽的坑,IDEA里的Tomcat每次重启后重新部署,Session会失效,这是正常现象,不要以为是代码bug。另外Application context设置成/abc之后,页面跳转的路径如果写死成/login开头,访问的时候都得带上项目名,所以建议统一用request.getContextPath()拼接路径。

7. 项目准备好之后,可继续扩展的方向

如果论文或答辩需要展示系统的亮点,不要满足于基础的增删改查,以下三个方向可以选一个落地。

第一个方向是Excel导入导出。任课老师的成绩单通常都是Excel格式,系统内提供导入模板下载,老师填完数据直接上传,后端用Apache POI解析Excel并批量写入数据库。导出功能适合管理员期末成绩汇总,一键导出全年级成绩表。

第二方向是成绩趋势分析,在已有统计的基础上,把某个学生历次考试的成绩变化趋势用折线图展示出来。这需要成绩表里把exam_name的数据规范化,每次考试的数据都打上同一个学生的标签,查询时GROUP BY exam_name即可。这个功能看着不起眼,但其实特别受班主任欢迎。

第三个方向是数据可视化大屏,把全校的及格率、优秀率、各班平均分、分数段分布全部集中到了一个页面,用ECharts做滚动图、柱状图、地图或者仪表盘。前端页面用div布局隔成几个区域,后端定时刷新数据,效果相当惊艳,答辩的时候一眼就能镇住场子。

我个人实际操作中的体会是,很多学生拿到源码第一反应是去跑起来,跑通了就觉得任务完成了。但实际上这套系统真正值钱的地方在数据库设计和权限控制的思路上,把这些吃透了,面试官问你项目的时候你才答得出“为什么这么设计”。另外有个小技巧,项目跑通之后,去源码里把Controller、Service、Dao三层之间每个方法的调用路径捋一遍,画在一张纸上,三天之后再默写一遍,这个过程对整个Java Web开发的理解非常有帮助。你照着这套思路把项目过一遍,能学到的绝对不止一个课设分数。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/29 1:32:58

APP自动化测试工程化实践:从设备选型到CI/CD闭环

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/29 1:32:56

RS232保护方案:三层纵深防护设计与实操要点

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/29 1:32:56

BT8022二维码扫枪+modbus+io实践教程

一、功能篇二、接线篇1、扫码枪坐下接水晶头网口2、水晶头线另一端USB输出 另外一个线的USB接入3、圆孔的是电源三、使用篇3.1、参数设置这张使用说明书&#xff0c;只需要扫①扫码开始设置条码&#xff0c;启动设置②恢复默认值&#xff08;默认9600 8 N 1 设备ID:1)③ 若…

作者头像 李华
网站建设 2026/9/29 1:32:17

进程与线程核心机制全解析:从原理到并发编程实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/29 1:31:01

强化学习入门:贝尔曼方程、DQN与经验回放实战拆解

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华