尧图精选

Spring Boot + MyBatis + Thymeleaf 实现同学录系统开发实战

🕒 发布时间:2026/9/12 1:03:54 📁 来源:尧图网络
简介一份基于Spring Boot MyBatis MySQL Thymeleaf 的同学录管理系统毕业设计源码包面向计算机相关专业毕业生或需要完成课程设计的学生。项目覆盖了前后端完整实现包含学生信息管理、班级管理、登录注册等典型功能模块适合用来理解SSM整合与Thymeleaf模板引擎在实际业务中的落地方式。压缩包内共111个文件以61个Java源码文件为主搭配14个HTML页面、6个XML配置、5个JavaScript脚本、2个properties配置及SQL脚本等整体结构清晰便于直接导入开发工具后运行调试。资源大小仅1.34MB轻量易获取已有181人学习浏览。通过该资源可以快速获得一套可运行的毕业设计项目包括数据库建表语句、前后端交互代码和页面样式既能作为论文写作的支撑材料也能在此基础上扩展功能或调整界面减少从零搭建系统的时间成本。1. 同学录系统为什么值得拆开看同学录系统在毕业设计里看起来很“轻”无非是登录注册、班级列表、学生详情加几条留言。但真把一个 Spring Boot MyBatis MySQL Thymeleaf 的完整项目跑起来并讲清楚你会发现它几乎覆盖了服务端渲染 Web 应用的所有高频考察点关系建模、动态 SQL、会话保持、模板渲染、静态资源映射。这个压缩包的页面文件里同时出现了 index.html、login.html、classes-manage.html、student-detail.html 和 admin-index.html说明它不只是学生端浏览还带了班级管理和后台视图业务面比一般 CRUD Demo 要宽。适合读这篇文章的人有两类一类是拿这个项目当模板、准备改造成自己毕设的在校生需要知道每个目录和文件的作用以及怎么改参数才能跑起来另一类是工作后偶尔接私活或带新人的工程师想快速判断 Spring Boot 2.x Thymeleaf 这类老组合在现有环境下的兼容性边界。下面从选型逻辑讲起再到数据库设计和每一层的落地代码最后把最容易卡住的 MyBatis 与 Thymeleaf 细节单独拿出来说。2. Spring Boot 与 MyBatis、Thymeleaf 的组合逻辑2.1 为什么是 Thymeleaf 而不是 JSP 或前后端分离JSP 在传统 SSM 项目里很常见但 Spring Boot 官方对 JSP 的支持一直不算顺需要额外配置 servlet 容器对 .jsp 的解析打包成 jar 时还有一堆限制。Thymeleaf 是纯模板引擎天然内嵌在 Spring Boot 的 autoconfigure 体系里加一个 spring-boot-starter-thymeleaf 依赖就能用不需要额外配置视图解析器。模板文件放在 src/main/resources/templates 下静态资源放 static 下结构非常规整。前后端分离这种方案对毕设来说工程量更大需要 Vue 或 React 工程、跨域处理、REST API 文档。同学录系统这种以页面跳转为主的场景用户点开一个班级服务端直接把带数据的 HTML 返回体验上没问题代码量也少得多。Thymeleaf 的一个额外优势是模板文件可以用浏览器直接打开看静态效果虽然动态数据渲染不出来但页面布局和 CSS 引用关系是可见的这对调试前端样式很友好。2.2 MyBatis 在 Spring Boot 中的定位MyBatis 的定位是半自动 ORMSQL 由你写映射由框架做。对比 Hibernate它的学习曲线平缓SQL 可控性强出了问题你能直接看到 SQL 语句和参数不像 Hibernate 那样需要理解 HQL 与 SQL 的隐式转换。这个项目从 SSM 演进到 Spring Boot核心包名和 Mapper 写法几乎可以平移相当于把 Spring 和 SpringMVC 的 XML 配置换成自动配置这对参考网上的 SSM 老教程很有帮助。我一般会把 MyBatis 分成两层看接口层定义方法签名XML 层写 SQL。Spring Boot 启动类上标注 MapperScan 指定 Mapper 接口所在包框架会自动扫描并生成代理对象。XML 文件如果放在 src/main/resources/mapper 下需要在 application.yml 里配置 mapper-locations 指过去否则启动会报 Invalid bound statement 错误。这是新手最容易踩的第一个坑。2.3 工程目录结构与 starter 依赖核心依赖配置在 pom.xml 里这里给出一个完整可用的最小集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 groupIdcom.mysql/groupId artifactIdmysql-connector-j/artifactId scoperuntime/scope /dependencymybatis-spring-boot-starter 需要显式写版本号因为它不走 Spring Boot 的依赖管理。2.3.2 版本兼容 Spring Boot 2.5 到 2.7如果你本地是 Spring Boot 3.x需要换成 3.0 以上的 mybatis starter同时注意 javax.servlet 到 jakarta.servlet 的包名迁移。mysql-connector-j 是 MySQL 8.x 驱动的新坐标旧坐标 mysql-connector-java 也能用但新项目建议直接用前者。对应到源码包里的页面templates 目录下至少有 login.html、register.html、index.html、classes-manage.html、classes-student-manage.html、classes-detail.html、student-detail.html、admin-index.html 和 admin-detail2.html。static 下的 common.css 负责全局样式。这样一个工程的职责边界就清晰了Controller 控制页面跳转和数据填充Service 处理业务Mapper 操作数据库Thymeleaf 负责呈现。3. 核心表设计、Mapper 接口与动态 SQL3.1 同学录的关系模型三张表打底同学录的核心业务对象有三个班级、学生、留言。班级和学生是 1 对 N一个学生有姓名、联系方式、入学年份、性格标签、留言板等属性。admin-detail2.html 这种页面说明系统里还存在管理员角色所以还需要一张简单的用户表用 role 字段区分普通用户和管理员。表名关键字段说明class_infoid, class_name, grade, head_teacher, create_time班级基本信息grade 表示届别studentid, class_id, name, phone, email, personality, photo_url同学信息class_id 关联班级messageid, student_id, from_user, content, create_time同学留言挂在某个同学名下userid, username, password, role登录与权限role 区分 admin 和 student建表 SQL 中需要特别注意外键的两种处理方式物理外键约束会带来插入顺序问题删除班级前必须先删学生和留言业务代码里要处理级联逻辑外键也就是只建索引不建约束灵活度高一些适合毕业设计这种通过 Service 层保证数据一致性的场景。我一般推荐后者因为 MyBatis 的批量插入和分页在无物理外键时更少踩坑。CREATE TABLE class_info ( id INT AUTO_INCREMENT PRIMARY KEY, class_name VARCHAR(50) NOT NULL, grade VARCHAR(20), head_teacher VARCHAR(20), create_time DATETIME DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE student ( id INT AUTO_INCREMENT PRIMARY KEY, class_id INT NOT NULL, name VARCHAR(20) NOT NULL, phone VARCHAR(20), email VARCHAR(50), personality VARCHAR(255), photo_url VARCHAR(255), INDEX idx_class_id (class_id) );phone 用 VARCHAR 而不是 BIGINT是因为手机号可能包含 86 前缀或分隔符数字类型会丢弃前导零。create_time 用 DATETIME 而不是 TIMESTAMPDATETIME 的范围更宽且不受数据库时区影响对同学录这种数据量小的系统足够用。3.2 Mapper 接口与 XML 的动态 SQL 细节Mapper 接口定义方法XML 实现 SQL这是 MyBatis 的标准用法。classes-student-manage.html 对应的查询逻辑是进入一个班级详情页展示该班所有学生支持按姓名搜索。这个场景用动态 SQL 最合适。Mapper public interface StudentMapper { ListStudent selectStudentsByClassId(Param(classId) Integer classId, Param(name) String name); }select idselectStudentsByClassId resultTypecom.example.classbook.entity.Student SELECT id, class_id AS classId, name, phone, email, personality, photo_url FROM student WHERE class_id #{classId} if testname ! null and name ! AND name LIKE CONCAT(%, #{name}, %) /if ORDER BY id /select这里至少有三个细节值得展开。第一resultType 映射时如果实体类属性名和数据库列名不一致比如 classId 与 class_id可以用 AS 别名映射也可以在 mybatis-config 里开启 mapUnderscoreToCamelCase 全局配置。前者直观后者省事但注意全局配置只对 resultType 自动映射生效不改变 SQL。第二LIKE 查询用 CONCAT 拼接而不是直接写 %#{name}%。因为没有引号包裹直接用占位符会被解析成参数占位SQL 变成 WHERE name LIKE %%{name}%语法直接报错。用 CONCAT 是 MyBatis 里处理模糊查询的标准写法。第三 标签测试字符串时name ! 对 null 值不会报错但如果你用 OGNL 写name ! null and name ! 注意空字符串判断在 MySQL 里对空格敏感的实际情况。搜索框输入一个空格SQL 会查出所有名字包含空格的人这往往不是预期行为可以在 Service 层做 trim。3.3 单个数字字符比较的坑与修复方式搜索热词里有“mybatis 单个数字字符比较”这个坑出现在 这类判断上。在 MyBatis 的 OGNL 表达式中如果 grade 变量本身是 Character 类型或者你的查询条件是传给一个数字字符串直接写testgrade ! 0会报 NumberFormatException 或将字符按数字比较。常见做法是先把传入参数在 Service 层转成 String再做判断或者 XML 里写成if testgrade ! null and grade.toString() ! 0 AND grade #{grade} /if原因在于 OGNL 对单字符字符串与 Character 的自动转换逻辑0 被解析为 char与 String 比较时触发强制类型转换。用 toString() 可以绕开这个解析分支。这个细节在静态代码审查时很难发现只有真实跑分页或筛选才会暴露。3.4 MyBatis 缓存级别与同学录场景的取舍MyBatis 一级缓存是 SqlSession 级别的同一个会话内两次相同的查询会命中缓存不查数据库。二级缓存是 Mapper 级别的跨会话共享但需要注意脏读问题如果你在一个事务里更新了某条学生记录另一个会话的二级缓存可能还持有旧值。同学录系统以读为主班级详情页会被反复打开开启二级缓存能省不少查询cache evictionLRU flushInterval60000 size512 readOnlytrue/readOnlytrue 对同学录这种不要求修改缓存的场景性能更好但如果有更新操作必须保证刷新策略及时。我的建议是班级表可以开二级缓存student 表和 message 表尽量不开因为留言和同学信息变动频繁缓存失效控制不好会产生“明明改了数据页面还是老样子”的诡异问题。真遇到这种问题第一反应不是查缓存配置而是先检查当前 SQL 是否命中了 MyBatis 的缓存再看 Thymeleaf 模板是否开启了页面级缓存。4. Spring Boot 业务层搭建与 Thymeleaf 页面渲染4.1 application.yml 里的关键配置项Spring Boot 通过自动配置大幅简化了 SSM 工程里繁琐的 XML 配置但数据源、视图缓存、SQL 日志这些还是要显式声明。下面是这个项目最可能的配置文件形态server: port: 8080 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/classbook?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: 123456 thymeleaf: cache: false suffix: .html mybatis: mapper-locations: classpath:mapper/*.xml type-aliases-package: com.example.classbook.entity configuration: map-underscore-to-camel-case: truedriver-class-name 在 MySQL 8.x 下必须用 com.mysql.cj.jdbc.Driver旧版 com.mysql.jdbc.Driver 虽然能跑但会打印弃用警告。url 里的 serverTimezoneAsia/Shanghai 是必填项否则 JDBC 驱动会报 CST 时区无法识别的异常。mybatis.mapper-locations 指向 XML 文件所在路径如果 Mapper 接口和 XML 同名且在同一包下也可以省略但分开放更清晰。spring.thymeleaf.cache 在开发阶段必须设为 false。Thymeleaf 默认开启模板缓存你改了 HTML 后刷新页面看不到效果还以为是浏览器缓存其实是服务端把模板编译结果缓存了。线上部署时再改回 true。4.2 Controller 层的页面跳转与数据填充同学录系统的 Controller 要处理两类请求一类是页面跳转返回模板名另一类是表单提交处理完重定向。看压缩包里的页面命名登录注册和后台管理模块齐全Controller 至少包含 LoginController、ClassController 和 StudentController。以班级管理为例Controller RequestMapping(/class) public class ClassController { Autowired private ClassService classService; PostMapping(/save) public String save(ClassInfo classInfo) { classService.saveOrUpdate(classInfo); return redirect:/class/manage; } GetMapping(/{id}/students) public String students(PathVariable Integer id, Model model) { model.addAttribute(classInfo, classService.getById(id)); model.addAttribute(students, studentService.listByClassId(id)); return classes-student-manage; } }PostMapping(/save) 处理表单提交saveOrUpdate 方法内部判断 id 是否为 null为 null 则新增否则更新。这种一个方法处理插入和更新的写法在毕设项目里很常见虽然不符合严格的设计模式但代码量少、好理解。redirect 关键字告诉 Spring MVC 发送 302 重定向避免表单重复提交这是 Thymeleaf 项目里非常推荐的做法。model.addAttribute 添加的对象在模板中直接用属性名访问classes-student-manage.html 里用 th:each 遍历 student 列表时就是遍历这里放入的 students。4.3 Thymeleaf 模板语法在页面中的实际写法register.html 和 login.html 涉及表单classes-manage.html 涉及列表展示。Thymeleaf 的核心语法就是 th:text、th:each、th:if、th:href 这几个标签属性。写一个学生列表页的片段tbody tr th:eachstu : ${students} td th:text${stu.name}张三/td td th:text${stu.phone}13800000000/td td th:text${stu.personality}安静/td td a th:href{/student/detail/{id}(id${stu.id})}查看详情/a /td /tr /tbodyth:each 类似于 JSP 的 forEachstu 是循环变量每次迭代从 students 列表中取一个 Student 对象。th:text 的作用是把标签体内容替换成表达式的值浏览器直接打开 HTML 时看到的是占位文本张三服务端渲染后变成真实数据。th:href 使用 {} 语法拼接 URL路径变量用 {id} 占位括号中传值生成 /student/detail/3 这样的地址。登录状态的保持有两种选择。如果是纯服务端渲染用 HttpSession 存储登录用户controller 里加拦截器判断 session 是否为空为空就重定向到 login.html。如果做了简单的 AJAX 请求可以用 Cookie但 Thymeleaf 模板引擎渲染的页面本来就以服务端跳转为主直接用 Session 更匹配。admin-index.html 与 admin-detail2.html 是管理员的专属页面拦截器配置时注意放行 /login、/register 和 /static/**否则登录页的 CSS 都无法加载。4.4 表单提交、参数绑定与页面回显login.html 和 register.html 的提交逻辑Spring Boot 的表单参数绑定做的很干脆。以注册为例PostMapping(/register) public String register(User user, Model model) { if (userService.findByUsername(user.getUsername()) ! null) { model.addAttribute(msg, 用户名已被注册); return register; } userService.register(user); return redirect:/login; }前端表单 input 的 name 属性必须与 User 实体类的属性名完全一致Spring MVC 才能自动完成参数绑定。校验失败时通过 model.addAttribute 返回提示信息模板里用 th:if 判断 msg 是否存在再显示避免每次刷新都弹出空白提示。这里容易被忽略的是字符编码问题。表单提交遇到中文乱码先检查页面 meta 标签和服务器端编码过滤器。Spring Boot 的 CharacterEncodingFilter 默认以 UTF-8 处理请求但如果你的 MySQL 表是 latin1 或 gbk 编码数据写进去后读出来就是乱码。建表时统一用 utf8mb4 能在源头上解决问题。5. 项目启动、数据验证与容易翻车的细节同学录系统跑不起来的场景按发生频率排序MySQL 连不上、MyBatis XML 路径配置错误、Thymeleaf 模板解析异常、端口占用。先给一个通用的验证路径再单独说几个真正的知识点。常见的坑可以从这几个方向排查。第一MySQL 安装配置教程里强调的 root 密码和端口本地连不上时先看 3306 端口是否监听命令行执行mysql -uroot -p确认能进入。第二Spring Boot 版本太高导致 mybatis-spring-boot-starter 不兼容启动日志出现 ClassNotFoundException 就检查 starter 版本与 Boot 版本是否匹配。第三thymeleaf 模板里写了错误的表达式启动时不报错访问页面才报 SpelEvaluationException 或 TemplateInputException此时看控制台的具体行号和表达式片段。MyBatis 控制台打印 SQL 是一个很实用的排错手段。在 application.yml 里追加logging: level: com.example.classbook.mapper: debug这样 MyBatis 会把每个 Mapper 方法最终执行 SQL 和传入参数打印出来。看日志时重点关注预编译 SQL 中 ? 的数量是否与参数个数一致。比如 selectById 方法的 SQL 打印为 WHERE id ?参数为 [3]说明绑定正常如果打印出 WHERE id 3 且没有 ? 说明你用了 ${} 做了字符串拼接这种情况下只要参数是用户输入就有 SQL 注入风险不建议这样做。关于 MyBatis 动态 SQL 的另一个高频问题是批量操作。给班级批量导入学生时常规循环逐条 insert 很慢且产生大量日志。使用 mybatis 进行批量写操作时我一般用 foreach 拼接批量插入insert idbatchInsert INSERT INTO student (class_id, name, phone) VALUES foreach collectionlist itemstu separator, (#{stu.classId}, #{stu.name}, #{stu.phone}) /foreach /insertMySQL 对单条 INSERT 的 VALUES 个数没有硬性限制几千条以内批量执行通常没问题而且比单条插入快得多。注意 batchInsert 方法返回的是影响行数而不是自增主键如果后续逻辑需要拿到新生成的学生 id得改用 useGeneratedKeys 逐条插入或插入后再查一次。最后说一个 Thymeleaf 时间格式化的细节。MySQL 的 DATETIME 取出后是 java.util.Date直接在页面显示是 Tue Dec 03 10:00:00 CST 2024 这种英文格式。用 Thymeleaf 自带的日期格式化工具类可以解决td th:text${#temporals.format(message.createTime, yyyy-MM-dd HH:mm)}2024-12-03 10:00/td使用 #temporals 需要模板页引入 thymeleaf-extras-java8time 依赖Spring Boot 的 thymeleaf starter 默认不包含它。如果不方便加依赖另一种做法是在实体类中增加一个 String 类型的 createTimeStr 字段查询时用 DATE_FORMAT(create_time, %Y-%m-%d %H:%i) 生成再直接输出。两种方式都在实际项目里见过前者更优雅后者改动更小按你自己的依赖管理习惯取舍即可。还有一点admin-index.html 这类管理页面里的表格通常会显示班级创建时间同学录里还会显示毕业年限。用 #temporals 处理时注意时区与 MySQL 的 serverTimezone 保持一致否则页面显示的时间会比数据库早 8 小时。排查这类问题时先把 yml 里的 serverTimezoneAsia/Shanghai 确认一遍再看 Java 进程的系统时区是不是 UTC。本文还有配套的精品资源点击获取
上一篇/下一篇内容由系统自动关联 返回资讯列表 →