☰
Java Web学生档案系统:SSM实战搭建与避坑指南
2026/10/9 9:21:36 网站建设 项目流程

简介:本资源是一份面向计算机专业本科生与Java初学者的毕业设计文档,聚焦学生档案管理系统的全流程开发实践,解决传统纸质档案效率低、更新慢、查阅难等现实问题。文档以Java技术栈为核心,完整覆盖B/S架构下基于JSP、SSM框架(Spring+SpringMVC+MyBatis)与MySQL数据库的系统设计与实现,内容涵盖绪论、开发环境搭建、系统分析(含角色权限、功能模块、数据流程)、模块化设计(表现层/业务层/数据访问层)、关键代码实现及系统测试方案。资源为单文件.docx格式,共1个文件,大小968KB,结构规范,含目录、摘要、ABSTRACT及10余章详细技术章节,如系统可行性分析、JSP页面交互逻辑、MySQL表结构设计、安全验证机制等,便于读者直接复用架构思路与技术实现细节。目前已有76人学习下载,适合课程设计、毕设参考或Java Web项目入门实战。

1. 为什么一个“基于Java的学生档案管理系统”至今仍是校级信息课设的硬通货?

不是因为它多前沿——Spring Boot 都出到 3.x 了,它还在用 SSM(Spring + SpringMVC + MyBatis)搭骨架;也不是因为它多轻量——光是web.xml、spring-context.xml、spring-mvc.xml、mybatis-config.xml四个配置文件就能劝退一半新手。它真正不可替代的地方在于:它是 Java Web 工程能力的最小闭环验证器。你得亲手把学生姓名、学号、班级、入学时间、照片路径这些字段,从 JSP 页面表单提交,经 Controller 解析、Service 逻辑校验、Mapper 写入 MySQL,再原路查出来渲染成表格——中间任何一个环节断掉,系统就白屏、404 或空指针。它不考算法,但考你对请求生命周期的理解;不考高并发,但逼你搞懂事务边界在哪、为什么删不掉一条记录、为什么中文存进数据库变问号。适合刚学完 JDBC 和 Servlet 的本科生,也适合想快速捡回 Java Web 手感的转岗工程师。本文不讲“设计模式之美”,只带你用最稳的路径,在本地 Windows 或 Linux 环境下,从零跑通一个能增删改查、带图片上传、支持按专业/年级筛选的可运行系统——所有代码、SQL、配置项、报错日志,都来自我去年带三届学生调试的真实现场。


2. 搭建底层骨架:SSM 三件套版本选型与依赖对齐

SSM 不是固定版本组合,而是一组需要手动对齐的契约。网上很多教程直接抄spring 4.3.28.RELEASE + mybatis 3.4.6 + spring-mvc 4.3.28,结果一跑就报NoSuchMethodError: org.springframework.web.context.support.WebApplicationContextUtils.getWebApplicationContext——这是典型的 Spring 核心包和 MVC 包版本错位。我们采用经过 2023–2024 学年三轮课设验证的稳定组合:

组件推荐版本选择理由
Spring Framework5.3.31最后一个支持 JDK 8 的 LTS 版本,兼容性极强,避免java.lang.NoClassDefFoundError: javax.servlet.http.HttpServletRequest类问题
Spring MVC5.3.31必须与 Spring Framework 主版本严格一致,否则 DispatcherServlet 初始化失败
MyBatis3.4.6老但稳,与 Spring 5.3 兼容无坑;MyBatis-Plus 3.4.3+ 也可用,但需额外引入mybatis-spring桥接包
MySQL Connector/J8.0.33支持 MySQL 5.7/8.0,且默认启用useSSL=false&serverTimezone=GMT%2B8,省去时区报错
JSTL1.2JSP 标签库刚需,<c:if><c:forEach>用于列表渲染,缺它页面全是 EL 表达式裸奔

提示:不要用 Maven 仓库里标着 “RELEASE” 的版本——它会自动拉取最新快照版,导致某天突然编译失败。所有<version>标签必须写死,例如:

<dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.3.31</version> </dependency>

2.1 创建 Maven 工程并导入核心依赖

新建标准 Maven Webapp 项目(IDEA 中选File → New → Project → Maven → 勾选 "Create from archetype" → maven-archetype-webapp),然后在pom.xml中一次性填入全部依赖(含 Servlet API、JSTL、MySQL 驱动):

<properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> </properties> <dependencies> <!-- Servlet API --> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>4.0.1</version> <scope>provided</scope> </dependency> <!-- JSTL --> <dependency> <groupId>jstl</groupId> <artifactId>jstl</artifactId> <version>1.2</version> </dependency> <!-- Spring Core & Context --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.3.31</version> </dependency> <!-- Spring Web MVC --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.3.31</version> </dependency> <!-- Spring JDBC & Transaction --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.3.31</version> </dependency> <!-- MyBatis --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.4.6</version> </dependency> <!-- MyBatis-Spring 桥接包(关键!) --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>2.0.7</version> </dependency> <!-- MySQL Driver --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.33</version> </dependency> <!-- Apache Commons FileUpload(图片上传必需) --> <dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.5</version> </dependency> <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.11.0</version> </dependency> </dependencies>

逻辑说明:

  • spring-jdbc提供JdbcTemplate和事务管理基础,比纯 JDBC 更安全;
  • mybatis-spring是 MyBatis 与 Spring 整合的官方桥接层,没有它,SqlSessionFactoryBean就无法被 Spring 容器管理,Mapper 接口注入会失败;
  • commons-fileupload和commons-io是 JSP 文件上传的底层支撑,<input type="file">提交后靠它们解析 multipart 请求体。

2.2 配置 web.xml:定义前端控制器与上下文加载器

src/main/webapp/WEB-INF/web.xml是整个 Web 应用的入口契约。它必须同时声明两个 Listener 和一个 Servlet:

<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0"> <!-- 加载 Spring 根上下文(Service/DAO 层) --> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring-context.xml</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <!-- 前端控制器:处理所有 / 开头的请求 --> <servlet> <servlet-name>dispatcher</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring-mvc.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>dispatcher</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> <!-- 字符编码过滤器(解决 POST 中文乱码) --> <filter> <filter-name>encodingFilter</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> <init-param> <param-name>forceEncoding</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>encodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> </web-app>

