news 2026/9/5 13:32:28

Java Web学生管理系统实战:Spring Boot+MyBatis-Plus构建企业级CRUD应用

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Java Web学生管理系统实战:Spring Boot+MyBatis-Plus构建企业级CRUD应用

简介:本资源是一套面向计算机专业本科生的Java Web课程设计与期末大作业实战项目——学生管理系统,专为缺乏完整项目经验的学习者提供开箱即用的高质量参考方案。系统采用JSP+Servlet+MySQL技术栈实现,涵盖学生信息增删改查、班级管理、成绩录入与查询等核心功能,配套完整数据库脚本、详细课程设计报告(含需求分析、系统设计、ER图、流程图及测试说明),助力快速完成答辩与文档提交。压缩包共77个文件,包含20个Java源码、20个编译后Class文件、3个JSP页面、3个CSS与3个JS前端资源、9张界面与流程图JPG、1个建表SQL脚本及1个Word版设计报告,总大小4.03MB,结构清晰、模块分明,便于理解MVC分层逻辑与Web开发全流程。目前已有160人学习下载,项目曾获98分高分评价,适合作为Java Web入门后的首个综合实践载体。

1. 项目缘起与核心价值

又到了期末,看着课程设计或者大作业的要求,是不是感觉头大?尤其是对于计算机、软件工程相关专业的同学来说,一个“学生管理系统”几乎是绕不开的经典课题。我当年也是从这个项目开始,第一次把书本上的Java、数据库、Web开发知识串起来,做出了一个能跑起来的“真东西”。这么多年过去了,带过不少学弟学妹,也面试过很多应届生,发现这个看似简单的项目,其实藏着很多决定你最终成绩、甚至未来面试成败的细节。今天,我就以一个过来人和面试官的双重身份,跟你聊聊怎么把一个“基于Java Web的学生管理系统”从“能跑通”做到“有亮点”,让你在交作业和未来写简历时,都能脱颖而出。

这个项目的核心价值,绝不仅仅是完成老师布置的任务。它本质上是一个微缩的企业级CRUD应用。CRUD,也就是增删改查,是几乎所有业务系统的基石。通过它,你能实战演练MVC架构的思想,把前端页面、后端业务逻辑、数据库操作清晰地分离开;你能亲手配置数据库连接池,理解为什么直接new Connection()是性能杀手;你能接触到会话管理、权限控制、数据验证这些Web开发中的通用难题。可以说,吃透了这个项目,你就拿到了进入Java Web开发世界的第一张门票。网上源码很多,但大多只给了骨架,缺了“灵魂”——也就是为什么这么设计,以及实际开发中会遇到的“坑”。接下来,我就结合最常见的实现方式,给你补全这些关键细节。

2. 技术选型与架构设计:为什么是它们?

拿到题目,第一步不是急着敲代码,而是定技术栈。一个合理的技术选型是项目成功的基石。基于当前的主流教学环境和企业入门要求,我推荐下面这套组合,并解释为什么这么选。

2.1 后端:Spring Boot + MyBatis-Plus 组合

为什么不直接用Servlet/JSP?纯粹用Servlet/JSP是很多教程的起点,它能帮你理解最底层的HTTP请求-响应模型。但对于一个期末项目,这会让你的代码迅速被大量的Servlet类和JSP脚本淹没,难以维护,更难以体现你对现代开发框架的理解。直接上Spring Boot是更明智的选择。

Spring Boot是绝对的王者。它通过“约定大于配置”的理念,让你几乎不用写任何XML配置文件,就能快速搭建一个可独立运行的Web应用。你只需要在pom.xml里引入一个spring-boot-starter-web依赖,它就会自动帮你内嵌Tomcat服务器、配置好Spring MVC。这节省了大量环境搭建和配置的时间,让你能聚焦于业务逻辑。

持久层:MyBatis-Plus (MP) vs. JPA/Hibernate这里有个关键选择。传统教学可能用JDBC或MyBatis,但我强烈推荐MyBatis-Plus。它是MyBatis的增强工具,在保留MyBatis灵活性的基础上,提供了强大的CRUD封装。对于学生管理系统这种表单驱动型的应用,其优势是碾压性的:

  1. 无SQL实现基础CRUD:你只需要创建Student实体类,继承MP提供的BaseMapper,就自动拥有了insert,selectById,updateById,deleteById等方法。这能让你代码量减少70%以上。
  2. 强大的条件构造器:对于“按姓名模糊查询”、“按班级和日期范围查询”这种复杂查询,MP的QueryWrapperLambdaQueryWrapper可以用Java链式调用的方式优雅地构建SQL条件,完全避免手写SQL字符串的拼接错误和SQL注入风险。
  3. 代码生成器:MP自带代码生成器,可以基于数据库表,一键生成实体类、Mapper接口、Service接口、ServiceImpl实现类以及Controller骨架代码。这不仅是“偷懒”,更是标准化的工程实践。

