简介基于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 为什么是首选这个题目的技术路线一般有三条JSPServletJDBC、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 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; 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) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; 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) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;这三张表体现的是“院系—专业—班级”的层级。学生表里只保存 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) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;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 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; 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) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;这里最关键的参数是 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 关联学生表或教师的工号 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;password 字段必须设置成 VARCHAR(100)不是因为密码本身有 100 位而是因为 MD5 加密结果固定 32 位、BCrypt 加密结果固定 60 位如果按“最长密码 20 位”去设计字段长度加密后根本存不下。就用 BCrypt这是当前最稳妥的密码哈希方案。role 字段为什么不用三张 RBAC 表因为本系统的菜单是固定的只有三类角色不需要动态分配菜单和按钮权限一张表一个字段就够。报告里可以写一句“基于 RBAC 模型简化实现”老师再追问时你再讲清楚角色菜单的扩展方向。4. 后端代码落地从配置、登录到分页查询的关键片段数据库定了之后后端代码的骨架基本就定了。下面按一个最小可运行项目的顺序从依赖配置到分页查询依次给出可以直接照搬的写法。4.1 Maven 依赖与 application.yml三个必调参数时区、编码、驼峰映射先给 pom.xml 里的关键依赖。手动搭项目时直接加这几项不要自己引一堆其他包。parent groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-parent/artifactId version2.7.18/version /parent dependencies dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-thymeleaf/artifactId /dependency dependency groupIdorg.mybatis.spring.boot/groupId artifactIdmybatis-spring-boot-starter/artifactId version2.3.2/version /dependency dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId version8.0.33/version /dependency dependency groupIdcom.github.pagehelper/groupId artifactIdpagehelper-spring-boot-starter/artifactId version1.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?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: 123456 thymeleaf: cache: false mybatis: mapper-locations: classpath:mapper/*.xml configuration: map-underscore-to-camel-case: trueurl 里有三个必须调对的参数useUnicodetrue 和 characterEncodingutf8 保证中文能正确传输serverTimezoneAsia/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 idselectStudentPage resultTypecom.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 teststudentName ! null and studentName ! AND s.student_name LIKE CONCAT(%, #{studentName}, %) /if if testclassId ! null AND s.class_id #{classId} /if if teststatus ! 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?id1 这样的地址也会被重定向到登录页从代码层面堵住了越权访问这条路。Session 存的对象 loginUser 可以是用户实体也可以是只存的用户名但要保证每个 Controller 判断权限时都能拿到角色信息。4.5 分页查询PageHelper 传参 pageNum、pageSize 的正确姿势列表页几乎都需要分页。用 PageHelper 时最容易犯的错误是重复调用 startPage或者在查询之后又调了一次。正确写法是这样Override public PageInfoStudent getStudentPage(Integer pageNum, Integer pageSize, String studentName) { PageHelper.startPage(pageNum, pageSize); ListStudent 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) { PageInfoStudent 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 中文乱码请求参数和数据库各出现一次现象页面上查询条件输入“张”返回结果里的学生姓名、班级名全是问号或乱码往数据库手动插入中文没问题但通过页面保存后变成乱码。原因乱码有三层来源。第一层是数据库连接串没带 characterEncodingutf8第二层是数据库本身的字符集不是 utf8mb4建表语句里漏了 CHARSETutf8mb4第三层是页面 POST 请求的 Content-Type 没设置 UTF-8。很多时候只修了其中一层乱码依然在。解决把三个地方一次性都改对。application.yml 的 url 里补上 characterEncodingutf8建表语句全部显式写 CHARSETutf8mb4服务端对 POST 请求统一设置 UTF-8Spring 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.namecount 阶段解析时不知道取哪一个。解决手动给 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 8class 文件版本高于 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/mapperMapper XML、sql/init.sql建表加初始化数据、docs/报告和答辩 PPT、README.md运行说明。运行说明里必须写清 JDK 版本、MySQL 版本、连接账号密码、启动方式。我自己在交付前会做一遍这样的检查代码里所有 System.out.println 删除或改成 logger接口返回的异常信息不要直接抛给页面统一捕获后跳转到错误页数据库脚本里的测试数据清掉明显造假的记录所有页面标题和菜单名称与论文目录保持一致最后在命令行用 java -jar 启动一次确认不是只在 IDEA 里能跑。这套工序看似琐碎但每次都能提前抓到几个问题。答辩用的演示机器上可以提前把 IDEA 和 MySQL 环境装好避免现场等环境。遇到投影仪分辨率和页面布局错位的情况提前准备一张竖屏截图放在 PPT 里作为兜底。这些都是血泪经验希望帮到你。本文还有配套的精品资源点击获取