简介:基于Java与MySQL实现的大学生学籍管理系统毕业设计资料,内含完整源代码与配套报告。项目围绕学生、班级、院系、专业、课程、选课、成绩、奖惩等核心模块展开,并借助视图、存储过程和触发器完善数据库逻辑:视图用于查询学生学号、姓名、班级、专业与院系,存储过程输出指定学生成绩单,触发器在增删学生或修改班级时自动更新对应人数,同时建立了表间参照完整性约束。资料包共203个文件,包括Java源码、编译后的class字节码、SQL脚本、Eclipse工程配置、JAR依赖以及DOC文档,压缩包约1.91MB,便于直接查看或二次开发。已有2434人学习下载,适合作为毕业设计参考,也适合希望掌握学籍管理业务与Java/MySQL数据库编程的初学者。整体结构清晰,代码注释完整,可直接导入Eclipse运行。
1. 学籍管理系统这个 Java 毕设题目,为什么最稳也最容易翻车
高校计算机类的毕业设计里,基于 Java 的大学生学籍管理系统几乎是每年都会出现的题目。表面上看,它只是学生信息、成绩、班级的增删改查,实际上要打通的是 Java 后端、MySQL 表结构、登录权限、统计报表一整条链路。这个题目性价比很高:功能边界清楚、工作量可控、答辩时每个模块都能讲到代码级别,所以很多选方向犹豫的人最后都会落到它头上。
但翻车率同样不低。最常见的情况是代码跑通一遍就丢到一边,数据库乱建外键,密码明文存,论文里贴的截图和最终运行的页面根本不是一回事。答辩老师只要点开一个你没演示过的菜单,或者让你现场改一个查询条件,问题立刻就暴露了。这个题目真正适合的,是愿意花一个月把每个类、每张表、每条 SQL 都吃透的人。下文按一套完整作品的交付顺序,讲清楚代码怎么写、数据库怎么设计、报告怎么组织,以及那些会让你当场翻车的坑。
2. 需求与技术选型:角色不拆清,后面全是返工
大部分选这个题的同学,需求写得特别模糊,一句“实现学生信息管理”就交上去了。这句话放到答辩台上只能得基础分。我希望你在写第一行代码之前,先把角色和用例拆好,这决定后面数据库和接口会不会做歪。
2.1 三类角色与用例边界:管理员、辅导员、学生分别能做什么
学籍管理系统的核心不是增删改查,而是谁有权限对哪些数据做什么操作。以一个普通本科院校的规模来设定,三类角色就够了。
| 角色 | 数据范围 | 核心操作 | 典型场景 |
|---|---|---|---|
| 教务管理员 | 全校 | 维护院系、专业、班级、课程,分配用户,查看全局统计 | 新生入学建学籍、毕业资格核算 |
| 辅导员 | 本班或本专业 | 录入成绩、维护奖惩记录、修改学生联系方式 | 学期成绩录入、学生变动登记 |
| 学生 | 本人 | 查看学籍卡、成绩单、课程信息 | 在校期间随时查阅 |
答辩老师最爱问的一句话是:“学生能不能直接绕过菜单访问后台接口?”所以系统里必须存在一个统一的登录校验机制,不管用拦截器还是过滤器,你得能在代码层面回答这个问题。另外要注意,学生看成绩的接口和辅导员录成绩的接口不能是同一个 URL 权限,这是后面设计 Controller 时最容易忽略的分层点。
2.2 技术选型对比:Spring Boot 2.7 + MyBatis + MySQL 8.0 为什么是首选
这个题目的技术路线一般有三条:JSP+Servlet+JDBC、Spring Boot + MyBatis + MySQL、以及基于 Swing 的桌面端。第一条能讲清楚底层原理,但代码量巨大,Session 管理、DAO 封装、分页都要手写,一个月时间大概率不够;第三条界面简单、逻辑直观,更适合 JavaSE 课程设计,作为毕业设计显得单薄。多数人的选择是第二条,原因有三点。
其一,Spring Boot 让配置变得很薄,一个 application.yml 就能管理数据源和连接参数,你可以在报告里明确写“本项目使用 Spring Boot 搭建基础框架”。其二,MyBatis 的 SQL 是手写的,答辩时你可以直接贴一条查询出来讲,比 JPA 自动生成的复杂语句好解释得多。其三,MySQL 是 Java 面试里的高频话题,毕业设计用过 MySQL 和事务,后面面试聊项目时至少有一个能打的案例。
版本上我建议:JDK 8 + Spring Boot 2.7.x + MyBatis 3.5.x + MySQL 8.0。这套组合经过大量项目验证,网上资料最全,单个问题几乎都能搜到现成解决方案。别追新,Spring Boot 3 系列要求 JDK 17,如果老师电脑上还是 JDK 8,项目会在最后交付时出大问题。
2.3 八个功能模块的拆分:哪些模块共用一张表,哪些必须独立
毕设评分看的是完成度和可讲性,不是功能数量。我一般会把范围卡在八个模块:登录认证、学生信息管理、班级管理、院系与专业管理、课程管理、成绩管理、奖惩记录、统计报表。
前七个很好理解,最后一个是答辩加分项。统计报表不需要做复杂图表,一个“按院系统计学生人数”“按班级统计平均成绩”的列表页加几行聚合 SQL 就够用。大多数学生做的系统里根本没有这种统计查询,你放上去,完成度就会显得高出一截。功能边界要写进报告的需求分析里,每项功能对应哪张表、哪个 Controller、哪个页面,做到心里有数。后面会给出表结构,你会发现八个模块刚好对应十来张表的组合,不会多到失控。
3. 数据库设计:九张表五个外键,先把核心结构定死
数据库是整个系统里最不能返工的部分。代码写错了可以改,表结构要是错了,Mapper、Service、页面、报告全文都得跟着改。我建议你在写 Java 代码之前,把所有字段理清,写到一个 init.sql 脚本里,在 MySQL 8.0 上测试通过再动手。
3.1 院系-专业-班级三张表:外键与唯一约束的一次到位写法
先建最上层的组织架构,不要一上来就建学生表。
CREATE TABLE t_department ( dept_id INT PRIMARY KEY AUTO_INCREMENT, dept_name VARCHAR(50) NOT NULL UNIQUE, create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE t_major ( major_id INT PRIMARY KEY AUTO_INCREMENT, major_name VARCHAR(50) NOT NULL, dept_id INT NOT NULL, CONSTRAINT fk_major_dept FOREIGN KEY (dept_id) REFERENCES t_department(dept_id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE t_class ( class_id INT PRIMARY KEY AUTO_INCREMENT, class_name VARCHAR(50) NOT NULL, major_id INT NOT NULL, grade_year VARCHAR(20) COMMENT '入学年份,例如2024级', CONSTRAINT fk_class_major FOREIGN KEY (major_id) REFERENCES t_major(major_id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;这三张表体现的是“院系—专业—班级”的层级。学生表里只保存 class_id,不保存院系和专业字段,这是第三范式的标准做法,答辩时可以讲出设计理由。UNIQUE 约束加在 dept_name 上,防止同一个院系被录入两次。主键一律用 INT 自增,别用 VARCHAAR 做主键,字符串主键在连表和分页时性能更差,答辩老师追问时很容易露怯。每张表都显式指定 InnoDB 和 utf8mb4,否则后续改引擎和字符集的成本全在你自己身上。
3.2 学生表字段设计:学号唯一、身份证脱敏、状态字段两个取值
学生表是整个系统的核心,字段设计会直接影响成绩表、奖惩表和用户表的写法。
CREATE TABLE t_student ( student_id INT PRIMARY KEY AUTO_INCREMENT, student_no VARCHAR(20) NOT NULL UNIQUE, student_name VARCHAR(50) NOT NULL, gender CHAR(1) DEFAULT '男', birthday DATE, id_card VARCHAR(18), phone VARCHAR(11), class_id INT NOT NULL, enroll_date DATE, status TINYINT DEFAULT 1 COMMENT '1在读 0离校 2休学', CONSTRAINT fk_student_class FOREIGN KEY (class_id) REFERENCES t_class(class_id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;student_no 是学号,系统里所有业务都靠它区分同名同姓的人,所以直接加 UNIQUE。id_card 属于敏感字段,按目前高校对个人信息保护的要求,最好加密存储,列表页只展示后四位。你可以用 AES 工具类加密后入库,这是答辩加分项;实在不想做加密,至少要能解释为什么不直接明文展示。status 字段建议用 TINYINT 而不是 VARCHAR,因为状态需要参与条件查询,数字比中文串更稳定。
3.3 课程与成绩表:唯一索引 uk_student_course_semester 的用途
课程表和成绩表是另一个容易出错的位置。很多初学者会把成绩设计成学生表里的一个字段,或者把每门课变成一列,这是最典型的反模式。正确做法是课程表独立,成绩表用唯一索引限制“同一学生同一课程同一学期只能有一条记录”。
CREATE TABLE t_course ( course_id INT PRIMARY KEY AUTO_INCREMENT, course_name VARCHAR(50) NOT NULL, credit DECIMAL(3,1) NOT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE t_score ( score_id INT PRIMARY KEY AUTO_INCREMENT, student_id INT NOT NULL, course_id INT NOT NULL, semester VARCHAR(20) NOT NULL COMMENT '例如2024-2025-1', score DECIMAL(5,1), UNIQUE KEY uk_student_course_semester (student_id, course_id, semester), CONSTRAINT fk_score_student FOREIGN KEY (student_id) REFERENCES t_student(student_id), CONSTRAINT fk_score_course FOREIGN KEY (course_id) REFERENCES t_course(course_id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;这里最关键的参数是 uk_student_course_semester 这个复合唯一索引。没有它,同一个学生同一门课可以被录两次,成绩就乱套了。score 字段用 DECIMAL(5,1) 而不是 INT,是因为成绩可能有 89.5 这种带小数的形式。semester 字段建议用“2024-2025-1”这种格式统一表示,比单独存一个年份和学期号更直观。
3.4 用户表与角色字段:密码字段长度为什么设置成 100
用户表不要跟学生表混在一起,学籍信息是业务数据,账号和密码是认证数据,拆开才能保证一个学生毕业后账号仍然可以保留或注销。
CREATE TABLE t_user ( user_id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(100) NOT NULL, role VARCHAR(20) NOT NULL COMMENT 'admin/teacher/student', ref_id VARCHAR(20) COMMENT '关联学生表或教师的工号' ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;password 字段必须设置成 VARCHAR(100),不是因为密码本身有 100 位,而是因为 MD5 加密结果固定 32 位、BCrypt 加密结果固定 60 位,如果按“最长密码 20 位”去设计字段长度,加密后根本存不下。就用 BCrypt,这是当前最稳妥的密码哈希方案。role 字段为什么不用三张 RBAC 表?因为本系统的菜单是固定的,只有三类角色,不需要动态分配菜单和按钮权限,一张表一个字段就够。报告里可以写一句“基于 RBAC 模型简化实现”,老师再追问时你再讲清楚角色菜单的扩展方向。
4. 后端代码落地:从配置、登录到分页查询的关键片段
数据库定了之后,后端代码的骨架基本就定了。下面按一个最小可运行项目的顺序,从依赖配置到分页查询,依次给出可以直接照搬的写法。
4.1 Maven 依赖与 application.yml:三个必调参数(时区、编码、驼峰映射)
先给 pom.xml 里的关键依赖。手动搭项目时直接加这几项,不要自己引一堆其他包。
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.7.18</version> </parent> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.3.2</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.33</version> </dependency> <dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper-spring-boot-starter</artifactId> <version>1.4.7</version> </dependency> </dependencies>然后是 application.yml。这是整个项目配置的中心,三个参数最容易出问题。
server: port: 8080 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/student_system?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai username: root password: 123456 thymeleaf: cache: false mybatis: mapper-locations: classpath:mapper/*.xml configuration: map-underscore-to-camel-case: trueurl 里有三个必须调对的参数:useUnicode=true 和 characterEncoding=utf8 保证中文能正确传输,serverTimezone=Asia/Shanghai 解决 MySQL 8.0 时区报错。map-underscore-to-camel-case 尤为重要,它让数据库的 student_name 自动映射到 Java 实体类的 studentName,否则你只能在 XML 里给每个字段写别名。这几个配置项也是 Java 面试里数据源部分的常见问题,答辩时被问到能答出来。
4.2 实体类与 Mapper:用 where 动态 SQL 完成学籍条件查询
实体类字段名按驼峰写,与数据库下划线字段对应。
public class Student { private Integer studentId; private String studentNo; private String studentName; private Integer classId; private String gender; private Date birthday; private Integer status; // 生成 getter/setter,或直接用 Lombok 的 @Data }Mapper 接口写一个带条件的分页查询方法。MyBatis 的 XML 里用<where>和<if>组合,是最常用的条件查询套路。
<select id="selectStudentPage" resultType="com.example.entity.Student"> SELECT s.student_id, s.student_no, s.student_name, s.gender, s.class_id, s.status FROM t_student s <where> <if test="studentName != null and studentName != ''"> AND s.student_name LIKE CONCAT('%', #{studentName}, '%') </if> <if test="classId != null"> AND s.class_id = #{classId} </if> <if test="status != null"> AND s.status = #{status} </if> </where> ORDER BY s.student_id DESC </select>注意这里几个细节。LIKE 拼接用的是 CONCAT('%', #{studentName}, '%'),不要写成 '%${studentName}%',后者会引入 SQL 注入风险,用 ${} 拼字符串在答辩时属于严重扣分点。<where>标签会自动处理第一个条件前面的 AND,写不写都可以,但不能漏掉<if>里的!= null and != ''判断,否则空字符串也会拼进 SQL。
4.3 Service 层事务:@Transactional 的参数学籍删除怎么保证数据一致性
学籍管理系统里最常见的“保证数据一致性”场景是删除学生。一个学生既在 t_student 表里,又在 t_score 表里有成绩,如果先删学生表,成绩表的记录就会变成孤儿数据,而且外键约束会直接让你删不动。正确顺序是先在事务里删成绩,再删学生。
@Service public class StudentServiceImpl implements StudentService { @Autowired private StudentMapper studentMapper; @Autowired private ScoreMapper scoreMapper; @Override @Transactional(rollbackFor = Exception.class) public void deleteStudent(Integer studentId) { scoreMapper.deleteByStudentId(studentId); studentMapper.deleteByPrimaryKey(studentId); } }@Transactional 的 rollbackFor = Exception.class 必须写。默认情况下 Spring 只对 RuntimeException 回滚,而 MyBatis 抛出的异常虽然是 RuntimeException,但如果你在代码里自己 try-catch 后抛了一个普通的 Exception,不加 rollbackFor 就不会回滚,数据会处于“成绩没了学生还在”的中间状态。这就是 Java 后端保证数据一致性的核心手段。答辩时如果老师问“多表操作怎么保证一致性”,你直接把这个方法贴出来即可。
4.4 登录拦截器:注册拦截器挡住所有未登录请求
登录校验是答辩必问项。最稳的方案是 Spring Boot 里的 HandlerInterceptor。
@Component public class LoginInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { Object loginUser = request.getSession().getAttribute("loginUser"); if (loginUser == null) { response.sendRedirect("/login"); return false; } return true; } }然后注册拦截器,放行登录页和静态资源。
@Configuration public class WebConfig implements WebMvcConfigurer { @Autowired private LoginInterceptor loginInterceptor; @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(loginInterceptor) .addPathPatterns("/**") .excludePathPatterns("/login", "/logout", "/css/**", "/js/**", "/error"); } }理解这段代码需要注意 addPathPatterns 和 excludePathPatterns 的作用范围。所有以 /login、/css、/js 开头的请求不拦截,其余全部拦截。这样即使学生手动输入 /student/delete?id=1 这样的地址,也会被重定向到登录页,从代码层面堵住了越权访问这条路。Session 存的对象 loginUser 可以是用户实体,也可以是只存的用户名,但要保证每个 Controller 判断权限时都能拿到角色信息。
4.5 分页查询:PageHelper 传参 pageNum、pageSize 的正确姿势
列表页几乎都需要分页。用 PageHelper 时最容易犯的错误是重复调用 startPage,或者在查询之后又调了一次。正确写法是这样:
@Override public PageInfo<Student> getStudentPage(Integer pageNum, Integer pageSize, String studentName) { PageHelper.startPage(pageNum, pageSize); List<Student> list = studentMapper.selectStudentPage(studentName); return new PageInfo<>(list); }Controller 里接收两个参数:
@GetMapping("/student/list") public String list(@RequestParam(defaultValue = "1") Integer pageNum, @RequestParam(defaultValue = "10") Integer pageSize, Model model) { PageInfo<Student> pageInfo = studentService.getStudentPage(pageNum, pageSize, null); model.addAttribute("pageInfo", pageInfo); return "student/list"; }PageHelper.startPage 必须紧跟在查询语句之前,中间不能有任何其他 SQL 操作,否则分页会作用到错误的语句上。PageInfo 里常用的是 total、pageNum、pageSize、list 四个属性,前端拿到后渲染表格和分页条。这个库的使用范围比较大,报告里一句话交代即可,但答辩时老师会问“分页是数据库层还是内存层”,你要能答出 PageHelper 是通过拦截器在 SQL 后面追加 LIMIT 实现的。
5. 毕设避坑:代码跑不通和答辩被问倒的高频原因
这一章列的每一条都是我见过的真实翻车案例,按“现象→原因→解决”写,你可以直接对照自己项目排查。
5.1 中文乱码:请求参数和数据库各出现一次
现象:页面上查询条件输入“张”,返回结果里的学生姓名、班级名全是问号或乱码;往数据库手动插入中文没问题,但通过页面保存后变成乱码。
原因:乱码有三层来源。第一层是数据库连接串没带 characterEncoding=utf8;第二层是数据库本身的字符集不是 utf8mb4,建表语句里漏了 CHARSET=utf8mb4;第三层是页面 POST 请求的 Content-Type 没设置 UTF-8。很多时候只修了其中一层,乱码依然在。
解决:把三个地方一次性都改对。application.yml 的 url 里补上 characterEncoding=utf8,建表语句全部显式写 CHARSET=utf8mb4,服务端对 POST 请求统一设置 UTF-8(Spring Boot 默认就处理,但如果你用了原生 Servlet 或过滤器重写了编码,就要检查设置顺序)。改完后重启项目,执行一条 INSERT 中文数据,再 SELECT 出来验证,别只看页面。
5.2 删除学生报外键约束错误:先删成绩再删学生
现象:点击删除学生按钮,页面直接报 SQLIntegrityConstraintViolationException,提示 Cannot delete or update a parent row。
原因:t_score 表里存在该学生的成绩记录,外键 fk_score_student 挡住了删除操作。初学者常见的解决办法是直接删掉外键约束,这让数据库少了一层保护,答辩时被追问会更难解释。
解决:在 Service 里先通过 scoreMapper.deleteByStudentId(studentId) 删掉成绩,再删学生,两步包在同一个事务里。这样既保留外键约束,又不会删不动。注意删除顺序和事务缺一不可,顺序错了会报错,事务缺了会数据不一致。
5.3 分页 count 查询报错:count 列与列表列不一致
现象:列表页第一页正常,点击第二页时报错,控制台显示 Error attempting to get column 'xxx' from result set,或者 count 查询的结果总数明显偏大。
原因:分页查询的列表 SQL 里用了 DISTINCT 或者多表连查导致结果行数变化,PageHelper 的自动 count 语句没有带上正确的查询条件。更常见的是列表 SQL SELECT 了多个表的同名字段,比如 s.name 和 c.name,count 阶段解析时不知道取哪一个。
解决:手动给 Mapper 的 select 语句加一个 count 查询,用 PageHelper 的 countSuffix 指定自定义 count 方法。另一个简单做法是把 SELECT 字段全部写成带别名的形式,如 s.student_name AS studentName,避免字段歧义。排查时先复制 SQL 到 Navicat 里手动跑 count 和 list 两条语句,对比数量差异在哪。
5.4 论文截图与运行结果对不上:临时改代码是大忌
现象:论文里写的是字段 A,但系统实际运行页面显示的是字段 B;论文里的功能列表有 8 个模块,系统里实际只有 6 个入口。
原因:多数人写论文和改代码是同时进行的,最后阶段为了演示效果临时改了前端字段显示、删了某个功能菜单,但论文里的截图和描述没有同步更新。答辩老师打开论文翻到第 3 章,再点开系统菜单一比,不一致的地方全部暴露。
解决:定稿前必须做一次“论文-系统对照检查”,从头到尾按目录把论文每个功能描述对应到实际运行页面,截图重截,功能描述重写。这个步骤放在最后一周,不要边写代码边贴图,所有截图集中在系统功能全部冻结后再截。
5.5 换电脑跑不起来:JDK 与 MySQL 版本的黑匣子现象
现象:代码在自己电脑上运行正常,拷到老师电脑或者答辩机器上,启动时报 java.lang.UnsupportedClassVersionError,或者数据库连接失败。
原因:Java 版本不一致是最常见的原因。你用的是 JDK 17,老师电脑是 JDK 8,class 文件版本高于 JVM 能识别的版本,直接拒绝加载。MySQL 版本不一致也会导致驱动加载报错,比如本机 MySQL 5.7,而代码里写的 MySQL 8 驱动在连接 5.7 时某些参数不兼容。
解决:项目在交付前统一到 JDK 8 + Spring Boot 2.7.x,这是兼容范围最大的一套组合。数据库脚本不要用 Navicat 的转储 SQL,那会包含图形化工具特有语句,改成纯建表语句 + INSERT 语句的 init.sql,并附上 MySQL 版本信息。答辩前把项目打成 jar 包,在另一台电脑上至少完整启动运行一遍,验证从零配置到跑通这一段流程没有任何遗漏。
6. 报告怎么写和答辩怎么讲:最后一周的冲刺方法
代码能跑只是毕设的一半,另一半是把你的工作有组织地写进报告里,并在答辩现场用最直接的方式讲清楚。最后一章说三个重点:报告结构、演示路线、提交检查。
6.1 毕业设计报告五章结构:每章写多少页、放什么图
毕业设计报告的常见结构是绪论、需求分析、系统设计、系统实现、测试与总结这五章。字数分配上,需求分析和系统设计是重头。
| 章节 | 建议页数 | 必放内容 | 常见扣分点 |
|---|---|---|---|
| 绪论 | 3-5 页 | 研究背景、意义、开发环境 | 背景写成百科词条 |
| 需求分析 | 6-10 页 | 用例图、角色表、功能模块列表 | 没画用例图、需求太泛 |
| 系统设计 | 8-15 页 | E-R 图、数据库表结构、类图或分层图 | 只有表结构没有 E-R 图 |
| 系统实现 | 10-20 页 | 核心代码加截图、参数说明 | 贴整段代码不做说明 |
| 测试与总结 | 3-5 页 | 功能测试表、结论、展望 | 测试表只有“正常通过” |
数据库表结构建议用表格形式列出所有字段,而不是把建表 SQL 整段贴上去。核心代码每段不超过 20 行,放完代码必须有 2-3 行文字说明这段代码解决了什么问题。附录里的代码要保持和正文一致的风格,不要出现与系统运行无关的测试类或临时调试代码。
6.2 答辩演示 10 分钟路线:从登录到统计报表的讲述顺序
答辩演示不要从首页开始点。推荐顺序是固定的:登录页→学生列表(展示分页和查询)→新增学生(展示表单校验)→成绩管理(展示录入与唯一索引的作用)→删除学生(展示事务处理)→统计报表(展示聚合查询)。
登录页要说明使用了拦截器,随便输入一段错误的 URL 会被重定向回去,这个动作现场演示比用嘴说有效。新增学生时输入一个已存在的学号,让系统报唯一索引错误,借此说明学号字段的 UNIQUE 约束。删除学生时先展示成绩表里有记录,再删除成功,解释为什么先删子表再删主表。最后在统计报表页面说出“这个查询用了 GROUP BY 和 AVG 函数”这句关键的话,答辩基本就稳住了。
6.3 交付前最后检查清单:从数据库脚本到注释的一次清理
最后提交的压缩包里,类夹结构应该是明确的:src/main/java(后台代码)、src/main/resources/mapper(Mapper XML)、sql/init.sql(建表加初始化数据)、docs/(报告和答辩 PPT)、README.md(运行说明)。运行说明里必须写清 JDK 版本、MySQL 版本、连接账号密码、启动方式。
我自己在交付前会做一遍这样的检查:代码里所有 System.out.println 删除或改成 logger;接口返回的异常信息不要直接抛给页面,统一捕获后跳转到错误页;数据库脚本里的测试数据清掉明显造假的记录;所有页面标题和菜单名称与论文目录保持一致;最后在命令行用 java -jar 启动一次,确认不是只在 IDEA 里能跑。这套工序看似琐碎,但每次都能提前抓到几个问题。
答辩用的演示机器上可以提前把 IDEA 和 MySQL 环境装好,避免现场等环境。遇到投影仪分辨率和页面布局错位的情况,提前准备一张竖屏截图放在 PPT 里作为兜底。这些都是血泪经验,希望帮到你。
本文还有配套的精品资源,点击获取