相比之下,JPA/Hibernate的“对象-关系映射”更彻底,但学习曲线稍陡,且对于复杂查询的调优不如MyBatis直观。对于期末项目,MP在效率和易用性上完胜。

2.2 前端:Thymeleaf + Bootstrap + jQuery

为什么不用Vue/React?很多同学想用时髦的前端框架来炫技,但我劝你慎重。如果你的课程核心是Java Web后端,引入一个复杂的前端框架(如Vue)意味着你需要额外学习其构建工具(Webpack/Vite)、路由、状态管理,这可能会让你在有限的工期里本末倒置,后端没做好,前端也一团糟。

经典组合的稳定性Thymeleaf是Spring Boot官方推荐的服务器端模板引擎。它的语法自然,能在HTML中直接使用th:text="${student.name}"这样的表达式渲染后端数据,并且支持片段复用,非常适合做管理后台这类多页面应用。Bootstrap提供了现成、美观、响应式的UI组件(表格、表单、按钮、弹窗),你几乎不用写CSS就能做出像样的界面。jQuery则用来处理一些简单的DOM操作和Ajax请求,比如点击删除按钮弹出确认框,然后发送异步请求。

这套组合的优势在于技术栈纯粹、学习成本低、资料丰富,能让你快速搭建出功能完整、界面不丑的管理系统,把主要精力放在后端业务逻辑和数据库设计上。

2.3 数据库:MySQL 8.0

没什么好说的,MySQL是开源关系型数据库的事实标准。用8.0版本,注意一些语法和默认配置(如默认的身份验证插件caching_sha2_password)可能与旧版本工具不兼容。在本地开发时,强力推荐使用Docker来安装运行MySQL,一行命令搞定,环境纯净,避免直接安装把系统搞乱。

3. 数据库设计:不止于一张表

很多同学的“系统”只有一张student表,这显然是不够的。一个基本的学生管理系统,至少应该体现实体之间的关系。这里给出一个精简但关系清晰的设计方案。

3.1 核心表结构设计

