每到期末,课程设计、大作业、毕业设计接踵而至,你是不是也在为找一个“能跑起来、技术栈主流、文档齐全、最好还能写到简历里”的Java项目而焦头烂额?网上项目千千万,要么技术栈老旧,要么代码残缺跑不起来,要么就是纯CRUD毫无亮点。
今天要聊的这个基于SpringBoot的学生成绩管理系统,可能就是你的“期末救星”。但别急着去搜源码,我们先得搞清楚一个关键问题:一个“好”的课程设计项目,和网上随手下载的“源码包”,核心区别到底在哪里?
很多人以为,课程设计就是下载个代码,改改页面,交差了事。但如果你真这么做了,很可能在答辩时被老师问得哑口无言,或者在未来面试时,这段经历根本加不了分。真正的价值不在于代码本身,而在于你能否清晰地说出:为什么用SpringBoot而不是SSH?MyBatis-Plus比原生MyBatis好在哪里?Thymeleaf模板引擎解决了JSP的什么痛点?整个项目的分层架构是如何设计的?
本文将带你深度拆解一个典型的SpringBoot学生成绩管理系统。我们不止提供可运行的源码和数据库(文末会说明),更重要的是,我会为你剖析从零到一的完整设计思路、技术选型背后的原因、每个层级的代码实现,以及那些新手最容易栽进去的“坑”。无论你是用于期末大作业、课程设计,还是作为SpringBoot的入门实战,这篇文章都能让你不仅“做出来”,更能“讲明白”。
1. 为什么学生成绩管理系统是SpringBoot入门的最佳实战?
你可能觉得“学生成绩管理”太普通了,但恰恰是它的业务简单、需求明确,使其成为学习企业级开发框架的绝佳沙盒。它几乎涵盖了后端开发最核心的CRUD(增删改查),并自然地引申出用户权限、数据关联、事务处理等进阶概念。
使用SpringBoot来构建,对比传统的SSH(Struts2 + Spring + Hibernate)或SSM(SpringMVC + Spring + MyBatis)架构,优势是颠覆性的:
- 零配置起步:传统SSM需要大量繁琐的XML配置。SpringBoot通过自动装配和约定大于配置的原则,让你用几个注解就能跑起一个Web应用。
- 内嵌容器:无需额外安装和配置Tomcat。一个
main方法就能启动,java -jar一键部署,简化了开发和部署流程。 - 技术栈现代且统一:本项目常集成的MyBatis-Plus、Thymeleaf、Druid等,都是当前企业开发的主流选择,学习它们对求职有直接帮助。
- 生态丰富:SpringBoot拥有庞大的Starter生态,需要什么功能(如安全、缓存、消息队列),引入对应依赖即可,极大地提升了开发效率。
所以,这个项目的目标不仅仅是完成一个管理系统,而是通过一个具体的业务场景,打通SpringBoot + Maven + MyBatis-Plus + Thymeleaf + MySQL + Ajax这一整套现代Java Web开发技术栈。
2. 核心功能与业务模块设计
在动手写代码之前,我们必须先理清业务。一个完整的学生成绩管理系统,通常包含以下核心模块:
系统管理模块:
- 用户登录/注销:不同角色(如管理员、教师、学生)拥有不同权限。
- 角色与权限管理(RBAC):控制菜单访问和操作权限(如学生只能查成绩,教师能录成绩)。
基础数据管理模块:
- 学生信息管理:对学生信息的增、删、改、查。
- 教师信息管理:对教师信息的维护。
- 课程信息管理:管理学校开设的课程。
核心业务模块:
- 选课管理:学生选择课程,教师教授课程。这里涉及学生、课程、教师三者的关联。
- 成绩管理:教师录入、修改所授课程的学生成绩;学生查询自己的成绩。这是系统的核心。
统计与查询模块:
- 成绩统计:按课程、班级、个人统计平均分、最高/最低分等。
- 综合查询:多条件组合查询学生、成绩等信息。
数据库设计(ER图核心思想): 这是项目的基石。主要实体及关系如下:
sys_user:系统用户表,包含登录名、密码、角色等字段。student_info:学生信息表,包含学号、姓名、班级等。与sys_user可关联(如通过用户ID)。teacher_info:教师信息表。course_info:课程信息表,包含课程号、课程名、学分等。student_course:学生选课表。这是一个关联表,包含student_id,course_id,用于表示多对多关系(一个学生可选多门课,一门课可被多个学生选)。score_info:成绩表。这是最复杂的一张表,通常包含id,student_course_id(或同时包含student_id和course_id),score(成绩),teacher_id(录入教师)等字段。它关联了学生、课程和教师。
清晰的数据库设计是后面所有编码工作的前提。
3. 环境准备与项目初始化
开发环境清单:
- JDK:1.8 或 11(推荐LTS版本,如11)
- IDE:IntelliJ IDEA(社区版即可)或 Eclipse(需安装Spring Tools插件)
- 项目管理:Apache Maven 3.6+
- 数据库:MySQL 5.7 或 8.0
- 浏览器:Chrome 或 Firefox(用于测试)
使用Spring Initializr快速初始化项目: 这是SpringBoot官方提供的项目生成工具,能避免手动配置的繁琐。
- 访问 start.spring.io 。
- 按以下选择进行配置:
- Project: Maven
- Language: Java
- Spring Boot: 选择稳定的版本,如 2.7.x 或 3.x(注意3.x需JDK17+)
- Project Metadata:
Group:com.yourname(例如:com.example)Artifact:score-managementName:score-managementPackage name: 自动生成
- Packaging: Jar
- Java: 选择与你JDK匹配的版本
- 添加依赖:在
Dependencies区域搜索并添加:Spring Web(用于构建Web应用)Thymeleaf(模板引擎)MyBatis Framework(或直接搜索MyBatis Plus,但Initializr可能没有,需后续手动添加)MySQL Driver(数据库驱动)Lombok(简化实体类代码,强烈推荐)
- 点击
Generate按钮,下载生成的ZIP包,解压后用IDEA打开。
手动补充关键依赖(pom.xml): Spring Initializr可能没有MyBatis-Plus,我们需要手动在pom.xml的<dependencies>部分添加:
<!-- MyBatis-Plus 增强版ORM框架 --> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.5.3.1</version> <!-- 请检查并使用最新稳定版 --> </dependency> <!-- Druid 数据库连接池,性能强大,自带监控 --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.2.16</version> </dependency> <!-- 可选:Hutool 工具类库,提高开发效率 --> <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.8.22</version> </dependency>添加后,IDEA会自动下载依赖。如果速度慢,请检查Maven配置的镜像是否为国内源(如阿里云镜像)。
4. 项目架构与核心配置详解
一个结构清晰的SpringBoot项目目录如下:
src/main/java └── com.example.scoremanagement ├── ScoreManagementApplication.java // 主启动类 ├── config // 配置类包 │ ├── MybatisPlusConfig.java // MyBatis-Plus配置 │ ├── WebMvcConfig.java // 静态资源、拦截器配置 │ └── DruidConfig.java // 数据源监控配置 ├── controller // 控制层 ├── service // 业务逻辑层 │ └── impl // 业务逻辑实现层 ├── mapper // 数据访问层(Dao层),MyBatis的Mapper接口 ├── entity // 实体类,对应数据库表 ├── dto // 数据传输对象,用于前后端交互 ├── vo // 视图对象,用于页面展示 └── utils // 工具类包 src/main/resources ├── application.yml // 主配置文件(推荐YAML格式) ├── static // 静态资源(css, js, images) ├── templates // 模板文件(Thymeleaf HTML) └── mapper // MyBatis XML映射文件(如果用XML方式)核心配置文件application.yml: YAML格式比properties更清晰,支持层级结构。
server: port: 8080 servlet: context-path: / # 项目上下文路径,可为空或 /api等 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/score_db?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai username: root password: your_password # 请替换为你的数据库密码 type: com.alibaba.druid.pool.DruidDataSource # 指定使用Druid连接池 druid: initial-size: 5 min-idle: 5 max-active: 20 web-stat-filter: enabled: true stat-view-servlet: enabled: true url-pattern: /druid/* # 开启Druid监控,访问 http://localhost:8080/druid login-username: admin login-password: admin thymeleaf: prefix: classpath:/templates/ suffix: .html mode: HTML encoding: UTF-8 cache: false # 开发时关闭缓存,修改HTML后立即生效 mvc: static-path-pattern: /static/** # 静态资源访问路径 mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 控制台打印SQL,便于调试 map-underscore-to-camel-case: true # 自动将下划线命名转为驼峰命名 global-config: db-config: id-type: auto # 主键策略,数据库自增 logic-delete-field: deleted # 全局逻辑删除字段名(如果表中有此字段) logic-delete-value: 1 # 逻辑已删除值 logic-not-delete-value: 0 # 逻辑未删除值 mapper-locations: classpath*:/mapper/**/*.xml # XML映射文件位置配置类示例:MybatisPlusConfig.java
package com.example.scoremanagement.config; import com.baomidou.mybatisplus.annotation.DbType; import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class MybatisPlusConfig { /** * 添加MyBatis-Plus插件 * 分页插件是必须的,否则分页功能不生效 */ @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); // 添加分页插件,并指定数据库类型为MYSQL interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); // 还可以添加其他插件,如乐观锁插件 // interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor()); return interceptor; } }5. 实体类、Mapper与Service层代码实现
我们以Student(学生)实体为例,展示各层级的代码编写。
5.1 实体类 (Entity) -Student.java使用Lombok注解极大简化代码。
package com.example.scoremanagement.entity; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; import java.time.LocalDateTime; @Data // Lombok注解,自动生成getter, setter, toString, equals, hashCode等方法 @TableName("student_info") // 指定对应的数据库表名 public class Student { /** * 主键ID,使用数据库自增策略 */ @TableId(type = IdType.AUTO) private Long id; /** * 学号,唯一标识 */ private String studentNumber; /** * 学生姓名 */ private String name; /** * 性别 */ private String gender; /** * 所属班级 */ private String className; /** * 联系电话 */ private String phone; /** * 电子邮箱 */ private String email; /** * 入学日期 */ private LocalDateTime enrollmentDate; /** * 创建时间 */ private LocalDateTime createTime; /** * 更新时间 */ private LocalDateTime updateTime; }5.2 Mapper接口 (DAO层) -StudentMapper.javaMyBatis-Plus的Mapper接口只需继承BaseMapper,即可获得大量通用CRUD方法,无需编写XML(简单查询)。
package com.example.scoremanagement.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.example.scoremanagement.entity.Student; import org.apache.ibatis.annotations.Mapper; @Mapper // 标识为MyBatis的Mapper,Spring会自动扫描 public interface StudentMapper extends BaseMapper<Student> { // 如果需要复杂的自定义SQL,可以在这里定义方法,并在对应的XML文件中实现 // 例如:List<Student> selectStudentsByCondition(@Param("name") String name, @Param("className") String className); }5.3 Service层 -StudentService.java与StudentServiceImpl.javaService层定义业务逻辑接口,Impl类是实现。同样可以继承MyBatis-Plus的IService来获得增强的Service层CRUD方法。
// StudentService.java package com.example.scoremanagement.service; import com.baomidou.mybatisplus.extension.service.IService; import com.example.scoremanagement.entity.Student; import java.util.List; public interface StudentService extends IService<Student> { // 可以在此定义更复杂的业务方法,不局限于CRUD List<Student> getStudentsByClassName(String className); boolean saveStudent(Student student); boolean updateStudent(Student student); boolean deleteStudentById(Long id); }// StudentServiceImpl.java package com.example.scoremanagement.service.impl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.example.scoremanagement.entity.Student; import com.example.scoremanagement.mapper.StudentMapper; import com.example.scoremanagement.service.StudentService; import org.springframework.stereotype.Service; import java.util.List; @Service // 标识为Spring的Service组件 public class StudentServiceImpl extends ServiceImpl<StudentMapper, Student> implements StudentService { @Override public List<Student> getStudentsByClassName(String className) { // 使用MyBatis-Plus的Lambda查询构造器,避免SQL注入,写法优雅 return this.lambdaQuery() .eq(Student::getClassName, className) // where class_name = #{className} .orderByAsc(Student::getStudentNumber) // order by student_number asc .list(); } @Override public boolean saveStudent(Student student) { // 这里可以添加业务逻辑,例如学号唯一性校验 if (student.getStudentNumber() == null || student.getStudentNumber().isEmpty()) { throw new RuntimeException("学号不能为空"); } // 设置创建时间 student.setCreateTime(LocalDateTime.now()); return this.save(student); } @Override public boolean updateStudent(Student student) { // 设置更新时间 student.setUpdateTime(LocalDateTime.now()); return this.updateById(student); } @Override public boolean deleteStudentById(Long id) { // 物理删除 // return this.removeById(id); // 逻辑删除(需要实体类有deleted字段并配置了逻辑删除) return this.removeById(id); } }6. Controller层与Thymeleaf页面交互
Controller负责接收请求、调用Service、返回响应(页面或JSON)。
6.1 Controller -StudentController.java
package com.example.scoremanagement.controller; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.example.scoremanagement.entity.Student; import com.example.scoremanagement.service.StudentService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.*; @Controller @RequestMapping("/student") // 类级别的请求映射,所有方法路径都以/student开头 public class StudentController { @Autowired private StudentService studentService; /** * 分页查询学生列表 * @param pageNum 当前页码,默认为1 * @param pageSize 每页条数,默认为10 * @param name 学生姓名(查询条件) * @param className 班级名称(查询条件) * @param model 传递给页面的模型数据 * @return 学生列表页面 */ @GetMapping("/list") public String list(@RequestParam(value = "pageNum", defaultValue = "1") Integer pageNum, @RequestParam(value = "pageSize", defaultValue = "10") Integer pageSize, @RequestParam(value = "name", required = false) String name, @RequestParam(value = "className", required = false) String className, Model model) { // 1. 构建分页对象 Page<Student> page = new Page<>(pageNum, pageSize); // 2. 构建查询条件 LambdaQueryWrapper<Student> queryWrapper = new LambdaQueryWrapper<>(); if (name != null && !name.trim().isEmpty()) { queryWrapper.like(Student::getName, name.trim()); // 模糊查询姓名 } if (className != null && !className.trim().isEmpty()) { queryWrapper.eq(Student::getClassName, className.trim()); // 精确查询班级 } queryWrapper.orderByDesc(Student::getCreateTime); // 按创建时间倒序 // 3. 执行分页查询 Page<Student> studentPage = studentService.page(page, queryWrapper); // 4. 将数据放入Model,供Thymeleaf渲染 model.addAttribute("pageInfo", studentPage); model.addAttribute("name", name); model.addAttribute("className", className); return "student/list"; // 返回 templates/student/list.html 页面 } /** * 跳转到新增学生页面 */ @GetMapping("/add") public String toAddPage() { return "student/add"; } /** * 执行新增学生操作 * @param student 前端表单提交的学生数据(Spring MVC自动绑定) * @return 重定向到列表页 */ @PostMapping("/add") public String add(Student student) { studentService.saveStudent(student); return "redirect:/student/list"; // 使用重定向防止表单重复提交 } /** * 跳转到编辑学生页面 * @param id 学生ID * @param model * @return */ @GetMapping("/edit/{id}") public String toEditPage(@PathVariable("id") Long id, Model model) { Student student = studentService.getById(id); model.addAttribute("student", student); return "student/edit"; } /** * 执行更新学生操作 */ @PostMapping("/update") public String update(Student student) { studentService.updateStudent(student); return "redirect:/student/list"; } /** * 删除学生 */ @GetMapping("/delete/{id}") public String delete(@PathVariable("id") Long id) { studentService.deleteStudentById(id); return "redirect:/student/list"; } /** * RESTful API 风格接口示例:返回JSON数据(供Ajax调用) */ @GetMapping("/api/{id}") @ResponseBody // 此注解表示返回JSON,而不是视图名 public Student getStudentApi(@PathVariable("id") Long id) { return studentService.getById(id); } }6.2 Thymeleaf页面示例 -list.html(片段)Thymeleaf语法自然,直接在HTML中嵌入动态数据。
<!DOCTYPE html> <html lang="zh" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>学生列表</title> <link rel="stylesheet" th:href="@{/static/css/bootstrap.min.css}"> </head> <body> <div class="container mt-4"> <h2>学生信息管理</h2> <!-- 查询表单 --> <form class="form-inline mb-3" th:action="@{/student/list}" method="get"> <input type="text" class="form-control mr-2" name="name" th:value="${name}" placeholder="学生姓名"> <input type="text" class="form-control mr-2" name="className" th:value="${className}" placeholder="班级"> <button type="submit" class="btn btn-primary">查询</button> <a th:href="@{/student/add}" class="btn btn-success ml-2">新增学生</a> </form> <!-- 学生列表表格 --> <table class="table table-bordered table-hover"> <thead> <tr> <th>ID</th> <th>学号</th> <th>姓名</th> <th>性别</th> <th>班级</th> <th>操作</th> </tr> </thead> <tbody> <!-- th:each 循环遍历列表 --> <tr th:each="student : ${pageInfo.records}"> <td th:text="${student.id}"></td> <td th:text="${student.studentNumber}"></td> <td th:text="${student.name}"></td> <td th:text="${student.gender}"></td> <td th:text="${student.className}"></td> <td> <a th:href="@{/student/edit/{id}(id=${student.id})}" class="btn btn-sm btn-warning">编辑</a> <a th:href="@{/student/delete/{id}(id=${student.id})}" class="btn btn-sm btn-danger" onclick="return confirm('确定删除吗?')">删除</a> <!-- Ajax调用示例按钮 --> <button class="btn btn-sm btn-info" onclick="getStudentDetail(${student.id})">查看详情(Ajax)</button> </td> </tr> </tbody> </table> <!-- 分页组件 --> <nav> <ul class="pagination"> <li class="page-item" th:classappend="${pageInfo.current == 1} ? 'disabled' : ''"> <a class="page-link" th:href="@{/student/list(pageNum=1, pageSize=${pageInfo.size}, name=${name}, className=${className})}">首页</a> </li> <li class="page-item" th:classappend="${pageInfo.current == 1} ? 'disabled' : ''"> <a class="page-link" th:href="@{/student/list(pageNum=${pageInfo.current - 1}, pageSize=${pageInfo.size}, name=${name}, className=${className})}">上一页</a> </li> <!-- 页码遍历,这里简化显示 --> <li class="page-item" th:each="i : ${#numbers.sequence(1, pageInfo.pages)}" th:classappend="${i == pageInfo.current} ? 'active' : ''"> <a class="page-link" th:href="@{/student/list(pageNum=${i}, pageSize=${pageInfo.size}, name=${name}, className=${className})}" th:text="${i}"></a> </li> <li class="page-item" th:classappend="${pageInfo.current == pageInfo.pages} ? 'disabled' : ''"> <a class="page-link" th:href="@{/student/list(pageNum=${pageInfo.current + 1}, pageSize=${pageInfo.size}, name=${name}, className=${className})}">下一页</a> </li> <li class="page-item" th:classappend="${pageInfo.current == pageInfo.pages} ? 'disabled' : ''"> <a class="page-link" th:href="@{/student/list(pageNum=${pageInfo.pages}, pageSize=${pageInfo.size}, name=${name}, className=${className})}">尾页</a> </li> </ul> <div>当前第 <span th:text="${pageInfo.current}"></span> 页,共 <span th:text="${pageInfo.pages}"></span> 页,总计 <span th:text="${pageInfo.total}"></span> 条记录</div> </nav> </div> <script th:src="@{/static/js/jquery-3.6.0.min.js}"></script> <script> // 简单的Ajax调用示例 function getStudentDetail(studentId) { $.get('/student/api/' + studentId, function(data) { alert('学生详情:' + JSON.stringify(data)); }); } </script> </body> </html>7. 核心业务:成绩管理的关联查询实现
成绩管理是系统的核心,涉及多表关联查询(学生、课程、成绩)。这里展示如何使用MyBatis-Plus进行复杂的关联查询。
7.1 创建成绩视图对象(VO) -ScoreVO.javaVO用于封装前端页面需要的复杂数据,它可能来自多张表。
package com.example.scoremanagement.vo; import lombok.Data; import java.math.BigDecimal; import java.time.LocalDateTime; @Data public class ScoreVO { // 成绩信息 private Long scoreId; private BigDecimal score; // 使用BigDecimal处理精确小数 private LocalDateTime examTime; // 学生信息 private Long studentId; private String studentNumber; private String studentName; private String className; // 课程信息 private Long courseId; private String courseNumber; private String courseName; private BigDecimal credit; // 教师信息(录入者) private String teacherName; }7.2 编写自定义Mapper方法与XML在ScoreMapper.java中定义方法:
package com.example.scoremanagement.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.example.scoremanagement.entity.Score; import com.example.scoremanagement.vo.ScoreVO; import org.apache.ibatis.annotations.Param; @Mapper public interface ScoreMapper extends BaseMapper<Score> { /** * 多表关联分页查询成绩详情 * @param page 分页参数(MyBatis-Plus会自动处理) * @param studentName 学生姓名(模糊查询) * @param courseName 课程名称(模糊查询) * @return 分页的ScoreVO列表 */ Page<ScoreVO> selectScoreDetailPage(Page<ScoreVO> page, @Param("studentName") String studentName, @Param("courseName") String courseName); }在resources/mapper/ScoreMapper.xml中编写SQL:
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.example.scoremanagement.mapper.ScoreMapper"> <select id="selectScoreDetailPage" resultType="com.example.scoremanagement.vo.ScoreVO"> SELECT s.id as scoreId, s.score, s.exam_time, stu.id as studentId, stu.student_number, stu.name as studentName, stu.class_name as className, c.id as courseId, c.course_number, c.name as courseName, c.credit, t.name as teacherName FROM score_info s LEFT JOIN student_info stu ON s.student_id = stu.id LEFT JOIN course_info c ON s.course_id = c.id LEFT JOIN teacher_info t ON s.teacher_id = t.id WHERE 1=1 <if test="studentName != null and studentName != ''"> AND stu.name LIKE CONCAT('%', #{studentName}, '%') </if> <if test="courseName != null and courseName != ''"> AND c.name LIKE CONCAT('%', #{courseName}, '%') </if> ORDER BY s.exam_time DESC </select> </mapper>7.3 在Service和Controller中调用在ScoreService和ScoreServiceImpl中定义对应方法,然后在ScoreController中像之前一样调用并返回给页面。这个过程清晰地展示了如何从简单的单表CRUD过渡到复杂的多表业务查询,这是项目从“玩具”走向“实用”的关键一步。
8. 项目运行、测试与部署
8.1 数据库初始化
- 在MySQL中创建数据库:
CREATE DATABASE score_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; - 运行项目
resources目录下的SQL脚本(需自行根据实体类编写或使用MyBatis-Plus的代码生成器逆向生成),创建表并插入测试数据。
8.2 启动项目在IDEA中,找到主启动类ScoreManagementApplication,右键点击Run 'ScoreManagementApplication'。 或在项目根目录下使用Maven命令:mvn spring-boot:run看到控制台输出类似Started ScoreManagementApplication in X.XXX seconds的信息,即表示启动成功。
8.3 访问系统打开浏览器,访问http://localhost:8080。你需要先实现一个简单的登录页面和控制器。登录后即可访问学生、课程、成绩等管理页面。
8.4 打包部署
# 在项目根目录下执行Maven打包命令,会生成一个可执行的Jar文件 mvn clean package -DskipTests # 打包完成后,在target目录下找到 score-management-0.0.1-SNAPSHOT.jar # 使用java命令运行 java -jar target/score-management-0.0.1-SNAPSHOT.jar你可以通过--server.port=8081参数指定端口,或使用nohup命令在后台运行。
9. 常见问题与排查指南(Q&A)
| 问题现象 | 可能原因 | 排查方式 | 解决方案 |
|---|---|---|---|
启动时报Failed to configure a DataSource | 数据库连接配置错误或数据库服务未启动。 | 1. 检查application.yml中的url,username,password。2. 检查MySQL服务是否运行 (`netstat -an | grep 3306)。<br>3. 检查驱动类名是否正确(MySQL 8+ 使用com.mysql.cj.jdbc.Driver`)。 |
访问页面报Whitelabel Error Page(404) | 请求路径不存在或Controller未正确映射。 | 1. 检查浏览器地址栏URL是否正确。 2. 检查Controller的 @RequestMapping和@GetMapping/@PostMapping注解路径。3. 检查方法返回值是否是存在的Thymeleaf模板路径。 | 核对请求路径与Controller映射关系。 |
页面显示${...}而不是数据 | Thymeleaf模板引擎未正确解析。 | 1. 检查HTML文件开头是否声明了Thymeleaf命名空间xmlns:th="http://www.thymeleaf.org"。2. 检查是否引入了 spring-boot-starter-thymeleaf依赖。3. 检查 application.yml中Thymeleaf缓存是否关闭(开发时)。 | 确保Thymeleaf配置正确,语法无误。 |
| 插入中文到数据库变成乱码 | 数据库、连接字符串或应用字符集不统一。 | 1. 检查数据库、表、字段的字符集是否为utf8mb4。2. 检查JDBC连接URL是否包含参数 ?useUnicode=true&characterEncoding=utf8。3. 检查IDE和文件编码是否为UTF-8。 | 统一设置为UTF-8(推荐utf8mb4)。 |
| MyBatis-Plus分页不生效 | 未配置分页插件。 | 检查是否在配置类中注入了PaginationInnerInterceptor。 | 参考第4部分的MybatisPlusConfig配置类。 |
Autowired依赖注入失败,报Bean找不到 | Service或Mapper未被Spring扫描到。 | 1. 检查启动类是否在根包下,或其@SpringBootApplication注解能扫描到组件。2. 检查Service类是否有 @Service,Mapper接口是否有@Mapper或启动类上有@MapperScan。 | 确保组件注解正确,包路径在扫描范围内。 |
10. 项目扩展与进阶建议(让你的作业脱颖而出)
完成基础CRUD只是第一步。要让你的项目在答辩或简历中更有分量,可以考虑以下扩展方向:
- 引入权限控制(Spring Security或Shiro):实现管理员、教师、学生三种角色的登录和菜单权限控制。这是企业级应用的标配。
- 前端与后端分离:将Thymeleaf模板替换为Vue.js或React前端项目,后端Controller提供纯JSON API(使用
@RestController)。这能体现你对现代架构的理解。 - 添加数据校验:在实体类字段上使用
@NotNull,@Size等注解,或在Controller方法参数中使用@Valid注解进行后端校验,提高系统健壮性。 - 全局异常处理:使用
@ControllerAdvice和@ExceptionHandler定义全局异常处理器,统一返回友好的JSON错误信息,而不是暴露堆栈信息。 - 接口文档化:使用Swagger或Knife4j自动生成API文档,方便前后端协作。
- 缓存优化:对不常变动的数据(如课程列表)使用Spring Cache(如Redis)进行缓存,提升性能。
- 日志记录:使用SLF4J + Logback记录详细的业务操作日志和异常日志。
- 单元测试:为Service层关键方法编写JUnit单元测试,体现工程化思维。
这个基于SpringBoot的学生成绩管理系统项目,就像一副完整的“骨架”。本文为你详细讲解了每一块“骨骼”(技术组件)如何搭建、如何连接,以及它们协同工作的原理。真正的学习价值,在于你能否基于这副骨架,填充“肌肉”(业务逻辑)和“血液”(设计思想),并最终让它“活”起来,解决实际问题。
建议你按照本文的步骤,亲手搭建一遍。过程中遇到的每一个错误,都是加深理解的机会。完成基础版本后,尝试选择一个扩展方向进行实践。当你不仅能运行它,还能向别人清晰地阐述为什么这么设计、如何优化时,这个项目才真正成为了你的能力证明。