ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

SpringBoot+Vue课表管理系统设计与实现:从数据库到部署全解析

SpringBoot+Vue课表管理系统设计与实现:从数据库到部署全解析 搞毕业设计或者课程设计的同学应该对课表管理系统这个题目不陌生。每年都有大量学生选它但真正能把SpringBootVue这套技术栈跑通、把课表排课逻辑讲清楚的项目其实并不算多。我前阵子正好完整过了一遍“西安工商学院课表管理系统”这个项目——基于SpringBootVue的源码后端用MyBatis操作MySQL前端是Vue全家桶整体结构清爽非常适合拿来作为Java全栈入门到进阶的练手项目。这篇文章我不打算给你贴一堆源码就完事而是把这套系统的设计思路、数据库表结构、后端关键接口、前端组件实现、部署流程以及我实际踩过的坑全部掰开揉碎讲清楚。1. 项目背景与整体设计思路拆解1.1 课表管理系统到底要解决什么问题很多人觉得课表管理系统就是“把课程信息往页面上一摆”这个理解太浅了。高校课表管理的真实痛点在于几个矛盾的叠加一是数据维度多。一门课涉及班级、教师、教室、时间星期几、第几节、周次单周双周还是全周这些维度交叉起来数据量不大但关系复杂。二是查询场景杂。学生要看“我这周有什么课”老师要查“我周几在哪个教室上课”教务处要维护排课数据这三种角色的视角完全不同。如果系统不做角色区分所有逻辑都揉在一起后期维护会非常痛苦。三是排课冲突检测难。同一时间一个教室不能被两门课占用一个老师不能同时在两个教室上课一个班级也不能拆到两个地方。这几条规则看起来简单但落到SQL和接口逻辑里很容易漏掉边界情况。这套基于SpringBootVue的课表管理系统核心就是围绕以上需求做角色划分管理员负责基础数据维护教师端可以查看和录入个人课表学生端按班级/个人课表查询。后端提供RESTful接口前端按角色渲染不同页面整体是一个典型的后台管理信息查询型Web应用。1.2 为什么选SpringBoot Vue这套黄金组合这个选择其实是“市场主流”和“学习成本”之间权衡的结果。如果你自己搭项目可能会纠结用SSH还是SSM或者前端用JSP还是Thymeleaf。但放到2025年的视角SpringBoot Vue几乎已经成了Java Web项目的工业标准配置。后端用SpringBoot的原因很直接它省掉了Spring MVC时代大量的XML配置内嵌Tomcat一个java -jar就能跑起来对于课表管理这种中小型项目来说开发效率比传统SSM高太多。更关键的是SpringBoot的自动配置机制让MyBatis接入变得异常简单引入依赖、写Mapper接口、加MapperScan三步搞定。前端用Vue而不是JSP核心区别在于前后端彻底分离。JSP时代的页面渲染靠服务端拼接HTML每次点按钮都要刷新页面体验很差。Vue负责前端路由和组件渲染通过axios异步调后端接口拿JSON数据用户在页面上点击“查询课表”时浏览器不会整页刷新只是局部更新数据区域这种体验是传统模板引擎给不了的。另外还有个很实在的考虑Vue的生态太成熟了。Element UI的表格、弹窗、表单组件直接拿来改样式就能用一个课表管理系统里的课程表展示、班级管理、教师管理、教室管理这几个核心页面用Element UI能省下大量前端时间。从项目结构上看这套系统的方案选型还有一个隐性优势分层足够清晰。Controller层只负责接收请求和返回结果Service层专注业务逻辑Mapper层只做数据库操作。这种三板斧结构虽然代码量会多一点点但排查问题的难度会低很多——哪一层出了问题直接看那一层的日志就能定位。2. 数据库设计与表结构解析2.1 核心表结构五张表搞定课表核心逻辑课表管理系统听起来功能不少但落到数据库层面核心就五张表。我画一下实际的建表思路大家对照这个结构去看源码会轻松很多。这里我用MySQL 5.7的InnoDB引擎为例字符集统一用utf8mb4避免中文乱码和emoji存储问题。第一张是admin管理员表字段不多id、username、password、real_name、role、create_time。密码存储这里必须提醒一句用BCrypt加密不要用MD5。MD5加盐倒也不是完全不能做但Spring Security自带的BCryptPasswordEncoder几行代码就接上了安全性和实现成本都比MD5加盐更优。第二张是student学生表核心字段包含id、student_no学号、name、gender、class_id、grade_year。这里class_id要关联到班级表而不是直接存一个字符串班名否则后续统计“某班有哪些学生”需要写LIKE模糊匹配性能很差。第三张是teacher教师表字段相对简单id、teacher_no、name、title职称、department_id、phone。第四张是course课程表这块需要重点设计。字段包括id、course_name、course_code、credit学分、course_type必修还是选修、total_hours总学时。学分和总学时这类信息对于课表渲染没什么用但对于教务管理场景的扩展非常关键源码里保留这些字段是有道理的。第五张是schedule课表核心表这张表的设计直接决定了业务逻辑的复杂度。字段大概是字段名类型说明idbigint主键course_idbigint关联课程表teacher_idbigint关联教师表class_idbigint关联班级表week_daytinyint星期几1-7section_starttinyint开始节次section_endtinyint结束节次week_starttinyint起始周week_endtinyint结束周week_typetinyint0全周 1单周 2双周classroomvarchar教室地点semestervarchar学期标识这张schedule表是整个系统数据层面的灵魂。week_day决定星期section_start和section_end决定第几节到第几节week_start和week_end决定这个课程从第几周上到第几周week_type则用于处理单双周这种高校特有的排课规则。很多初版课表系统不考虑单双周结果后面排课的时候发现有的课程奇数周在A教室、偶数周要在B教室只能再改表结构非常被动。2.2 数据库设计的权衡与避坑数据库设计这块我基于源码和实际落地经验给你几个比较实用的建议。先说说为什么不搞冗余字段。有人会想既然查询课表总是要显示课程名、教师名、班级名那直接把course_name、teacher_name、class_name冗余到schedule表里不就行了吗省得JOIN了。这个做法在小项目里确实能提速但有一个隐患一旦课程名称改了或者教师换校区了你所有冗余字段都要同步更新漏一条数据课表就是错的。课表管理系统的数据特点是“读多写少”用JOIN查询的成本完全可控完全没必要牺牲一致性来换那点性能。再说说索引设计。schedule表最频繁的查询条件是class_id semester其次是teacher_id semester。设计索引时应该建联合索引而不是单独给每个字段建索引。比如idx_class_semester(class_id, semester)它能同时覆盖按班级查课表的场景还能通过最左前缀原则在单独查class_id时生效。看过不少项目源码索引这块经常被忽略数据少的时候没感觉数据到几千条之后查询速度下降特别明显。还有时间字段的坑。create_time、update_time字段建议统一用datetime类型并且让MySQL自己维护不要在前端或者Service层手动set。做法是在建表语句里加上DEFAULT CURRENT_TIMESTAMP和ON UPDATE CURRENT_TIMESTAMP这样插入和更新时完全不用管时间字段数据可靠性更高还不容易因为前后端时区不同导致时间错乱。CREATE TABLE schedule ( id bigint(20) NOT NULL AUTO_INCREMENT, course_id bigint(20) NOT NULL COMMENT 课程ID, teacher_id bigint(20) NOT NULL COMMENT 教师ID, class_id bigint(20) NOT NULL COMMENT 班级ID, week_day tinyint(4) NOT NULL COMMENT 星期几 1-7, section_start tinyint(4) NOT NULL COMMENT 开始节次, section_end tinyint(4) NOT NULL COMMENT 结束节次, week_start tinyint(4) NOT NULL DEFAULT 1 COMMENT 起始周, week_end tinyint(4) NOT NULL DEFAULT 20 COMMENT 结束周, week_type tinyint(4) NOT NULL DEFAULT 0 COMMENT 周类型 0全周 1单周 2双周, classroom varchar(100) DEFAULT NULL COMMENT 上课教室, semester varchar(20) NOT NULL COMMENT 学期标识如2024-2025-2, create_time datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, update_time datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY idx_class_semester (class_id, semester), KEY idx_teacher_semester (teacher_id, semester) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT课表信息表;3. 后端核心实现SpringBoot MyBatis的综合实战3.1 项目分层结构与核心依赖配置这套后端项目的包结构很规范基本是以下这些包controller放接口路由service放业务逻辑mapper放MyBatis的数据库操作接口entity放实体类common放统一返回结果和异常处理config放配置类。依赖方面pom.xml里保留了几个关键的依赖spring-boot-starter-web就不用多说了mybatis-spring-boot-starter负责整合MyBatismysql-connector-java负责连接MySQLlombok用来简化实体类代码减少Getter/Setter的编写还有hutool或fastjson这类工具库处理JSON。这里我建议用hutool它的工具类覆盖比较全课表管理里涉及日期处理、字符串转换的场景它都能减少不少代码量。dependency groupIdorg.mybatis.spring.boot/groupId artifactIdmybatis-spring-boot-starter/artifactId version2.3.1/version /dependency dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId version8.0.33/version /dependency对应的application.yml配置如下。注意MyBatis的mapper-locations路径要和实际资源目录对应如果配错了启动的时候会报Invalid bound statement (not found)错误。server: port: 8080 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/course_schedule?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/Shanghai username: root password: yourpassword mybatis: mapper-locations: classpath:mapper/*.xml type-aliases-package: com.example.courseschedule.entity configuration: map-underscore-to-camel-case: true log-impl: org.apache.ibatis.logging.stdout.StdOutImpl其中map-underscore-to-camel-case这个配置特别重要。你数据库字段是week_day实体类是weekDay如果不开启驼峰映射查询结果里weekDay就是null前端拿不到数据还会以为是接口报错实际是这里没配。3.2 Mapper层设计与SQL解析MyBatis的用法有两种一种是注解写SQL一种是XML写SQL。课表管理这种查询条件比较灵活的项目我更推荐用XML方式。原因很简单注解方式虽然写起来快但SQL一复杂字符串拼在注解里可读性和可维护性都会变差而且没法实现动态SQL那种“条件可选”的效果。可以看看课表查询的核心Mapper实现。比如按条件查询课表前端传过来可能带班级ID、带学期、带教师ID也可能不带某个条件这时候就需要MyBatis的动态SQL来处理。select idselectScheduleList resultTypecom.example.courseschedule.entity.ScheduleVO SELECT s.*, c.course_name, t.name as teacher_name, cl.name as class_name FROM schedule s LEFT JOIN course c ON s.course_id c.id LEFT JOIN teacher t ON s.teacher_id t.id LEFT JOIN class cl ON s.class_id cl.id where if testclassId ! null AND s.class_id #{classId} /if if testteacherId ! null AND s.teacher_id #{teacherId} /if if testsemester ! null and semester ! AND s.semester #{semester} /if /where ORDER BY s.week_day, s.section_start /select这段SQL用 标签包了一层里面每个 都会自动判断参数是否为空为空就跳过这个条件。相比手动拼SQL这种方式既安全又能应对课表管理里查询条件不固定的场景。LEFT JOIN而不是INNER JOIN是为了保证即使关联的教师、班级信息缺失课程记录本身也能查出来前端不至于整条数据消失。还有查询学生个人课表的场景。核心思路是前端传入studentId后端先查出该学生所属的classId再用classId去schedule表查课表数据。这种两步查询可以放在Service层做也可以在Mapper层写一个嵌套子查询直接一步完成。子查询的方式效率更高但可读性略差如下select idselectStudentSchedule resultType... SELECT s.*, c.course_name, t.name as teacher_name FROM schedule s LEFT JOIN course c ON s.course_id c.id LEFT JOIN teacher t ON s.teacher_id t.id WHERE s.class_id ( SELECT class_id FROM student WHERE id #{studentId} ) AND s.semester #{semester} /select这种嵌套子查询在数据量不大的情况下完全够用。本质上就是“先用学生表找到班级再用班级找课表”的两步操作只是并到了一条SQL里能省一次网络往返。这类小优化在课表管理这种需要频繁按学号查课表的场景里体感差距是实在的。3.3 冲突检测的核心业务逻辑课表系统里最容易被低估的就是冲突检测。管理员排课的时候如果同一时间、同一教室已经有别的课系统必须给出提示。源码里的做法很标准在插入schedule记录之前先做一次重叠判断。判断的条件是时间上有交集星期相同、节次区间重叠、资源上有交集教室相同、或者教师相同学时。关键SQL可以这么写select idcountConflict resultTypeint SELECT COUNT(*) FROM schedule WHERE semester #{semester} AND week_day #{weekDay} AND classroom #{classroom} AND NOT (section_end lt; #{sectionStart} OR section_start gt; #{sectionEnd}) if testweekType ! null AND (week_type 0 OR week_type #{weekType}) /if /select这里的核心是区间重叠判断我用的是“NOT结束节次小于新开始节次 OR 开始节次大于新结束节次”这个逻辑等价于两个时间段有交集。这个写法在写课表系统的排课功能时通用性很强也不只适用于教室冲突把classroom字段换成teacher_id就是教师冲突检测换成class_id就是班级冲突检测。比较隐蔽的是单双周的处理。比如A课程单周上周一的1-2节B课程双周也是周一1-2节时间上看起来重叠但实际上并不冲突。如果冲突检测SQL不考虑week_type上课就会误报。处理方式是如果两条课表的week_type都是单周或都是双周确实冲突但如果一个是单周一个是双周即使时间重叠也能共存。上面SQL里ANDweek_type 0 OR week_type #{weekType}这个条件就是用来过滤这个场景的。4. 前端核心实现Vue组件化开发与联调细节4.1 Vue项目结构与路由设计前端部分基于Vue 2 Element UI axios Vue Router的经典组合。之所以用Vue 2而不是Vue 3主要原因是Element UI对Vue 2的支持最成熟网上资料多遇到问题很快能找到解决方案。对课表管理这种以表格和表单为主的中后台项目Vue 2 Element UI完全够用而且对新手更友好没有Composition API那套概念负担。src目录下的结构大概是这样views目录放页面级组件比如Login.vue、AdminDashboard.vue、TeacherSchedule.vue、StudentSchedule.vuerouter目录放路由配置api目录放axios请求封装。页面级组件和通用业务组件的拆分逻辑要清晰像我这种DataTable在多个页面复用的场景抽出来做成一个子组件就很合理如果只是某个页面独有的逻辑就不要强行封装否则过度设计反而增加维护成本。axios请求封装这块课表系统里有一个通用做法把后端返回格式统一成{ code, message, data }axios的响应拦截器里判断code是否为200不是200就弹出错误提示。这样Controller层只需要返回业务数据不用关心错误提示的UI逻辑前端也只需要在拦截器里统一处理错误。源码里是这样接的import axios from axios const service axios.create({ baseURL: /api, timeout: 10000 }) service.interceptors.response.use( response { const res response.data if (res.code ! 200) { this.$message.error(res.message || 请求失败) return Promise.reject(new Error(res.message)) } return res.data }, error { this.$message.error(网络异常请稍后重试) return Promise.reject(error) } ) export default service4.2 课表展示组件的实现思路课表展示是前端功能里最关键的模块核心难点在于要把数据库中一条条的schedule记录转换成二维表格矩阵。行的维度是星期一到星期日列的维度是第1节到第8节每个单元格放课程名称、教师和教室。这里不建议直接用Element UI的el-table来渲染因为课程通常跨多个节次比如1-2节连上需要合并单元格。更靠谱的方案是用CSS Grid或Table布局自己渲染。实现思路是把schedule数据转换成二维数组matrix[weekDay][section]先初始化一个7x8的空白矩阵然后遍历课表数据填充进去。跨节次的课程用rowspan属性合并行。// 将后端返回的课表数据转换为7x8矩阵 buildScheduleMatrix(scheduleList) { const matrix [] for (let day 1; day 7; day) { matrix[day] [] for (let section 1; section 8; section) { matrix[day][section] null } } scheduleList.forEach(item { for (let section item.sectionStart; section item.sectionEnd; section) { matrix[item.weekDay][section] item } }) return matrix }这里还有一个小细节跨周次的显示。比如某门课是单周一三五有课周的维度在二维表格里不好直接呈现。实际项目里的做法是在课程卡片的tooltip里展示周次信息比如悬停单元格会显示“1-16周 单周”或者在单元格里用更小的字体展示周次范围。这种信息层级的设计在课表系统里比在一个格子里塞满所有文字要舒服得多。4.3 前后端联调的关键细节前后端联调阶段有几个问题非常高频我一个个说。第一个是跨域问题。前端开发服务器跑在localhost:8081Vue CLI默认端口或者3000后端跑在localhost:8080浏览器会拦截跨域请求。最简单的处理方案是让后端允许跨域写一个WebMvcConfigurer配置类重写addCorsMappings方法放行所有路径。Configuration public class CorsConfig implements WebMvcConfigurer { Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping(/**) .allowedOrigins(http://localhost:8081) .allowedMethods(GET, POST, PUT, DELETE) .allowedHeaders(*) .allowCredentials(true); } }第二个是时间格式问题。MySQL里的datetime类型通过MyBatis查出来是java.util.Date或者LocalDateTimeJackson序列化成JSON时默认可能是时间戳格式前端拿到后显示成一大段数字。解决方案是在application.yml里全局配置Jackson的日期格式spring: jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT8第三个是请求参数命名问题。Java后端字段用驼峰命名classId、studentNo前端传参时要保持完全一致。如果使用FormData或者query string传参粗细大小写写错了后端接收不到值。这个排查起来比较烦躁建议大家在axios里把所有参数名统一用小驼峰后端实体类字段也统一小驼峰从源头上杜绝这个问题。5. 实操部署全流程从本地跑起来到服务器发布5.1 本地环境准备清单先把环境列清楚这一步省不了Environment出问题后期排查成本反而更高。JDK要用8或11。SpringBoot 2.x版本对JDK 8的支持最稳定很多课程设计项目用的都是JDK 8。如果你本地装的是JDK 17也不是不能跑但需要把SpringBoot版本升级到2.5以上依赖方面会有一点兼容性问题建议直接用JDK 8减少麻烦。MySQL推荐使用5.7或8.0。5.7的用户量大、教程多8.0的窗口函数更强、性能更好但要注意连接驱动需要mysql-connector-java 8.0并且URL里要加serverTimezoneAsia/Shanghai不然插入数据时会报时间相关的错误。Node.js用来跑Vue开发环境推荐装14.x或16.x的LTS版本npm包管理器会随着Node一起装上。这里提醒一下不要追求最新版NodeVue 2的相关依赖对Node版本兼容性有限制太新反而跑不起来。最后是开发工具后端用IntelliJ IDEA社区版免费就够用前端用VS Code。IDEA要装Lombok插件不然实体类里的Data注解不会生效编译时直接报找不到getter/setter方法。5.2 项目启动步骤与验证方法启动步骤不复杂但顺序错了容易迷糊。我按实际操作顺序给你列一下第一步创建数据库。打开MySQL命令行或者Navicat新建一个course_schedule数据库编码选utf8mb4然后把项目里自带的course_schedule.sql文件导入。导入完可以先验证一下执行show tables能看到admin、student、teacher、course、schedule这五个表就算成功。这里尤其强调一下导入操作如果SQL脚本里有中文字段值navicat导入时编码不对会直接乱码一定要把连接编码也设为utf8mb4。第二步改数据库配置。打开后端项目的application.yml修改spring.datasource里的username和password。很多新手在这里会漏掉时区参数如果MySQL是8.0版本URL里少了serverTimezoneAsia/Shanghai启动时会直接抛异常。第三步启动后端。在项目根目录执行mvn spring-boot:run或者在IDEA里直接运行主启动类。看到Spring Boot的启动日志出现“Started Application in x.xxx seconds”就代表启动成功。这里再教你一个小技巧启动成功后可以直接在浏览器访问http://localhost:8080/api/ping如果返回{ code: 200 }就说明接口通了不用急着进前端判断。第四步启动前端。进入frontend目录执行npm install安装依赖第一次装会花几分钟如果装的时候出现node-sass报错多半是Node版本和依赖不兼容升级或者降级Node的LTS版本可以解决。依赖装完后执行npm run serve启动开发服务器默认端口是8081。浏览器打开localhost:8081能看到登录页就说明前端起来了。第五步用测试账号登录。管理员账号一般是admin/admin123学生账号可以看student表里的测试数据。登录后先用管理员账号添加一条课程信息再排一条课表验证一下前端页面能否正常展示课表矩阵。到这一步整个本地环境就算真正跑通了。5.3 打包发布到服务器的核心操作本地跑通之后如果想把项目发布到服务器上需要注意前后端是独立的两个项目。前端打包在frontend目录执行npm run build生成dist目录。这里有一个特别容易踩的坑Vue项目默认的静态资源路径是绝对路径/部署到服务器的子目录下会白屏。解决办法是修改vue.config.js里的publicPath设为./这样构建出来的文件就是相对路径放到Nginx任意目录下都能直接访问。后端打包在项目根目录执行mvn clean package -DskipTests生成target目录下的jar包。把这个jar包上传到服务器执行nohup java -jar course-schedule.jar app.log 21 就可以后台运行了。默认端口8080如果要修改端口记得在application.yml里改完再打包。服务器上建议用Nginx做反代Nginx监听80端口把/api开头的请求转发到本机的8080端口其他请求交给前端静态文件。这个配置很基础但实用性极高server { listen 80; server_name yourdomain.com; root /var/www/course-schedule/dist; index index.html; location /api/ { proxy_pass http://127.0.0.1:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } location / { try_files $uri $uri/ /index.html; } }注意try_files这行Vue Router如果使用History模式刷新页面时路由地址会直接打到Nginx必须通过try_files把未命中的路径都指向index.html否则一刷新就404。如果你不想处理这个情况也可以在Vue Router里用hash模式URL里带#但体验略差。6. 常见问题排查与性能优化心得6.1 启动阶段的经典报错排查实录把这段时间我和学生一起调试时遇到过的问题整理成一份速查表基本覆盖了这类项目99%的报错场景报错现象根本原因解决方案Invalid bound statement (not found)Mapper接口与XML的namespace或id不对应检查Mapper接口的全限定类名与XML中的namespace完全一致数据库连接失败Access denied for user数据库账号密码错误检查application.yml里的username和passwordFailed to configure a DataSource没有配置数据源或数据源配置被扫到了其他环境确认application.yml里spring.datasource配置完整前端页面空白控制台报404静态资源路径配置错误修改publicPath为./重新构建端口被占用8080或8081被其他进程占用用netstat -anonpm install卡住不动npm官方源访问慢设置registry为https://registry.npmmirror.com这里单独展开两个比较隐蔽的问题。第一个是MyBatis的Mapper扫描问题。启动类上必须加MapperScan注解或者每个Mapper接口上加Mapper注解否则MyBatis根本找不到这些接口启动数据库操作时会报Bean找不到的错。很多人项目跑着跑着突然Mapper失效了往往就是加新包时忘了扫描路径。第二个是MySQL 8.0的驱动配置。老项目里用的驱动类是com.mysql.jdbc.DriverMySQL 8.0以后必须换成com.mysql.cj.jdbc.Driver只改pom依赖不换驱动类启动时大概率会直接报ClassNotFoundException。6.2 性能优化课表系统的三大提速方案课表管理系统数据量不算大但还是有几个值得优化的点。我从源码和工程实践的角度挑三个最有价值的说。第一个是查询接口的响应时间优化。课表查询接口最怕的是前端在页面里发起N个请求比如学生登录后需要同时查个人信息、查学期列表、查课表数据三个请求串行发送很慢。正确的姿势是在后端做一个聚合接口一次请求返回studentDTO和scheduleDTO列表前端一个接口搞定。HTTP请求次数少了整个页面加载的体感速度会明显提升。这种聚合接口在SpringBoot里实现很简单Service层把结果塞进一个Map或者定义一个聚合DTO就能搞定。第二个是MyBatis一级缓存和二级缓存的合理使用。课表数据的特点是读多写少非常适合使用MyBatis的二级缓存。一级缓存是SqlSession级别的默认开启但SpringBoot整合MyBatis后每次Mapper调用可能是不同SqlSession所以一级缓存基本形同虚设。二级缓存是Mapper级别的配置方式是在Mapper的XML里加 标签并且要求实体类实现Serializable接口。要注意的是一旦加入了二级缓存课程信息更新后缓存不会自动清理需要调用clearCache()或者在Mapper里配置flushCachetrue。课表管理项目数据变动不频繁开二级缓存对查询性能的提升还是很可观的。第三个是SQL层面避免SELECT *。源码里的Mapper查询基本都是明确列出字段的这样做的好处一方面是减少网络传输数据量更重要的是避免因为表结构变化导致的隐性问题。比如schedule表将来加了remark字段但没更新Mapper前端DTO里根本没有这个字段如果用了SELECT *MyBatis的自动映射阶段可能因为字段对不上而报错。明确指定字段的习惯在后期维护中能少踩很多莫名其妙的坑。6.3 从课表管理系统延伸出去这套架构还能做什么聊点实际经验。课表管理系统虽然听起来只是一个毕业设计级的项目但把它的架构吃透之后延伸出其他系统是非常快的。因为它的核心模式——“基础数据管理 角色化查询 二维表格展示”——本质上覆盖了很多信息管理类系统的共性需求。同一套SpringBoot MyBatis MySQL Vue的代码骨架把schedule表换成会议室预约记录weekDay和section换成日期段和时段就变成“会议室预约管理系统”把student表换成会员表把课表记录换成预约记录就变成了“健身房管理系统的预约子模块”把course换成项目schedule换成排期就能做成一个极简“项目管理排期工具”。所以大家在啃这套源码的时候不要只是把它当作业去糊弄而是去理解它的分层思想、查询模型和前端组件化思路。这些东西才是真正能复用下去的资产。我也建议动手改一改源码。比如试着给课表系统加一个“教室课表”维度在查询条件里加一个room参数前端新增一个按教室维度查看的视图。做完这个小改动你对这套系统的理解会上一整个台阶。课表管理系统的繁琐之处就在于维度交叉把它理顺了你再去写任何信息管理类系统都会顺手很多。我自己做这套项目复盘时最深的体会是课表管理系统最复杂的不是任何单点技术而是“字段之间的关联关系”和“边界情况的处理”。单周还是双周、跨节次还是单节次、学期跨年还是同年这些奇葩情况真实存在代码里如果不去处理上线之后就会有学生反映课表不对。做这类系统先把业务规则理解透再谈技术方案顺序反了后面改动起来是真折腾。
返回列表