-- 1. 学生表 (核心) CREATE TABLE `t_student` ( `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID', `student_id` varchar(20) NOT NULL COMMENT '学号(业务唯一标识)', `name` varchar(50) NOT NULL COMMENT '姓名', `gender` tinyint DEFAULT NULL COMMENT '性别(0:女,1:男)', `birthday` date DEFAULT NULL COMMENT '出生日期', `class_id` bigint DEFAULT NULL COMMENT '所属班级ID', `phone` varchar(20) DEFAULT NULL COMMENT '手机号', `email` varchar(100) DEFAULT NULL COMMENT '邮箱', `address` varchar(255) DEFAULT NULL COMMENT '家庭住址', `enrollment_date` date NOT NULL COMMENT '入学日期', `status` tinyint DEFAULT '1' COMMENT '状态(1:在读,2:休学,3:毕业)', `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', PRIMARY KEY (`id`), UNIQUE KEY `uk_student_id` (`student_id`), KEY `idx_class_id` (`class_id`), KEY `idx_name` (`name`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='学生信息表'; -- 2. 班级表 CREATE TABLE `t_class` ( `id` bigint NOT NULL AUTO_INCREMENT, `class_code` varchar(20) NOT NULL COMMENT '班级代码(如CS202401)', `class_name` varchar(100) NOT NULL COMMENT '班级名称(如2024级计算机1班)', `major` varchar(100) DEFAULT NULL COMMENT '专业', `instructor` varchar(50) DEFAULT NULL COMMENT '辅导员', `create_time` datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `uk_class_code` (`class_code`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='班级表'; -- 3. 用户表(用于系统登录) CREATE TABLE `t_user` ( `id` bigint NOT NULL AUTO_INCREMENT, `username` varchar(50) NOT NULL COMMENT '用户名', `password` varchar(255) NOT NULL COMMENT '加密后的密码', `role` varchar(20) DEFAULT 'student' COMMENT '角色(admin, teacher, student)', `real_name` varchar(50) DEFAULT NULL COMMENT '真实姓名', `is_locked` tinyint DEFAULT '0' COMMENT '是否锁定(0:否,1:是)', `last_login_time` datetime DEFAULT NULL, `create_time` datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `uk_username` (`username`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='系统用户表';

3.2 设计要点与避坑指南

  1. 主键与业务键分离id是自增主键,用于内部关联和索引,性能最好。student_id(学号)是业务上的唯一标识,用作UNIQUE KEY。两者分开,避免了学号规则变动(比如从纯数字改为包含年份)对系统底层关联的影响。
  2. 字段注释与字符集:务必为每个字段和表写COMMENTCHARSET=utf8mb4支持存储Emoji表情和所有Unicode字符,避免未来出现乱码问题。这是新手极易忽略但非常重要的细节。
  3. 时间字段create_timeupdate_time是审计追踪的黄金标准。利用MySQL的特性DEFAULT CURRENT_TIMESTAMPON UPDATE CURRENT_TIMESTAMP自动维护,不要在Java代码里手动设置(除非有特殊时区要求)。
  4. 索引设计:在student_id(唯一)、class_id(外键关联查询)、name(模糊搜索)上建立索引,能极大提升查询效率。但索引不是越多越好,会影响写性能。
  5. 密码存储t_user表中的password字段长度设为255,为使用BCrypt等强哈希算法留足空间。绝对禁止明文存储密码!后续在业务逻辑中会详细说明。

4. 后端核心实现:从Controller到数据库

有了清晰的数据模型,我们就可以开始编写业务代码了。这里以“学生信息管理”模块为例,拆解每一步。

4.1 实体类与Mapper:MyBatis-Plus的威力

首先,使用MP的代码生成器(或手动创建)生成Student实体类。注意,这里要使用Lombok插件来消除冗长的getter/setter代码。

import com.baomidou.mybatisplus.annotation.*; import lombok.Data; import java.time.LocalDate; import java.time.LocalDateTime; @Data @TableName("t_student") // 指定表名 public class Student { @TableId(type = IdType.AUTO) // 主键自增 private Long id; private String studentId; private String name; private Integer gender; // 使用包装类型,允许null private LocalDate birthday; private Long classId; private String phone; private String email; private String address; private LocalDate enrollmentDate; private Integer status; @TableField(fill = FieldFill.INSERT) // 插入时自动填充 private LocalDateTime createTime; @TableField(fill = FieldFill.INSERT_UPDATE) // 插入和更新时自动填充 private LocalDateTime updateTime; }

对应的Mapper接口极其简单:

import com.baomidou.mybatisplus.core.mapper.BaseMapper; import org.apache.ibatis.annotations.Mapper; @Mapper // 关键注解,让Spring Boot能扫描到 public interface StudentMapper extends BaseMapper<Student> { // 不需要写任何方法,BaseMapper已提供了完整的CRUD方法 }

这就是MP的魅力。StudentMapper已经拥有了selectList,selectById,insert,updateById,deleteById等方法。你还需要一个元对象处理器来自动填充create_timeupdate_time字段:

import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; import org.apache.ibatis.reflection.MetaObject; import org.springframework.stereotype.Component; import java.time.LocalDateTime; @Component public class MyMetaObjectHandler implements MetaObjectHandler { @Override public void insertFill(MetaObject metaObject) { this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now()); this.strictInsertFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now()); } @Override public void updateFill(MetaObject metaObject) { this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now()); } }

4.2 Service层:业务逻辑的容器

Service层负责协调复杂的业务逻辑。这里我们创建一个接口和其实现类。

