资讯动态

JSP+SSM第二课堂成绩单系统毕设全解析:源码改造与部署避坑指南

发布时间:2026/10/6 8:26:12 来源:尧图企业网站定制
简介面向高校计算机相关专业学生的第二课堂成绩单管理毕业设计项目基于SSMSpringSpringMVCMyBatis框架前端采用JSP数据库使用MySQL支持JDK1.8及Eclipse、MyEclipse、STS、IDEA等主流IDE。系统后台区分普通管理员与超级管理员普通管理员可修改密码并处理录入单审核超级管理员额外可维护普通管理员账号管理思想道德与技术修养信息、发布新闻、管理注册用户覆盖第二课堂成绩录入、审核、公示等典型业务场景。资源包共887个文件大小约42.25MB以JSP页面、Java源码与class文件、JAR依赖库、SQL脚本、JS/CSS/HTML前端资源及XML配置文件为主另含文档说明和实操视频目录结构便于按模块检索。目前已有82人学习下载。资源附带完整源码、数据库脚本、项目论文、环境工具包并在说明文档中提供同框架项目的安装教程后端业务按思想道德、技术修养、学生信息等Controller模块组织可直接部署运行也适合在毕业设计基础上继续扩展功能。1. 第二课堂成绩单系统是什么JSPSSM 毕设项目的完整画像如果你正在找 jspssm 大学生第二课堂成绩单系统这类源码大概率是被毕业设计卡住了。第二课堂成绩单系统说白了就是把学生在课堂之外参加的活动——社团、志愿、竞赛、讲座——统一登记、审核、量化成一张可打印的成绩单。这个业务在高校里真实存在而且每年都会有学生拿它当毕设题目技术栈十有八九是 JSP 做页面、SSMSpring SpringMVC MyBatis做后端配 MySQL 存数据。它之所以流行是因为这套组合刚好覆盖了 JavaWeb 课程里几乎所有考点MVC 分层、ORM 映射、拦截器、文件上传和权限控制。这篇文章我不打算讲空泛的项目介绍而是把你真正需要的东西列出来表结构怎么设计、SSM 怎么整合、页面和控制器之间怎么传数据、部署到 Tomcat 时哪些坑必踩。我默认你手上那份源码不是你自己写的而是下载下来要改造成自己项目的——所以每个环节我都会讲清楚“为什么这么做”以及“改哪里才能变成你自己的”。本文的目标只有一句让这套 JSPSSM 代码在你本地跑起来并且你能在答辩时讲清楚每一个关键配置。2. 为什么 JSPSSM 在毕设里仍是主流选型逻辑与工程结构2.1 SSM 与 Spring Boot 的取舍别被“过时”带偏很多学生拿到这份源码第一反应是为什么不用 Spring Boot这个问题答辩老师也爱问。SSM 是 Spring、SpringMVC、MyBatis 三个框架手写整合而 Spring Boot 把配置自动完成。但正因如此SSM 项目能看出你对“整合过程”是否真的理解——配置数据源、扫描 Mapper、配置视图解析器、管理事务每一步都是显式的。Spring Boot 把这些藏进自动配置里反而不好讲。更重要的是许多高校毕设题库里 JSPSSM 仍然是出现频率极高的题目导师也熟悉这套排错方式。所以别觉得它老它能活这么多年就是因为教学价值高、坑明确、资料多。我这里列一下常见做法里的版本搭配与不同 JDK 的兼容情况组件常见版本说明JDK1.7 / 1.8不建议上 JDK11老框架反射和 JSP 编译会有兼容问题Tomcat7.x / 8.x对应 Servlet 3.0/3.1SSM 项目最稳的是 Tomcat 8.5Spring4.x / 5.x4.3.x 和 5.0.x 在毕设源码里最常见MyBatis3.2.x ~ 3.4.x太新的版本可能会跟旧 Spring 整合包冲突MySQL5.7 / 8.08.0 需要换驱动类名和增加时区参数你拿到的源码里大概率自带一份pom.xmlMaven 工程或者一堆 jar 包非 Maven 工程。Maven 工程最好办因为依赖能一键下载非 Maven 工程需要手动在 WEB-INF/lib 里补齐 jar 包。下面先讲 Maven 工程的导入方式。2.2 导入 IDEA 并跑通 TomcatMaven 工程的一键启动拿到源码后第一步不是改代码而是把工程导入 IDEA。打开 IDEA选择File - Open定位到源码目录下的pom.xml以 Maven 工程方式打开。IDEA 会自动下载依赖这个过程是这套项目第一次“翻车”的高发地——原因通常是 Maven 镜像是默认中央仓库下载极慢甚至超时。我一般会在settings.xml里把镜像换成阿里云公共仓库写法如下mirror idaliyunmaven/id mirrorOf*/mirrorOf name阿里云公共仓库/name urlhttps://maven.aliyun.com/repository/public/url /mirror这里注意mirrorOf写成*会把所有仓库请求都转发到阿里云对国内网络环境是最省事的做法。如果项目里同时配置了私服这个通配符可能会覆盖私服地址那就改成mirrorOfcentral/mirrorOf只代理中央仓库。依赖下载完成后配置 Tomcat。在 IDEA 里点Run - Edit Configurations - 加号 - Tomcat Server - Local然后在Deployment选项卡里添加Artifact选项目名:war exploded。这里有一个非常关键的参数Application context要写成/还是/项目名这取决于源码里的请求路径和路径写法。如果项目里所有 JSP 的 hyperlink 都写了${pageContext.request.contextPath}那么 Application context 填/项目名也没问题如果是action/login这种绝对路径最好把 context 设成/避免 404。示例配置说明如下# Tomcat 配置要点 # 1. VM options 一栏不要填 -Xmx 之外的参数避免内存不足 # 2. 端口保持 8080如果被占用可以在 Server 选项卡里改成 8081 # 3. 打开 After launch 默认会用浏览器打开首页启动后如果控制台有一个INFO: Server startup in 5000 ms类似输出说明 Tomcat 起来了。如果卡在Deploying web application archive很久多半是数据库连接失败——因为 SSM 的contextLoaderListener在应用启动时就会初始化数据源数据库没连上整个应用就起不来。这是 SSM 项目最重要的一个特征配置错了不会给你一个友好的错误页而是直接启动失败。2.3 SSM 整合的关键配置文件里到底写了什么拿到源码后你一定会在resources或config目录下看到至少三个配置文件。很多学生最怕的就是这三个文件之间的关系。我直接说明白spring-mvc.xml管控制器、视图解析器、静态资源放行。spring-dao.xml或applicationContext.xml管数据源、SqlSessionFactory、Mapper 扫描。web.xml管两个容器的挂载顺序和拦截路径。这是一份常见的spring-dao.xml数据源配置bean iddataSource classorg.apache.commons.dbcp.BasicDataSource property namedriverClassName valuecom.mysql.jdbc.Driver/ property nameurl valuejdbc:mysql://localhost:3306/second_class?useUnicodetrueamp;characterEncodingutf8/ property nameusername valueroot/ property namepassword value123456/ /bean bean idsqlSessionFactory classorg.mybatis.spring.SqlSessionFactoryBean property namedataSource refdataSource/ property nameconfigLocation valueclasspath:mybatis-config.xml/ property namemapperLocations valueclasspath:mapper/*.xml/ /bean注意 url 里的amp;是 XML 转义代表很多学生漏掉这个符号导致连接参数丢失。useUnicodetruecharacterEncodingutf8是为了让数据库里中文不乱码。如果数据库是 MySQL 8.0driverClassName要改成com.mysql.cj.jdbc.Driver并且 url 后面追加serverTimezoneAsia/Shanghai否则会报时区错误。3. 把成绩单系统拆成四张核心表数据库设计与导入实操3.1 表的字段设计与关联逻辑为什么必须有中间表第二课堂成绩单系统的数据模型不复杂但少一张表就会在后续开发里痛不欲生。按我做过的同类项目核心就四张表学生表、活动表、成绩单主表、成绩单明细表。学生与活动是多对多关系一个学生参加多个活动一个活动有多个学生参加所以需要一张关联表来记录“学生参加活动”这个事实以及每次参与获得的学分和审核状态。学生表student字段至少包括学号主键、姓名、院系、专业、班级、入学年份。活动表activity字段包括活动ID自增主键、活动名称、活动类型志愿/社团/竞赛/讲座、活动时间、主办方、可申请学时。这里活动类型用数字字典管理比如 1 代表志愿2 代表社团3 代表竞赛4 代表讲座比直接存中文要规范得多做统计查询也方便。成绩单主表transcript是按学期或学年生成的一个学生一个学年一条记录存总学时、总积分、审核状态。成绩单明细表transcript_detail是学生参与的每条活动记录存参与时间、获得的学时、学分、审核状态。主表和明细表是一对多关系。为什么要拆出主表而不是直接查关联表因为成绩单是一个“盖章凭证”一旦生成就要固定下来后面活动数据变动不能影响已经打印的成绩单。这就是快照思想答辩时讲到这一句会很加分。3.2 建表 SQL 与初始化数据一段能直接复用的脚本下面这段 SQL 可以直接在 Navicat 或命令行里执行注意先创建数据库再建表CREATE DATABASE IF NOT EXISTS second_class DEFAULT CHARSET utf8mb4; USE second_class; CREATE TABLE student ( stu_no VARCHAR(20) PRIMARY KEY COMMENT 学号, stu_name VARCHAR(50) NOT NULL COMMENT 姓名, dept_name VARCHAR(50) COMMENT 院系, major_name VARCHAR(50) COMMENT 专业, class_name VARCHAR(50) COMMENT 班级, enroll_year INT COMMENT 入学年份 ) ENGINEInnoDB COMMENT学生表; CREATE TABLE activity ( act_id INT AUTO_INCREMENT PRIMARY KEY, act_name VARCHAR(100) NOT NULL COMMENT 活动名称, act_type TINYINT COMMENT 1志愿 2社团 3竞赛 4讲座, act_time DATETIME COMMENT 活动时间, org_name VARCHAR(100) COMMENT 主办方, hour_limit INT COMMENT 可获学时上限 ) ENGINEInnoDB COMMENT活动表; CREATE TABLE student_activity ( id INT AUTO_INCREMENT PRIMARY KEY, stu_no VARCHAR(20) NOT NULL, act_id INT NOT NULL, join_time DATETIME COMMENT 参与时间, hours DECIMAL(4,1) COMMENT 获得学时, credit DECIMAL(4,1) COMMENT 获得学分, status TINYINT DEFAULT 1 COMMENT 1待审 2通过 3驳回, UNIQUE KEY uk_stu_act (stu_no, act_id) ) ENGINEInnoDB COMMENT参与记录表;student_activity表就是前文说的关联表。UNIQUE KEY uk_stu_act (stu_no, act_id)是防止同一个人重复参加同一个活动被记两次学分这个约束在业务里非常关键。第二课堂成绩单系统里最容易出现的逻辑错误就是同一个学生同一场活动录了两遍导致学时翻倍。这条唯一索引是从源头上堵住这个问题。3.3 初始化一条测试数据让页面能立刻看到效果很多下载到的源码数据库脚本里数据是空的登录进去什么都看不到。你需要自己插几条测试数据。这里注意学生表的密码字段我没有设计进去——因为大多数毕设源码里用户表和密码是分离的或用user表。如果你拿到的源码里登录验证是走student表就补一个password字段如果走单独的sys_user表那就在那张表插管理员账号。常见做法是把管理员账号直接写死在 SQL 里INSERT INTO sys_user (username, password, role) VALUES (admin, 123456, 1); -- 注意密码在源码中通常不是纯明文 -- 如果源码用了 MD5请把 123456 替换成对应的 MD5 值这里“对应的 MD5 值”通常是e10adc3949ba59abbe56e057f20f883e即123456的 32 位 MD5。但不要盲目套用因为有些源码会对密码加盐后再加密。怎么判断打开源码里UserMapper.xml的selectByLogin语句看 SQL 条件是password #{password}还是password md5(#{password})前者说明需要预先加密。启动项目后如果能登录几次并把页面点翻说明你的环境基本跑通了。4. 从登录到成绩单导出四个核心模块的 JSPSSM 实现代码4.1 登录拦截器为什么每个页面都要做会话校验SSM 项目里的登录状态管理一般用拦截器实现。如果某个 JSP 页面直接访问时不经过登录校验那就是一个典型的未授权访问漏洞。在毕设答辩演示时直接输入http://localhost:8080/admin.jsp如果能看到后台内容老师会直接问你这个安全问题。这里给出一个实现拦截器的最简方式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(loginUser); if (user null) { response.sendRedirect(request.getContextPath() /login.jsp); return false; } return true; } }然后在spring-mvc.xml里注册拦截器并排除登录接口mvc:interceptors mvc:interceptor mvc:mapping path/**/ mvc:exclude-mapping path/login/ mvc:exclude-mapping path/login.jsp/ mvc:exclude-mapping path/static/**/ bean classcom.example.interceptor.LoginInterceptor/ /mvc:interceptor /mvc:interceptors这里的mvc:exclude-mapping很重要不排除掉静态资源和登录页拦截器会把 CSS、JS、图片全部拦掉页面会变成没有样式的裸 HTML这是常见的翻车现场。/**匹配所有路径login 接口和 JSP 都必须放行。4.2 第二课堂成绩单查询三表关联的 Mapper 写法成绩单页面的核心操作是“按学号查询学生及其参与活动列表”。这个查询要跨越三张表student、student_activity、activity。MyBatis 里有两种做法resultType 多表联查返回 DTO或者 resultMap 嵌套映射。对毕设来说多表联查返回 DTO数据传输对象代码最少、最好讲、也最好调试。先写 SQL 语句SELECT s.stu_no, s.stu_name, s.dept_name, a.act_name, a.act_type, sa.hours, sa.credit, sa.status FROM student s LEFT JOIN student_activity sa ON s.stu_no sa.stu_no LEFT JOIN activity a ON sa.act_id a.act_id WHERE s.stu_no #{stuNo}然后写对应的 Mapper 接口public interface TranscriptMapper { ListTranscriptDTO selectStudentTranscript(Param(stuNo) String stuNo); }关键点在TranscriptDTO的字段设计上建议用它接收混合数据而不是直接用实体类。如果直接拿Student实体接收活动名称和学时这些字段就要额外写 getter造成实体职责混乱。DTO 的好处在于可以专门为这个页面定制字段不污染数据库实体。这份 DTO 里我一般会写这几个字段stuNostuNamedeptNameactNameactTypehourscreditstatus。其中status在页面显示时还要做中文映射比如 1 显示待审核、2 显示已通过、3 显示已驳回。这个映射动作在 JSP 里用 EL 表达式做比较麻烦我一般在 Controller 里直接转换成字符串返回。4.3 成绩单页面渲染JSP 里用 JSTL 避免 Java 脚本有些老源码在 JSP 里直接写% %Java 脚本这在运行时没问题但极难维护。拿到手后建议看一页即可了解源码风格。如果发现是 JSTL EL那恭喜你代码干净如果是% %套着大段 Java你后续加功能会很痛苦。但毕设答辩时老师不太会扣这个他们更关心功能跑不跑得通。成绩单页面最核心的一段 JSP 代码如下c:forEach items${transcriptList} varitem tr td${item.stuNo}/td td${item.actName}/td td c:choose c:when test${item.status 1}待审核/c:when c:when test${item.status 2}已通过/c:when c:otherwise已驳回/c:otherwise /c:choose /td /tr /c:forEach这里items${transcriptList}对应 Controller 里往 ModelAndView 塞的 List 名称。如果页面上什么都没有显示先看三处一是% taglib prefixc urihttp://java.sun.com/jsp/jstl/core %有没有引入二是 jstl.jar 和 standard.jar 有没有在 WEB-INF/lib三是transcriptList这个 key 在 Model 里是否正确。前两个是 JSTL 不生效的最常见原因现象是 JSP 页面直接显示 JSTL 标签源码而没有解析。4.4 成绩单审核流程Controller 如何串联 Service 和 Mapper成绩单系统的第二个核心动作是“审核”。“学生申报 辅导员审批”是第二课堂成绩单系统的典型业务闭环。审核的本质是修改student_activity表的status字段但高层设计上必须经过 Service 层。下面给出一个标准的 Controller Service Mapper 三段式Controller RequestMapping(/transcript) public class TranscriptController { Resource private TranscriptService transcriptService; RequestMapping(/audit) public String audit(RequestParam(id) Integer id, RequestParam(result) Integer result) { transcriptService.auditRecord(id, result); return redirect:/transcript/list; } }Service 层实现Service public class TranscriptServiceImpl implements TranscriptService { Resource private TranscriptMapper transcriptMapper; Override public void auditRecord(Integer id, Integer result) { // result: 1通过 2驳回 transcriptMapper.updateStatus(id, result); } }Mapper 接口void updateStatus(Param(id) Integer id, Param(result) Integer result);这个过程里注意RequestParam(result)绑定的是表单里提交的 input 值。有的源码喜欢传中文意义的值如pass或reject再在 Controller 里做映射。这样做的优势是前端模板里的可读性更好但要多写一层 if 判断。对于毕设项目我建议直接用数字传参逻辑最简单答辩时讲起来也不容易卡壳。事务方面单个 update 语句天然具备原子性不需要额外加Transactional但如果审核通过后还需要同步修改成绩单主表的累计学时就需要在 Service 方法上加上事务注解Transactional(rollbackFor Exception.class) public void auditRecord(Integer id, Integer result) { studentActivityMapper.updateStatus(id, result); if (result 1) { transcriptMapper.summaryHours(id); } }4.5 JSP 表单提交与请求路径action 写法的两种风格很多学生在 JSP 表单里写actionaudit然后抱怨提交到 404。这里的原因是相对路径和绝对路径的区别。actionaudit是相对当前 URL 路径去请求如果当前页面是/transcript/list.jsp它会提交到/transcript/audit正好对但如果你在浏览器里打开了/abc.jsp再提交就会变成/abc/audit直接 404。正确的写法有两种!-- 方式一绝对路径带项目上下文 -- form action${pageContext.request.contextPath}/transcript/audit methodpost !-- 方式二依赖 base 标签统一加前缀 -- base href${pageContext.request.contextPath}/ form actiontranscript/audit methodpost第一种写法最直观每一处都写很清楚但代码冗余第二种只在head里写一个base标签后面所有相对路径都自动带上前缀。如果源码里的base已经写好了你只需要把href参数保证正确即可。这是 JSP 项目里最常见的“看着没问题但点不动”的原因重点检查你的pageContext.request.contextPath是否与 IDEA 里配置的 Application context 一致。5. 跑通这套源码的五个避坑点从 JDK 到数据库的血泪实录5.1 Tomcat 启动报 ClassNotFoundExceptionjar 包冲突或缺失现象Tomcat 启动时报java.lang.ClassNotFoundException: org.springframework.web.context.ContextLoaderListener页面完全无法访问。原因有两种一是在 IDEA 里没有把 Maven 依赖部署到 WEB-INF/lib二是spring-web和spring-webmvc版本不一致。解决方法是打开Project Structure - Artifacts - Output Layout查看Available Elements里有没有Maven: org.springframework:spring-web:5.x.x如果没有就右键把它加进WEB-INF/lib。如果是非 Maven 工程则直接检查web/WEB-INF/lib目录里有没有 spring-web jar 包。这是 SSM 项目最容易出现的问题因为 IDEA 的 Maven 工程不会自动把依赖打进来。每次增加依赖后都要重新构建 Artifact。检查方式很简单部署成功后打开 Tomcat 的webapps/项目名/WEB-INF/lib目录看看里面有没有出现你新加的 jar 包。5.2 MySQL 8.0 连不上驱动类名和时区是两座大山现象启动报com.mysql.jdbc.Driver找不到或The server time zone value Öйú is unrecognized。原因MySQL 8.0 把驱动类从com.mysql.jdbc.Driver换成com.mysql.cj.jdbc.Driver并且连接要求显式指定 serverTimezone。解决方法是去 Maven 的pom.xml里升级 mysql-connector-java 依赖并修改数据源配置。dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId version8.0.26/version /dependency对应的数据源改为jdbc.drivercom.mysql.cj.jdbc.Driver jdbc.urljdbc:mysql://localhost:3306/second_class?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/ShanghaiAsia/Shanghai是东八区标准写法不要写成GMT8因为后者在某些驱动版本里解析失败。这里有个血泪经验如果 Tomcat 启动日志里出现中文乱码的时间报错说明数据库连接已经通了但时区参数没对齐。5.3 JSP 页面中文乱码Tomcat 默认编码不是 UTF-8现象页面上的中文显示为一串问号???。原因Tomcat 8 以前默认使用 ISO-8859-1 处理 POST 请求参数而 JSP 页面已经声明了 UTF-8。解决方法是显式设置 CharacterEncodingFilter在web.xml中加一个过滤器放在所有过滤器最前面filter filter-nameencodingFilter/filter-name filter-classorg.springframework.web.filter.CharacterEncodingFilter/filter-class init-param param-nameencoding/param-name param-valueUTF-8/param-value /init-param init-param param-nameforceEncoding/param-name param-valuetrue/param-value /init-param /filter filter-mapping filter-nameencodingFilter/filter-name url-pattern/*/url-pattern /filter-mappingforceEncoding这个参数很关键设成true后请求参数和响应编码都会强制使用 UTF-8不用再在每个 Controller 里写request.setCharacterEncoding。如果你检查源码时发现这个过滤器缺失基本可以断定会遇到中文乱码坑。排查顺序是先看 JSP 头部的pageEncoding再看数据库连接 url 的 characterEncoding最后看 Tomcat 的 server.xml 里的URIEncodingUTF-8。5.4 刷新页面重复提交审核缺一个重定向现象点一次审核通过后按 F5 刷新同一记录又被审核了一次或者提示记录不存在。原因Controller 的audit方法执行完后return audit_success.jsp直接转发到页面没有重定向。刷新时浏览器会重新发送上一次的 POST 请求。解决方法是把成功后返回改成redirect:/transcript/list让浏览器以 GET 方式重新请求列表页。return redirect:/transcript/list;这是一个分辨源码好坏的分水岭。如果你的源码里审核提交后直接返回视图名说明作者的交互设计不够细致。这套逻辑同样适用于删除、新增操作。重定向后列表页会自动刷新而且用户按 F5 也不会重复提交。5.5 Mapper 接口和 XML 无法对应namespace 写错查半天现象启动报Invalid bound statement (not found): com.example.mapper.TranscriptMapper.selectStudentTranscript。原因MyBatis 的 Mapper XML 文件里namespace写的接口全限定名和实际接口路径不一致或者 XML 文件没有被打到 classpath。第一优先级是检查 XML 文件在 target/classes 目录里是否存在——IDEA 有时候不会自动把.xml资源复制到编译目录需要在pom.xml里显式声明resources resource directorysrc/main/java/directory includes include**/*.xml/include /includes filteringfalse/filtering /resource /resources这段配置把src/main/java下的 Mapper XML 一起编译打包。如果漏掉这个运行时会找不到 XML但编译期一点错都不报。这个坑特别隐蔽半天查不出来。第二优先级是检查namespacecom.example.mapper.TranscriptMapper是否和你 Mapper 接口的包名一致注意是接口的全限定名而不是 XML 的文件名。第三优先级是检查mapper resourcemapper/TranscriptMapper.xml的路径是否大小写敏感Ant 风格路径不要写错。6. 让答辩加分的一个进阶技巧成绩单 PDF 导出与 Excel 打印成绩单系统的核心交付物是“成绩单”线上页面能看、能审核只是过程最后导出打印成正式凭证才是闭环。很多学生的毕设源码里只做到了页面展示没有导出功能。但这个功能实现起来并不复杂而且给答辩的整体印象分会高不少——因为它是“最后一公里”的落地动作。常见做法是在前端用 JavaScript 实现页面打印或者在后端用 POI 导出 Excel。两种都可以但老师问到“怎么做”时你讲后端生成 Excel 再用 POI 转 PDF 的方案明显更专业。下面给一个用 Apache POI 导出成绩单 Excel 的核心代码public void exportTranscript(HttpServletResponse response, String stuNo) throws Exception { ListTranscriptDTO list transcriptService.selectStudentTranscript(stuNo); // 在内存里创建一个工作簿 Workbook workbook new HSSFWorkbook(); Sheet sheet workbook.createSheet(第二课堂成绩单); Row header sheet.createRow(0); header.createCell(0).setCellValue(学号); header.createCell(1).setCellValue(活动名称); header.createCell(2).setCellValue(学时); header.createCell(3).setCellValue(学分); for (int i 0; i list.size(); i) { TranscriptDTO dto list.get(i); Row row sheet.createRow(i 1); row.createCell(0).setCellValue(dto.getStuNo()); row.createCell(1).setCellValue(dto.getActName()); row.createCell(2).setCellValue(dto.getHours()); row.createCell(3).setCellValue(dto.getCredit()); } // 写输出流并告诉浏览器这是一个下载文件 response.setContentType(application/vnd.ms-excel); response.setHeader(Content-Disposition, attachment;filenametranscript.xls); workbook.write(response.getOutputStream()); workbook.close(); }这里的核心是HSSFWorkbook它对应 Excel 2003 的.xls格式兼容性最好但单表最多 65536 行对成绩单项目完全够用。如果要支持.xlsx和更多样式换成XSSFWorkbook并把文件名改成.xlsx。注意导出的文件是乱码时检查Content-Disposition里的文件名是否需要 URL 编码中文文件名在 Chrome 和 IE 的解析方式不同。实际生产里这个功能往往还会加一个限制只有审核通过的数据才能出现在导出成绩单里。这个判断可以在 SQL 里做也可以在 Controller 里过滤。我建议在 SQL 里做因为数据量大时前端过滤会遇到分页断层的麻烦WHERE sa.stu_no #{stuNo} AND sa.status 2在这套第二课堂成绩单系统上我最后悔的一件事是早期没把“导出内容需要走审核状态过滤”这个规则想清楚导致导出的成绩单把待审核的数据也打出来了。这个错误在学生答辩演示时如果被发现被追问的程度不亚于安全和漏洞问题。所以如果你拿到源码后要加导出功能请一定先处理好审核状态过滤。另外一个我常用的验证方法是导出完 Excel 后把文件里的数据跟页面上的汇总数字手工比对一遍。比如我先把页面里某个学生已有的学时加总再打开导出的文件看合计是否一致。这不只是验证功能也是在熟悉这套系统的业务逻辑。如果你能把这个对比过程熟练讲出来答辩时老师会觉得你是真实做过而不是背稿子。希望这些 JSPSSM 的整合思路、表设计方式和避坑记录能帮到你。毕业设计不一定非要从零写能把一套源码读懂、能说清楚每个模块为什么这么设计本身就是合格的能力证明。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