news 2026/4/16 16:58:21

基于SpringBoot的Java毕设实战:13个表支撑比赛日程系统的架构设计与避坑指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
基于SpringBoot的Java毕设实战:13个表支撑比赛日程系统的架构设计与避坑指南


背景:13张表是怎么把人逼疯的?

做毕设时,导师一句“比赛日程系统,功能要全”,听起来简单,一拆表就傻眼:
赛事、项目、队伍、队员、裁判、场地、日程、积分、公告、报名、轮空、消息、日志——13张表瞬间到位。
大多数同学第一反应是“堆字段”,外键到处拉,结果:

  • 循环引用:A 表删不掉,B 表查不动,C 表一更新,D 表全锁行
  • N+1 查询:赛程列表接口,for 循环里顺手getTeam().getPlayers(),生产环境 200 ms 能飙到 5 s
  • 事务边界模糊:一个“生成日程”按钮,里层调了 4 个 Service,外层没加@Transactional,回滚只回一半,数据直接乱套

毕设答辩前夜,一边改 SQL 一边哭的场景,懂的都懂。

技术选型:MyBatis 还是 JPA?

多表关联场景,两条路线都能走,但踩坑姿势不同:

维度MyBatisSpring Data JPA
SQL 可控手写 XML,复杂 Join 一目了然@Query或方法名,调试要开 SQL 日志
缓存/懒加载无默认,自己写一级缓存+懒加载,一不小心 N+1
分页手写 count 查询Pageable一行代码搞定
代码量每个表 4 个文件(XML+Mapper)注解实体+Repository 接口即可
学习曲线低,但后期 SQL 爆炸前期爽,后期要懂实体状态、flush、detach

结论:毕设周期短、表关联深、导师要求“规范”,JPA 更香;若后续要极端优化,可再局部写 SQL,两者混用 Spring 也支持。

核心模型:13 张表的关系长这样

先放 ER 简图,混个眼熟:

关键关系一句话总结:
赛事 1-N 项目,项目 1-N 日程,日程 N-N 队伍(中间表 schedule_team),日程 1-1 场地,队伍 N-N 队员(中间表 team_player)……

下面用 JPA 注解落地,只贴核心片段,能跑即可。

1. 赛事实体

@Entity @Table(name = "t_event") public class Event { @Id @GeneratedValue private Long id; private String name; private LocalDate startDate; private LocalDate endDate; @OneToMany(mappedBy = "event", cascade = CascadeType.ALL, orphanRemoval = true) private List<Item> items = new ArrayList<>(); }

2. 项目实体

@Entity @Table(name = "t_item") public class Item { @Id @GeneratedValue private Long id; private String itemName; // 如“男子篮球” @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "event_id") private Event event; @OneToMany(mappedBy = "item") private List<Schedule> schedules = new ArrayList<>(); }

3. 日程实体(最复杂)

@Entity @Table(name = "t_schedule") public class Schedule { @Id @GeneratedValue private Long id; private LocalDateTime startTime; private LocalDateTime endTime; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "item_id") private Item item; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "venue_id") private Venue venue; @ManyToMany @JoinTable(name = "schedule_team", joinColumns = @JoinColumn(name = "schedule_id"), inverseJoinColumns = @JoinColumn(name = "team_id")) private Set<Team> teams = new HashSet<>(); }

注意:

  • 全部用LAZY,只在 Service 里写JOIN FETCH解决 N+1
  • 多对多再建中间表,避免双向List造成无限递归 JSON
  • 实体类绝不暴露toString()含关联字段,栈溢出警告

DTO 转换:不让 Entity 裸奔

Controller 直接返回 Entity,会:

  1. 把懒加载代理拖进 Jackson,秒抛LazyInitializationException
  2. 暴露内部字段,循环引用 500

套路:MapStruct 一行注解搞定

@Mapper(componentModel = "spring") public interface ScheduleMapper { ScheduleDto toDto(Schedule s); List<ScheduleDto> toDto(List<Schedule> list); }

DTO 里只留venueNameteamNames等扁平字段,前端开心,后端安全。

事务与性能:生成日程的正确姿势

需求:根据“项目+轮次”一键生成 48 条日程,涉及 4 张表写操作。
下面代码演示“事务+批量+幂等”三位一体:

@Service @RequiredArgsConstructor public class ScheduleGenService { private final ScheduleRepository scheduleRepository; private final VenueRepository venueRepository; private final IdGenerator idGenerator; // 雪花算法,保证幂等 @Transactional // 1. 事务边界 public List<Schedule> generate(Item item, int rounds){ // 2. 幂等:先查重 if(scheduleRepository.countByItemId(item.getId()) > 0){ throw new BizException("日程已存在,禁止重复生成"); } List<Venue> venues = venueRepository.findAllBySport(item.getSport()); List<Schedule> batch = new ArrayList<>(rounds * venues.size()); for(int i=0; i<rounds; i++){ for(Venue v : venues){ Schedule s = new Schedule(); s.setId(idGenerator.nextId()); // 3. 提前设主键,批量插入可走 JDBC 批处理 s.setItem(item); s.setVenue(v); s.setStartTime(calcStartTime(i, v)); batch.add(s); } } // 4. 批量保存 scheduleRepository.saveAll(batch); return batch; } }

要点:

  • 事务只加在写服务,读接口不加,减少锁范围
  • 提前分配主键,MySQL 批插入rewriteBatchedStatements=true秒级 1w+
  • 接口幂等靠业务键(itemId+round),不是单纯依赖数据库唯一索引

安全三板斧

  1. SQL 注入:JPA 只要用方法名或参数绑定:xxx,基本免疫;手写@Query也杜绝拼接
  2. 批量更新:MySQL 的on duplicate key update配合JpaRepository@Modifying注意分片,一次 500 条最稳
  3. 接口幂等:除业务判断外,前端点“生成”按钮后置灰+UUID 令牌,后端用 RedisSETNX做双重校验,防重放

生产环境避坑清单

  • 双向关联绝不写cascade = CascadeType.ALL了事,级联删除一跑,半库数据蒸发
  • 枚举字段统一@Enumerated(STRING),防止序号移位全崩
  • 逻辑删除加deleted字段,手写WHERE deleted = 0拦截,JPA 2 级缓存不会自动过滤,记得配@Where
  • 多对多中间表别加业务字段,一旦加字段就升成实体,否则后续补字段全表锁
  • 生产环境打开spring.jpa.show-sql=false,用 datasource-proxy 慢查询日志替代,别让控制台刷屏把性能吃光

可扩展思考:赛制说改就改,怎么办?

当前模型只支持“单循环+积分制”,如果导师突然说“加淘汰赛、加复活赛”,硬编码if/else直接爆炸。
留给读者的作业:

  1. 把“赛制”抽象成 Strategy 接口,提供ScheduleStrategy.generate()
  2. 项目表加strategy_type字段,与 Spring 的@Strategy自动装配联动
  3. 新建模块schedule-strategy-elimination,遵循相同 DTO,做到“热插拔”

先动手把生成日程 Service 拆成“规则引擎+执行器”,跑通单元测试,你就能在答辩时自信回答“系统支持任意赛制”——导师微笑,你稳过。


把 13 张表拆干净、事务扣稳、接口拍平,比赛日程系统就不再是“毕设噩梦”,而是简历上能吹的亮点。
代码给你了,坑也标好了,下一步要不要把“淘汰赛”模块真正写出来,就看你想不想让自己的毕设从 80 分跳到 95 分。


版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/15 15:32:55

企业级Java运行时:阿里巴巴Dragonwell17深度解析

企业级Java运行时&#xff1a;阿里巴巴Dragonwell17深度解析 【免费下载链接】dragonwell17 Alibaba Dragonwell17 JDK 项目地址: https://gitcode.com/gh_mirrors/dr/dragonwell17 阿里巴巴Dragonwell17是基于OpenJDK深度优化的企业级Java运行时&#xff0c;专为大规模…

作者头像 李华
网站建设 2026/4/16 0:31:24

3个系统方案:彻底解决WebPShop插件兼容性问题

3个系统方案&#xff1a;彻底解决WebPShop插件兼容性问题 【免费下载链接】WebPShop Photoshop plug-in for opening and saving WebP images 项目地址: https://gitcode.com/gh_mirrors/we/WebPShop WebPShop插件作为Photoshop处理WebP格式的关键工具&#xff0c;其兼容…

作者头像 李华
网站建设 2026/4/16 11:03:19

企业流程自动化新范式:Workflow开源低代码审批系统全解析

企业流程自动化新范式&#xff1a;Workflow开源低代码审批系统全解析 【免费下载链接】Workflow 仿钉钉审批流程设置 项目地址: https://gitcode.com/gh_mirrors/work/Workflow 在数字化转型加速的今天&#xff0c;企业流程管理正面临效率与灵活性的双重挑战。传统审批系…

作者头像 李华
网站建设 2026/4/16 9:07:39

USBASP烧录器固件升级与Arduino IDE兼容性优化指南

1. USBASP烧录器固件升级的必要性 如果你手头有一个2018版的USBASP烧录器&#xff0c;可能会遇到一个尴尬的问题&#xff1a;它只能在Windows系统下使用智峰的progisp软件&#xff0c;而无法被Arduino IDE识别。这种情况在Linux系统下尤其常见&#xff0c;很多开发者发现自己的…

作者头像 李华
网站建设 2026/4/16 9:08:48

毕业设计软件技术选型指南:从单体架构到微服务的实战避坑

毕业设计软件技术选型指南&#xff1a;从单体架构到微服务的实战避坑 摘要&#xff1a;许多毕业生在完成毕业设计时&#xff0c;常因缺乏工程经验而在技术选型、架构设计和部署流程上踩坑&#xff0c;导致项目难以演示或扩展。本文聚焦“毕业设计软件技术”场景&#xff0c;系统…

作者头像 李华