// StudentService.java (接口) public interface StudentService { Page<StudentVO> getStudentPage(StudentQueryDTO queryDTO); StudentDetailVO getStudentDetail(Long id); boolean saveStudent(StudentSaveDTO saveDTO); boolean updateStudent(StudentUpdateDTO updateDTO); boolean deleteStudent(Long id); } // StudentServiceImpl.java (实现类) @Service @Slf4j // Lombok注解,自动提供log变量 public class StudentServiceImpl extends ServiceImpl<StudentMapper, Student> implements StudentService { @Autowired private ClassService classService; // 假设有班级服务 @Override public Page<StudentVO> getStudentPage(StudentQueryDTO queryDTO) { // 1. 构建分页对象(Page是MP的分页对象) Page<Student> page = new Page<>(queryDTO.getPageNum(), queryDTO.getPageSize()); // 2. 构建查询条件(使用Lambda,避免字段名硬编码错误) LambdaQueryWrapper<Student> wrapper = new LambdaQueryWrapper<>(); wrapper.like(StringUtils.isNotBlank(queryDTO.getName()), Student::getName, queryDTO.getName()); wrapper.eq(queryDTO.getClassId() != null, Student::getClassId, queryDTO.getClassId()); wrapper.eq(queryDTO.getStatus() != null, Student::getStatus, queryDTO.getStatus()); wrapper.orderByDesc(Student::getUpdateTime); // 按更新时间倒序 // 3. 执行分页查询 Page<Student> studentPage = this.page(page, wrapper); // 4. 将Page<Student>转换为Page<StudentVO>(视图对象,包含班级名称等) // 这里需要手动转换,并可能关联查询班级表获取className // 省略具体转换代码... return studentVOPage; } @Override public boolean saveStudent(StudentSaveDTO saveDTO) { // 1. 数据校验(如学号唯一性) LambdaQueryWrapper<Student> wrapper = new LambdaQueryWrapper<>(); wrapper.eq(Student::getStudentId, saveDTO.getStudentId()); if (this.count(wrapper) > 0) { throw new BusinessException("学号已存在"); } // 2. DTO转Entity Student student = new Student(); BeanUtils.copyProperties(saveDTO, student); // Spring工具类,属性拷贝 // 3. 设置默认状态等 student.setStatus(1); // 默认在读 // 4. 保存到数据库 return this.save(student); } // 其他方法类似... }

关键点解析

  1. DTO(Data Transfer Object)的使用StudentQueryDTO用于接收前端复杂的查询条件,StudentSaveDTO用于接收新增数据。这避免了直接使用Student实体类接收所有参数,导致实体类过于臃肿或暴露不该暴露的字段(如id,createTime)。
  2. LambdaQueryWrapper:这是MP的精华之一。它用Lambda表达式引用实体类的字段方法(如Student::getName),是类型安全的。相比于用字符串"name",它在编译时就能发现字段名拼写错误,极大地提高了开发效率和代码健壮性。
  3. 业务异常处理:像“学号已存在”这类业务错误,应该抛出自定义的业务异常(如BusinessException),然后在全局异常处理器中统一捕获,并返回给前端友好的错误信息,而不是直接返回false或打印日志了事。

4.3 Controller层:API的入口

Controller层负责接收HTTP请求,调用Service,并返回响应。这里要处理好参数校验和统一响应格式。