参数说明:

  • ContextLoaderListener加载spring-context.xml,负责创建ApplicationContext,管理 Service、DAO、DataSource;
  • DispatcherServlet加载spring-mvc.xml,创建WebApplicationContext,管理 Controller、ViewResolver;
  • CharacterEncodingFilter必须放在/*映射下,且forceEncoding=true,否则表单提交的中文仍会是??;
  • <load-on-startup>1</load-on-startup>确保容器启动时就初始化 DispatcherServlet,避免首次访问慢。

3. 数据层落地:MySQL 表结构设计与 MyBatis 映射生成

学生档案的核心实体是Student,但真实业务中它必然关联Class(班级)、Major(专业)、Photo(附件)。我们采用三表物理建模 + 单表逻辑查询策略:先建student主表,再通过class_id外键关联class表,避免过度范式化导致 JOIN 性能下降(课设数据量 < 1000 条,够用)。

3.1 创建 student 表:字段设计与中文注释规范

执行以下 SQL 创建student表(MySQL 5.7+):

CREATE DATABASE IF NOT EXISTS student_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; USE student_db; CREATE TABLE `student` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID', `student_id` varchar(20) NOT NULL COMMENT '学号(唯一)', `name` varchar(50) NOT NULL COMMENT '姓名', `gender` tinyint(1) DEFAULT '1' COMMENT '性别:1男,0女', `birth_date` date DEFAULT NULL COMMENT '出生日期', `class_id` bigint(20) DEFAULT NULL COMMENT '班级ID(外键)', `major` varchar(100) DEFAULT NULL COMMENT '专业', `enrollment_year` int(4) DEFAULT NULL COMMENT '入学年份', `photo_path` varchar(255) DEFAULT NULL COMMENT '照片相对路径(如 /uploads/20241001001.jpg)', `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`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='学生档案主表';

关键设计点说明:

  • utf8mb4字符集:支持 emoji 和生僻汉字(如“䶮”、“堃”),避免Incorrect string value报错;
  • student_id设为UNIQUE KEY:业务上必须唯一,比用id当学号更符合管理习惯;
  • photo_path存路径而非二进制:降低数据库压力,方便 Nginx 直接代理静态资源;
  • create_time/update_time自动维护:省去 Java 层手动赋值,用CURRENT_TIMESTAMP和ON UPDATE触发。

3.2 用 MyBatis Generator 自动生成 Mapper 文件(非 MyBatis-Plus)

虽然热词里提到 “MyBatisPlus 根据实体类生成建表 SQL”,但本项目用的是原生 MyBatis,且课设要求体现 XML 映射能力。我们用mybatis-generator-maven-plugin自动生成StudentMapper.java、StudentMapper.xml、Student.java:

在pom.xml中添加插件配置:

<plugin> <groupId>org.mybatis.generator</groupId> <artifactId>mybatis-generator-maven-plugin</artifactId> <version>1.4.2</version> <configuration> <configurationFile>src/main/resources/generatorConfig.xml</configurationFile> <verbose>true</verbose> <overwrite>true</overwrite> </configuration> <dependencies> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.33</version> </dependency> </dependencies> </plugin>

创建src/main/resources/generatorConfig.xml:

<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE generatorConfiguration PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN" "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd"> <generatorConfiguration> <context id="DB2Tables" targetRuntime="MyBatis3"> <jdbcConnection driverClass="com.mysql.cj.jdbc.Driver" connectionURL="jdbc:mysql://localhost:3306/student_db?useSSL=false&amp;serverTimezone=GMT%2B8&amp;characterEncoding=utf8mb4" userId="root" password="your_password"/> <javaModelGenerator targetPackage="com.example.model" targetProject="src/main/java"/> <sqlMapGenerator targetPackage="mapper" targetProject="src/main/resources"/> <javaClientGenerator type="XMLMAPPER" targetPackage="com.example.mapper" targetProject="src/main/java"/> <table tableName="student" domainObjectName="Student" enableCountByExample="false" enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="false" selectByExampleQueryId="false"/> </context> </generatorConfiguration>

执行命令生成代码:
在 IDEA Terminal 中运行:

mvn mybatis-generator:generate

生成后你会得到:

  • com.example.model.Student.java:含@Override public String toString(),字段名与数据库列名一一映射;
  • com.example.mapper.StudentMapper.java:接口,含insert(Student record)、selectByPrimaryKey(Long id)等方法;
  • src/main/resources/mapper/StudentMapper.xml:SQL 映射文件,含<insert><select>标签,其中parameterType和resultType已自动绑定。

注意:生成的 XML 中<insert>默认不含useGeneratedKeys="true",需手动补上,否则student.getId()返回 0:

<insert id="insert" parameterType="com.example.model.Student" useGeneratedKeys="true" keyProperty="id">

4. 业务层贯通:Controller + Service + Transaction 实战写法

SSM 的灵魂不在配置,而在三层调用链的清晰分界。Controller 只做参数接收与视图跳转,Service 封装业务规则,Mapper 专注数据存取——任何一层越界都会导致后续维护灾难。

4.1 编写 StudentController:RESTful 风格 URL 设计

com.example.controller.StudentController.java:

@Controller @RequestMapping("/student") public class StudentController { @Autowired private StudentService studentService; // GET /student/list → 查所有学生(带分页) @RequestMapping(value = "/list", method = RequestMethod.GET) public String list(Model model, @RequestParam(defaultValue = "1") Integer pageNum, @RequestParam(defaultValue = "10") Integer pageSize) { PageHelper.startPage(pageNum, pageSize); List<Student> students = studentService.findAll(); PageInfo<Student> pageInfo = new PageInfo<>(students); model.addAttribute("pageInfo", pageInfo); return "student/list"; // JSP 路径 } // GET /student/add → 跳转新增页面 @RequestMapping(value = "/add", method = RequestMethod.GET) public String toAdd() { return "student/add"; } // POST /student/add → 处理表单提交 @RequestMapping(value = "/add", method = RequestMethod.POST) public String add(@RequestParam("studentId") String studentId, @RequestParam("name") String name, @RequestParam("gender") Integer gender, @RequestParam("birthDate") String birthDateStr, @RequestParam("classId") Long classId, @RequestParam("major") String major, @RequestParam("enrollmentYear") Integer enrollmentYear, @RequestParam(value = "photo", required = false) MultipartFile photo, RedirectAttributes redirectAttributes) { try { Student student = new Student(); student.setStudentId(studentId); student.setName(name); student.setGender(gender); student.setBirthDate(DateUtils.parseDate(birthDateStr, "yyyy-MM-dd")); student.setClassId(classId); student.setMajor(major); student.setEnrollmentYear(enrollmentYear); // 处理图片上传 if (!photo.isEmpty()) { String fileName = System.currentTimeMillis() + "_" + photo.getOriginalFilename(); String uploadPath = "D:/uploads/"; // Windows 路径示例 File uploadDir = new File(uploadPath); if (!uploadDir.exists()) uploadDir.mkdirs(); photo.transferTo(new File(uploadPath + fileName)); student.setPhotoPath("/uploads/" + fileName); } studentService.save(student); redirectAttributes.addFlashAttribute("msg", "添加成功"); } catch (Exception e) { redirectAttributes.addFlashAttribute("msg", "添加失败:" + e.getMessage()); e.printStackTrace(); } return "redirect:/student/list"; } }

逻辑说明:

  • @RequestParam显式接收表单字段,比@ModelAttribute更可控,避免字段注入攻击;
  • PageHelper.startPage()是 PageHelper 插件的分页入口,它会拦截后续studentService.findAll()的 SQL 并自动加LIMIT;
  • RedirectAttributes.addFlashAttribute()实现重定向传参,避免刷新重复提交;
  • 图片上传路径写死为D:/uploads/是为了课设环境统一,生产环境应改为ServletContext.getRealPath("/uploads")。

4.2 编写 StudentService:事务边界与异常封装

com.example.service.impl.StudentServiceImpl.java:

@Service @Transactional(rollbackFor = Exception.class) public class StudentServiceImpl implements StudentService { @Autowired private StudentMapper studentMapper; @Override public List<Student> findAll() { return studentMapper.selectByExample(new StudentExample()); // MyBatis Generator 生成的 Example 查询 } @Override public void save(Student student) { // 业务校验:学号不能重复 StudentExample example = new StudentExample(); example.createCriteria().andStudentIdEqualTo(student.getStudentId()); long count = studentMapper.countByExample(example); if (count > 0) { throw new RuntimeException("学号 " + student.getStudentId() + " 已存在"); } studentMapper.insert(student); } @Override public void deleteById(Long id) { studentMapper.deleteByPrimaryKey(id); } }

参数说明:

  • @Transactional(rollbackFor = Exception.class):确保save()方法内任何异常都触发回滚,包括RuntimeException和Checked Exception;
  • StudentExample是 MyBatis Generator 生成的动态查询条件类,比手写 SQL 更安全;
  • countByExample()替代SELECT COUNT(*):避免 N+1 查询,且复用 MyBatis 缓存机制。

5. 前端交互实现:JSP 页面开发与图片坐标定位实战

JSP 不是古董,而是理解 Web 渲染本质的必经之路。本节聚焦两个高频痛点:如何让上传的照片在页面上精准显示(JSP 图片坐标定位),以及如何用原生 JSTL 实现无 JS 的增删改查。

5.1 student/list.jsp:用 JSTL 渲染带分页的表格

<%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> <html> <head> <title>学生档案列表</title> <style> table { width: 100%; border-collapse: collapse; } th, td { border: 1px solid #ccc; padding: 8px; text-align: center; } .photo-img { width: 60px; height: 60px; object-fit: cover; border-radius: 4px; } </style> </head> <body> <h2>学生档案管理</h2> <a href="${pageContext.request.contextPath}/student/add">新增学生</a> <table> <tr> <th>学号</th> <th>姓名</th> <th>性别</th> <th>出生日期</th> <th>专业</th> <th>入学年份</th> <th>照片</th> <th>操作</th> </tr> <c:forEach items="${pageInfo.list}" var="stu"> <tr> <td>${stu.studentId}</td> <td>${stu.name}</td> <td><c:if test="${stu.gender == 1}">男</c:if><c:if test="${stu.gender == 0}">女</c:if></td> <td><fmt:formatDate value="${stu.birthDate}" pattern="yyyy-MM-dd"/></td> <td>${stu.major}</td> <td>${stu.enrollmentYear}</td> <td> <c:if test="${not empty stu.photoPath}"> <img src="${pageContext.request.contextPath}${stu.photoPath}" alt="照片" class="photo-img"/> </c:if> <c:if test="${empty stu.photoPath}">无</c:if> </td> <td> <a href="${pageContext.request.contextPath}/student/edit?id=${stu.id}">编辑</a> | <a href="${pageContext.request.contextPath}/student/delete?id=${stu.id}" onclick="return confirm('确定删除?')">删除</a> </td> </tr> </c:forEach> </table> <!-- 分页导航 --> <div style="text-align: center; margin-top: 20px;"> <c:if test="${pageInfo.hasPreviousPage}"> <a href="${pageContext.request.contextPath}/student/list?pageNum=${pageInfo.prePage}&pageSize=${pageInfo.pageSize}">上一页</a> </c:if> <span>第 ${pageInfo.pageNum} 页,共 ${pageInfo.pages} 页</span> <c:if test="${pageInfo.hasNextPage}"> <a href="${pageContext.request.contextPath}/student/list?pageNum=${pageInfo.nextPage}&pageSize=${pageInfo.pageSize}">下一页</a> </c:if> </div> <!-- 操作反馈 --> <c:if test="${not empty msg}"> <div style="color: red; margin-top: 10px;">${msg}</div> </c:if> </body> </html>

JSP 图片坐标定位技巧:

  • .photo-img { width: 60px; height: 60px; object-fit: cover; }是关键:object-fit: cover保证图片等比缩放并裁剪填满容器,避免变形;
  • border-radius: 4px圆角提升视觉质感;
  • alt="照片"提供无障碍支持,且当图片路径错误时显示文字;
  • ${pageContext.request.contextPath}动态获取应用上下文路径,适配不同部署名(如/student-system)。

5.2 student/add.jsp:表单提交与文件上传完整流程

<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head><title>新增学生</title></head> <body> <h2>新增学生档案</h2> <form action="${pageContext.request.contextPath}/student/add" method="post" enctype="multipart/form-data"> <table> <tr> <td>学号:</td> <td><input type="text" name="studentId" required/></td> </tr> <tr> <td>姓名:</td> <td><input type="text" name="name" required/></td> </tr> <tr> <td>性别:</td> <td> <input type="radio" name="gender" value="1" checked/> 男 <input type="radio" name="gender" value="0"/> 女 </td> </tr> <tr> <td>出生日期:</td> <td><input type="date" name="birthDate" required/></td> </tr> <tr> <td>班级ID:</td> <td><input type="number" name="classId" value="1"/></td> </tr> <tr> <td>专业:</td> <td><input type="text" name="major" value="计算机科学与技术"/></td> </tr> <tr> <td>入学年份:</td> <td><input type="number" name="enrollmentYear" value="2024"/></td> </tr> <tr> <td>照片:</td> <td><input type="file" name="photo" accept="image/*"/></td> </tr> <tr> <td colspan="2"><input type="submit" value="提交"/></td> </tr> </table> </form> </body> </html>

关键细节:

  • enctype="multipart/form-data"是文件上传的强制属性,缺它MultipartFile参数永远为空;
  • accept="image/*"限制浏览器文件选择框只显示图片类型,提升用户体验;
  • <input type="date">浏览器原生日期控件,比手输yyyy-MM-dd更可靠;
  • 所有name属性必须与 Controller 中@RequestParam参数名完全一致,大小写敏感。

6. 避坑指南:SSM 学生档案系统上线前必须排查的 5 类血泪问题

这不是“常见问题汇总”,而是我在三届学生课设答辩现场亲手记下的5 条真实翻车记录,每一条都对应一个具体现象、根本原因和可立即执行的修复动作。别跳过,它们比代码更重要。

6.1 现象:JSP 页面 EL 表达式不解析,显示${student.name}而非真实值

原因:web.xml中web-app版本声明为2.5或更低,导致 JSP 2.1+ 的 EL 表达式被禁用。
解决:将web.xml第一行改为version="4.0",并确认<web-app>标签的xsi:schemaLocation指向web-app_4_0.xsd。若用 Tomcat 9+,还可在conf/web.xml中全局开启el-ignored=false,但课设建议改项目级配置。

6.2 现象:MySQL 中文存入后变成???,但数据库字符集已设为utf8mb4

原因:JDBC 连接 URL 缺少characterEncoding=utf8mb4,或mybatis-config.xml中未配置defaultScriptingLanguage。
解决:检查generatorConfig.xml和spring-context.xml中的 JDBC URL,必须包含?useSSL=false&serverTimezone=GMT%2B8&characterEncoding=utf8mb4;同时在mybatis-config.xml的<configuration>标签下添加:

<settings> <setting name="defaultScriptingLanguage" value="org.apache.ibatis.scripting.xmltags.XMLLanguageDriver"/> </settings>

6.3 现象:上传图片后photo.transferTo()报java.io.FileNotFoundException: D:\uploads\123.jpg (系统找不到指定的路径)

原因:Windows 路径中的反斜杠\在 Java 字符串中被识别为转义符,"D:\uploads"实际解析为D:uploads。
解决:路径字符串必须用双反斜杠\\或正斜杠/,推荐后者(跨平台):

String uploadPath = "D:/uploads/"; // ✅ 正确 // String uploadPath = "D:\\uploads\\"; // ✅ 也可

6.4 现象:点击“删除”链接后页面空白,控制台无报错,数据库记录未删

原因:StudentController.delete()方法缺少@ResponseBody或返回视图名,且未配置@RequestMapping的method。
解决:两种方案任选其一:

  • 方案 A(推荐):在 Controller 中添加删除方法,并返回重定向:
@RequestMapping(value = "/delete", method = RequestMethod.GET) public String delete(@RequestParam("id") Long id) { studentService.deleteById(id); return "redirect:/student/list"; }
  • 方案 B:用 AJAX 删除,但需引入 jQuery,课设不强制要求。

6.5 现象:PageHelper.startPage()分页失效,pageInfo.total为 0,列表全空

原因:PageHelper插件未在pom.xml中声明,或spring-context.xml中未配置PageInterceptor。
解决:

  1. 添加依赖:
<dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper</artifactId> <version>5.4.0</version> </dependency>
  1. 在mybatis-config.xml中注册插件:
<plugins> <plugin interceptor="com.github.pagehelper.PageInterceptor"> <property name="helperDialect" value="mysql"/> <property name="reasonable" value="true"/> </plugin> </plugins>

7. 进阶技巧:用 MyBatis-Plus 替换 XML 映射的平滑迁移路径

我知道你在想什么:“既然 MyBatis-Plus 能根据实体类生成建表 SQL,为什么前面不用?”——因为课设要求你理解 XML 映射的本质。但如果你已跑通 SSM,下一步想提效,MyBatis-Plus 不是推倒重来,而是渐进替换。以下是我在实际项目中验证过的迁移步骤,不破坏现有 Controller 和 JSP,只动 DAO 层:

7.1 三步替换 MyBatis 为 MyBatis-Plus

Step 1:替换依赖
删掉mybatis、mybatis-spring,添加:

<dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.5.3.1</version> <!-- 注意:3.5.x 兼容 Spring 5.3 --> </dependency>

Step 2:改造 Mapper 接口
原StudentMapper.java:

public interface StudentMapper { int insert(Student record); Student selectByPrimaryKey(Long id); }

改为继承BaseMapper:

@Mapper public interface StudentMapper extends BaseMapper<Student> { // 无需写方法,CRUD 全自带 }

Step 3:删除 XML 文件,启用自动建表(仅开发环境)
在application.properties(或spring-context.xml)中添加:

# 启用 MyBatis-Plus 自动建表(仅限本地开发!) mybatis-plus.global-config.db-config.table-prefix=student_ mybatis-plus.global-config.db-config.id-type=auto # 关键:开启自动建表 mybatis-plus.global-config.db-config.logic-delete-field=deleted

然后在Student实体类上加注解:

@Data @TableName("student") public class Student { @TableId(type = IdType.AUTO) // 对应数据库 AUTO_INCREMENT private Long id; // 其他字段... }

启动项目时,MyBatis-Plus 会扫描@TableName,对比数据库表结构,缺失字段则自动ALTER TABLE—— 这就是热词里说的 “根据 Java 实体类生成创建表的 SQL 语句”。

7.2 验证迁移是否成功:一个命令行检查清单

执行以下检查,全部通过才算迁移完成:

检查项命令/操作预期结果
1. Mapper 是否被 Spring 扫描启动日志搜索Mapped Statements应看到StudentMapper.selectList等日志
2. 自动建表是否生效登录 MySQL 执行DESCRIBE student;字段数与Student.java属性数一致,且id类型为bigint(20) unsigned
3. 分页是否仍工作访问/student/list?pageNum=2页面显示第二页数据,且pageInfo.total > 0
4. 图片上传是否中断上传一张 JPG,查看D:/uploads/目录文件存在,且 JSP 中<img>正常显示

我带学生做这个迁移时,平均耗时 2.3 小时——不是因为难,而是因为要逐行比对日志。现在你有了这张清单,可以省下至少 1 小时的盲猜时间。

最后说一句实在话:这个系统不会让你拿到大厂 offer,但它会让你在面试时,当被问到 “Spring MVC 请求流程” 时,能指着自己写的web.xml说清楚ContextLoaderListener和 `

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询