@RestController @RequestMapping("/api/student") @Slf4j public class StudentController { @Autowired private StudentService studentService; @GetMapping("/page") public Result<Page<StudentVO>> getPage(StudentQueryDTO queryDTO) { // @Validated 注解可以配合JSR-303校验注解在DTO上,进行参数校验 Page<StudentVO> page = studentService.getStudentPage(queryDTO); return Result.success(page); } @PostMapping public Result<Void> addStudent(@Validated @RequestBody StudentSaveDTO saveDTO) { // @Validated 触发校验,@RequestBody 接收JSON boolean success = studentService.saveStudent(saveDTO); return success ? Result.success() : Result.error("新增失败"); } @PutMapping("/{id}") public Result<Void> updateStudent(@PathVariable Long id, @Validated @RequestBody StudentUpdateDTO updateDTO) { updateDTO.setId(id); // 确保ID一致 boolean success = studentService.updateStudent(updateDTO); return success ? Result.success() : Result.error("更新失败"); } @DeleteMapping("/{id}") public Result<Void> deleteStudent(@PathVariable Long id) { // 实际项目中,删除前可能需要检查关联数据(如是否有成绩记录) boolean success = studentService.deleteStudent(id); return success ? Result.success() : Result.error("删除失败"); } }

统一响应封装Result是一个自定义的通用响应类,通常包含code(状态码)、message(消息)、data(数据)三个字段。这能让前端以统一的方式处理所有接口的响应。

@Data public class Result<T> { private Integer code; private String message; private T data; public static <T> Result<T> success(T data) { Result<T> result = new Result<>(); result.setCode(200); result.setMessage("success"); result.setData(data); return result; } // 其他静态工厂方法... }

5. 前端页面与交互:让系统“活”起来

后端API准备好了,前端就是调用它们并展示数据。我们使用Thymeleaf模板来渲染页面。

5.1 学生列表页:分页与查询

resources/templates/student/list.html中:

<!DOCTYPE html> <html lang="zh" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>学生管理</title> <!-- 引入Bootstrap CSS --> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet"> <!-- 引入jQuery --> <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script> </head> <body> <div class="container-fluid mt-3"> <h2>学生信息列表</h2> <!-- 查询条件表单 --> <form class="row g-3 mb-3" id="searchForm"> <div class="col-auto"> <input type="text" class="form-control" name="name" placeholder="学生姓名"> </div> <div class="col-auto"> <select class="form-select" name="classId"> <option value="">全部班级</option> <!-- 从后端获取班级列表并循环渲染 --> <option th:each="cls : ${classList}" th:value="${cls.id}" th:text="${cls.className}"></option> </select> </div> <div class="col-auto"> <button type="button" class="btn btn-primary" onclick="loadData(1)">查询</button> <button type="button" class="btn btn-secondary" onclick="resetSearch()">重置</button> </div> </form> <!-- 操作按钮 --> <div class="mb-3"> <button class="btn btn-success" onclick="openAddModal()">新增学生</button> </div> <!-- 数据表格 --> <table class="table table-bordered table-hover"> <thead> <tr> <th>学号</th> <th>姓名</th> <th>性别</th> <th>班级</th> <th>手机号</th> <th>状态</th> <th>操作</th> </tr> </thead> <tbody id="dataBody"> <!-- 数据由JavaScript动态加载 --> </tbody> </table> <!-- 分页组件 --> <nav aria-label="Page navigation"> <ul class="pagination justify-content-center" id="pagination"> </ul> </nav> </div> <!-- 新增/编辑模态框 (Modal) --> <div class="modal fade" id="studentModal" tabindex="-1" aria-hidden="true"> <!-- Modal内容省略,包含表单 --> </div> <script> let currentPage = 1; const pageSize = 10; // 页面加载完成后初始化数据 $(document).ready(function() { loadData(1); }); // 加载表格数据 function loadData(pageNum) { currentPage = pageNum; // 收集查询条件 const params = { pageNum: pageNum, pageSize: pageSize, name: $('input[name="name"]').val(), classId: $('select[name="classId"]').val() }; $.ajax({ url: '/api/student/page', type: 'GET', data: params, success: function(result) { if (result.code === 200) { renderTable(result.data.records); // 渲染表格行 renderPagination(result.data); // 渲染分页 } else { alert('加载失败:' + result.message); } }, error: function() { alert('网络请求失败'); } }); } // 渲染表格行数据 function renderTable(records) { const $tbody = $('#dataBody'); $tbody.empty(); if (!records || records.length === 0) { $tbody.append('<tr><td colspan="7" class="text-center">暂无数据</td></tr>'); return; } $.each(records, function(i, student) { const genderText = student.gender === 1 ? '男' : (student.gender === 0 ? '女' : '未知'); const statusText = {1:'在读',2:'休学',3:'毕业'}[student.status] || '未知'; const row = ` <tr> <td>${student.studentId}</td> <td>${student.name}</td> <td>${genderText}</td> <td>${student.className || '--'}</td> <td>${student.phone || '--'}</td> <td><span class="badge bg-info">${statusText}</span></td> <td> <button class="btn btn-sm btn-outline-primary" onclick="viewDetail(${student.id})">查看</button> <button class="btn btn-sm btn-outline-warning" onclick="openEditModal(${student.id})">编辑</button> <button class="btn btn-sm btn-outline-danger" onclick="deleteStudent(${student.id}, '${student.name}')">删除</button> </td> </tr> `; $tbody.append(row); }); } // 删除操作(带确认) function deleteStudent(id, name) { if (!confirm(`确定要删除学生【${name}】吗?此操作不可恢复。`)) { return; } $.ajax({ url: `/api/student/${id}`, type: 'DELETE', success: function(result) { if (result.code === 200) { alert('删除成功'); loadData(currentPage); // 刷新当前页 } else { alert('删除失败:' + result.message); } } }); } // 其他函数:openAddModal, openEditModal, saveStudent等... </script> </body> </html>

关键点解析

  1. 前后端分离的“半分离”模式:页面由Thymeleaf渲染(负责布局和静态数据注入,如班级下拉框),但表格数据通过Ajax动态加载。这比纯Thymeleaf渲染所有数据更灵活,体验更好。
  2. Ajax与RESTful API:使用jQuery的$.ajax调用我们后端的RESTful接口(GET /api/student/page,DELETE /api/student/{id})。这是现代Web应用的标准交互方式。
  3. 用户体验细节:删除前的确认弹窗、操作成功后的提示和列表刷新,这些细节能让你的系统显得更专业、更友好。

5.2 表单验证与提交

在新增/编辑的模态框中,表单提交前必须进行前端验证。可以使用HTML5原生验证或jQuery Validation插件。提交时,将表单数据序列化为JSON,通过Ajax POST/PUT到后端。

function saveStudent() { const formData = { studentId: $('#studentId').val(), name: $('#name').val(), gender: $('input[name="gender"]:checked').val(), // ... 收集其他字段 }; // 简单的前端非空校验 if (!formData.studentId || !formData.name) { alert('学号和姓名不能为空'); return; } const url = formData.id ? `/api/student/${formData.id}` : '/api/student'; const method = formData.id ? 'PUT' : 'POST'; $.ajax({ url: url, type: method, contentType: 'application/json', data: JSON.stringify(formData), success: function(result) { if (result.code === 200) { $('#studentModal').modal('hide'); // 关闭模态框 alert('保存成功'); loadData(currentPage); // 刷新列表 } else { alert('保存失败:' + result.message); } } }); }

6. 进阶功能与项目亮点

如果只做到基本的增删改查,你的项目可能只是“及格”。要拿高分或让项目成为简历亮点,必须加入一些体现你思考的进阶功能。

6.1 用户登录与权限控制

一个管理系统没有登录说不过去。实现一个简单的基于Session的登录认证

  1. 密码加密:使用Spring Security的BCryptPasswordEncoder。在保存用户时加密,在登录时比对。

    @Configuration public class SecurityConfig { @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } } // 在注册或修改密码时 String encodedPassword = passwordEncoder.encode(rawPassword); user.setPassword(encodedPassword); // 在登录校验时 boolean matches = passwordEncoder.matches(rawPassword, encodedPasswordFromDB);
  2. 登录接口与Session存储

    @PostMapping("/login") public Result<String> login(@RequestBody LoginDTO loginDTO, HttpSession session) { User user = userService.getByUsername(loginDTO.getUsername()); if (user == null || !passwordEncoder.matches(loginDTO.getPassword(), user.getPassword())) { return Result.error("用户名或密码错误"); } if (user.getIsLocked()) { return Result.error("账户已被锁定"); } // 登录成功,将用户信息存入Session session.setAttribute("LOGIN_USER", user); // 更新最后登录时间 userService.updateLastLoginTime(user.getId()); return Result.success("登录成功"); }
  3. 拦截器实现权限检查:创建一个LoginInterceptor,拦截除了登录页面和登录接口之外的所有请求,检查Session中是否存在用户信息。

    @Component public class LoginInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { HttpSession session = request.getSession(); Object user = session.getAttribute("LOGIN_USER"); if (user == null) { // 未登录,返回JSON错误或重定向到登录页 response.setContentType("application/json;charset=utf-8"); PrintWriter out = response.getWriter(); out.write(JSON.toJSONString(Result.error(401, "未登录"))); out.flush(); return false; } return true; } }

    WebMvcConfigurer中注册这个拦截器,并配置排除路径。

6.2 数据导出为Excel

这是一个非常实用的功能,也是老师喜欢看到的亮点。使用Apache POI或更易用的EasyExcel(阿里开源)来实现。

  1. 添加依赖(以EasyExcel为例):

    <dependency> <groupId>com.alibaba</groupId> <artifactId>easyexcel</artifactId> <version>3.3.2</version> </dependency>
  2. 定义导出数据的DTO:这个DTO的字段顺序和注解决定了Excel表头的顺序和名称。

    @Data public class StudentExportDTO { @ExcelProperty("学号") private String studentId; @ExcelProperty("姓名") private String name; @ExcelProperty(value = "性别", converter = GenderConverter.class) private Integer gender; @ExcelProperty("班级") private String className; @ExcelProperty("手机号") private String phone; // ... 其他字段 }
  3. 编写Controller导出接口

    @GetMapping("/export") public void exportStudents(StudentQueryDTO queryDTO, HttpServletResponse response) throws IOException { // 1. 设置响应头,告诉浏览器这是一个文件下载 response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); response.setCharacterEncoding("utf-8"); String fileName = URLEncoder.encode("学生列表", "UTF-8").replaceAll("\\+", "%20"); response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx"); // 2. 查询数据(这里可以复用分页查询的逻辑,但通常导出是全部数据) List<Student> list = studentService.list(new LambdaQueryWrapper<Student>() .like(StringUtils.isNotBlank(queryDTO.getName()), Student::getName, queryDTO.getName()) // ... 其他条件 ); // 3. 将Student列表转换为StudentExportDTO列表(可能需要关联查询班级名称) List<StudentExportDTO> exportList = convertToExportDTO(list); // 4. 使用EasyExcel写入到response的输出流 EasyExcel.write(response.getOutputStream(), StudentExportDTO.class) .sheet("学生信息") .doWrite(exportList); }

6.3 项目报告与文档

这是课程设计评分的重要组成部分。报告不应只是代码的堆砌,而应体现你的设计思路、实现过程和总结思考

报告建议结构

  1. 项目概述:简要说明项目背景、目标、功能模块。
  2. 需求分析:用文字或用例图描述系统的核心功能(如学生信息管理、班级管理、用户登录)。
  3. 系统设计
    • 架构设计:画出系统架构图(如浏览器 -> Nginx -> Spring Boot应用 -> MySQL),并说明采用MVC分层结构。
    • 数据库设计:给出完整的E-R图,并附上核心表的SQL建表语句(就是前面设计的那部分)。
    • 接口设计:列出核心的RESTful API清单(URL、方法、参数、返回示例)。
  4. 系统实现
    • 关键技术:说明Spring Boot、MyBatis-Plus、Thymeleaf等技术选型的理由。
    • 核心代码展示与说明:挑选2-3个最有代表性的代码片段(如Service层的复杂查询、Controller层的统一异常处理、前端Ajax交互),并配上详细的注释说明。
    • 功能截图:附上系统主要界面的运行截图(列表页、表单页、登录页等)。
  5. 系统测试:描述你如何进行测试的。可以是简单的单元测试(用@SpringBootTest测试Service方法),也可以是手动功能测试(列出测试用例,如“输入错误密码登录,预期提示错误信息”)。
  6. 总结与展望
    • 工作总结:你在项目中承担的角色、遇到的最大困难及解决方案(这是亮点!比如:“在实现分页查询时,发现关联查询班级名称导致性能问题,后采用MyBatis-Plus的@TableField(exist=false)配合自定义SQL片段解决”)。
    • 心得体会:通过项目学到了什么,对Java Web开发有了哪些新认识。
    • 不足与改进:诚实地说出项目的不足之处(如“前端界面比较简陋,没有做响应式适配”、“缺乏完整的单元测试覆盖”),并提出可能的改进方向(如“可以引入Vue.js重构前端”、“可以集成Redis缓存热点数据”)。

7. 本地运行与部署上线

7.1 本地运行(开发环境)

  1. 克隆/导入项目:使用IDEA或Eclipse导入Maven项目。
  2. 配置数据库:在src/main/resources/application.yml中配置你的MySQL连接信息。
    spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/student_db?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai username: root password: yourpassword thymeleaf: cache: false # 开发时关闭缓存,修改html立即生效 mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 控制台打印SQL,调试用
  3. 创建数据库并导入SQL:运行你写的建表SQL脚本。
  4. 运行主类:找到XXXApplication(通常以Application结尾)类,运行它的main方法。Spring Boot会启动内嵌的Tomcat,默认在http://localhost:8080
  5. 访问系统:打开浏览器访问http://localhost:8080(或你自定义的上下文路径)。

7.2 部署到服务器(可选,但非常加分)

如果你想让老师或同学通过公网访问你的项目,可以尝试部署。最简单的方式是使用Docker

  1. 打包应用:在项目根目录执行mvn clean package,会在target目录下生成一个xxx.jar文件。
  2. 编写Dockerfile
    FROM openjdk:11-jre-slim VOLUME /tmp COPY target/student-management-system-0.0.1-SNAPSHOT.jar app.jar ENTRYPOINT ["java","-jar","/app.jar"]
  3. 构建并运行Docker镜像
    # 在服务器上(确保安装了Docker) docker build -t student-system . # 运行容器,映射端口,并链接到另一个MySQL容器或外部数据库 docker run -d -p 8080:8080 \ -e "SPRING_DATASOURCE_URL=jdbc:mysql://mysql_host:3306/student_db" \ -e "SPRING_DATASOURCE_USERNAME=root" \ -e "SPRING_DATASOURCE_PASSWORD=yourpassword" \ --name student-app \ student-system

8. 避坑指南与经验之谈

最后,分享几个我踩过或者见别人踩过的坑,希望能帮你少走弯路。

  1. 字符编码问题:这是中文系统的永恒之痛。确保三处统一为UTF-8

    • 数据库:建库建表时指定CHARSET=utf8mb4
    • 后端:在Spring Boot配置文件中,数据库连接URL加上?characterEncoding=utf8,确保Tomcat容器也使用UTF-8。
    • 前端:HTML文件<meta charset="UTF-8">,Ajax请求也明确设置contentType: 'application/json;charset=utf-8'
  2. 日期时间处理:Java 8的LocalDateLocalDateTime是首选。在实体类中直接使用它们,并在数据库连接URL中指定serverTimezone=Asia/Shanghai。前端传递日期字符串时,使用标准的yyyy-MM-dd格式,Spring MVC可以自动绑定。

  3. MyBatis-Plus字段映射坑:数据库字段名是create_time,实体类属性是createTime,MP默认开启了下划线转驼峰的映射,所以没问题。但如果你的字段名是createTime(驼峰),数据库也是createTime,就需要在application.yml中配置mybatis-plus.configuration.map-underscore-to-camel-case=false

  4. 分页查询性能:当数据量很大时,LIMIT offset, size这种写法在offset很大时非常慢。MP的分页在MySQL下也是这么实现的。如果数据量真的很大(比如超过10万条),需要考虑使用“游标分页”或“基于ID的范围查询”来优化。

  5. 事务管理:在Service方法中,如果涉及多次数据库写操作(比如先删A表,再插B表),务必在方法上添加@Transactional注解,保证原子性。但要注意,默认只在抛出RuntimeException时回滚,如果捕获了异常又不重新抛出,事务不会回滚。

  6. 前端资源加载404:静态资源(CSS, JS, 图片)放在src/main/resources/static/目录下。Thymeleaf模板放在src/main/resources/templates/下。如果页面引用/static/css/style.css,Spring Boot默认会从static目录下查找。

把这个项目做扎实,你收获的不仅仅是一个期末的“优”,更是对Java Web开发全流程的一次深刻实践。从数据库设计到前端交互,从业务逻辑到异常处理,每一个环节都值得细细打磨。当你把代码整理好,报告写清楚,再部署到公网能访问,这份作品就足以成为你简历上扎实的一个项目经验。

本文还有配套的精品资源,点击获取

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

PC上位机与中控系统多传感器数据采集全流程实现指南

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

作者头像 李华
网站建设 2026/9/5 13:29:49

SodaDownloader技术解析:从流媒体加密到无损音频获取的逆向工程实践

简介&#xff1a;SodaDownloader是一款面向音乐爱好者与音质追求者的开源工具&#xff0c;专为汽水音乐平台设计&#xff0c;解决用户无法直接下载高质量音频的痛点&#xff0c;尤其适用于离线收听、本地音乐库构建及FLAC/M4A无损音源收藏等场景。资源包共26个文件&#xff0c;…

作者头像 李华
网站建设 2026/9/5 13:29:40

基于Qt与C++的三维牙齿模型自动化预处理系统开发实战

简介&#xff1a;本资源是一套面向高校本科生及研究生的三维医学图像处理实践项目&#xff0c;聚焦口腔医学场景下的自动化牙齿预处理任务&#xff0c;适用于毕业设计、课程设计与医疗AI方向项目开发。系统基于Qt5.13.2C构建&#xff0c;集成VTK8.2.0实现STL格式整口牙模型的连…

作者头像 李华
网站建设 2026/9/5 13:26:12

YOLOv8实战:从零构建校园玻璃幕墙清洁度检测系统

简介&#xff1a;本资源是一项面向高校计算机相关专业学生&#xff08;如计算机科学、人工智能、自动化等&#xff09;的毕业设计级计算机视觉项目&#xff0c;聚焦校园建筑玻璃幕墙清洁度智能检测这一实际应用场景&#xff0c;基于YOLOv8目标检测框架实现端到端的脏污识别与量…

作者头像 李华
网站建设 2026/9/5 13:25:03

基于STM32与Proteus的智能家居环境监测系统仿真设计实战

简介&#xff1a;本资源是一套面向嵌入式初学者与课程设计者的STM32实践项目&#xff0c;适用于单片机原理、物联网感知技术等课程的期末大作业或毕业设计选题。项目基于STM32F4系列芯片&#xff0c;在Proteus中完成家居环境多参数仿真采集&#xff0c;涵盖温湿度&#xff08;D…

作者头像 李华
网站建设 2026/9/5 13:22:56

GLM-5.3-Flash 接入 Cline 免费实战:配置避坑与多模型切换指南

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

作者头像 李华