Spring Boot宠物管理系统实战:从JPA到MyBatis-Plus工程落地
简介这是一套面向Java全栈初学者与毕业设计学生的Spring Boot宠物信息管理实战项目聚焦宠物领养、寄养、用品销售及数据统计等核心业务场景助力开发者快速掌握前后端分离开发全流程。资源包含完整源码与配套实操视频技术栈涵盖Spring Boot后端、Vue前端及MySQL数据库功能模块覆盖公告管理、宠物档案、房间调度、领养/寄养记录、订单处理、发现流浪宠物及多维领养统计分析。压缩包共2000个文件以917个Markdown文档含需求说明、接口设计、部署指南、890个JavaScript文件Vue组件与交互逻辑及181个JSON配置为主整体93.62MB结构清晰、注释充分便于分模块学习与二次开发。目前已有333人下载学习附带详细视频录制直观演示环境搭建、功能调试与关键代码实现过程显著降低上手门槛。1. 为什么一个“宠物信息管理系统”值得用 Spring Boot 重做一遍不是所有毕设都配得上“可落地”三个字。但当你在宠物医院前台看到护士手动翻 Excel 记录疫苗时间、在社区流浪猫救助群反复核对领养人电话、甚至用纸质登记本管理绝育手术排期时你就知道一个轻量、可扩展、能跑在普通云服务器上的 Java 系统真不是玩具——它缺的不是功能而是工程化表达。这个标题里的“基于 Spring Boot 宠物信息管理系统”本质是把「动物个体档案 人宠关系 医疗服务节点」三类数据用现代 Web 工程方式串起来用 JPA 做实体生命周期建模比如一只猫从收容、体检、绝育到被领养的全过程用 RESTful 接口暴露核心操作如/api/pets/{id}/vaccinate再靠 Thymeleaf 或 Vue 做分层视图。它不追求高并发但必须清晰体现 Spring Boot 的自动配置优势比如spring-boot-starter-data-jpa自动装配 HikariCP 连接池、领域驱动分层PetService不该直接 newPetRepository、以及真实业务约束同一身份证号不能重复领养、疫苗有效期需校验时间逻辑。适合刚学完 Java 基础和 MySQL 的学生补全工程链路也适合 Java 初级开发者验证自己对 Controller/Service/Repository 分层的理解是否经得起修改——比如把“添加宠物”改成支持批量导入 Excel你得知道该在哪一层加Transactional又该在哪一层处理DateTimeParseException。2. 用 Spring Boot 3.2 JDK 17 搭建最小可运行骨架2.1 为什么选 Spring Boot 3.2 而不是 2.xSpring Boot 3.2 是当前 LTS 版本中兼容性最稳、文档最全的生产就绪版本。它强制要求 JDK 17这意味着你可以合法使用sealed类、record作为 DTO、switch表达式等现代语法避免在毕设代码里写一堆if-else判断宠物类型猫/狗/兔。更重要的是3.2 默认启用 Jakarta EE 9 命名空间jakarta.persistence.*替代javax.persistence.*如果你后续要集成 Hibernate Validator 或 Liquibase不会因包名冲突导致NoClassDefFoundError。而 Spring Boot 2.7 已于 2023 年 11 月停止维护继续用它意味着无法获取安全补丁——这点在毕设答辩时被问到“为什么不用新版本”你得有技术依据而不是只说“老师教的是 2.x”。提示不要用start.spring.io默认生成的 Spring Boot 3.3.x2024 年 5 月刚 GA它的spring-boot-starter-webflux在 Windows 下偶发启动失败且部分 IDE 插件尚未完全适配。3.2.6 是截至 2024 年 6 月最稳妥的选择。2.2 创建项目并初始化核心依赖用 IntelliJ IDEA 创建新项目时选择Spring Initializr确保 URL 为https://start.spring.ioProject SDK 设为 JDK 17Spring Boot 版本选3.2.6。勾选以下依赖依赖名作用必填理由Spring Web提供RestController、RequestMapping等基础 Web 支持所有 HTTP 接口的基石Spring Data JPA封装 Hibernate提供JpaRepository接口宠物、主人、医生等实体的 CRUD 操作H2 Database内存数据库启动即用无需安装毕设演示阶段免去 MySQL 配置麻烦Lombok用Data、Builder减少样板代码避免手写toString()导致Pet{id1, name咪咪}打印混乱Validation提供NotBlank、PastOrPresent等注解校验领养日期不能是未来时间生成后检查pom.xml中java.version是否为17并确认spring-boot-starter-data-jpa的groupId是org.springframework.boot非org.springframework.data。2.2.1 配置 application.yml 启用 H2 控制台spring: datasource: url: jdbc:h2:mem:petdb driver-class-name: org.h2.Driver username: sa password: h2: console: enabled: true path: /h2-console jpa: database-platform: org.hibernate.dialect.H2Dialect hibernate: ddl-auto: create-drop show-sql: true properties: hibernate: format_sql: true这段配置的关键点在于ddl-auto: create-drop表示每次启动应用时重建表结构适合开发阶段快速迭代比如新增Pet.vaccinationStatus字段后无需手动删库show-sql: true和format_sql: true让控制台输出格式化 SQL方便你确认OneToMany关系是否生成了正确的外键约束path: /h2-console暴露 H2 控制台启动后访问http://localhost:8080/h2-console填入 JDBC URLjdbc:h2:mem:petdb即可直接查表。2.3 定义核心实体用 JPA 注解表达真实业务约束宠物管理系统的核心不是“增删改查”而是如何用代码描述现实规则。例如Entity Table(name t_pet) public class Pet { Id GeneratedValue(strategy GenerationType.IDENTITY) private Long id; NotBlank(message 宠物名称不能为空) Column(length 20) private String name; Enumerated(EnumType.STRING) Column(name species) private Species species; // 枚举CAT, DOG, RABBIT PastOrPresent(message 出生日期不能是未来) Column(name birth_date) private LocalDate birthDate; ManyToOne(fetch FetchType.LAZY) JoinColumn(name owner_id, nullable false) private Owner owner; // 一只宠物必须有主人 OneToMany(mappedBy pet, cascade CascadeType.ALL, orphanRemoval true) private ListMedicalRecord medicalRecords new ArrayList(); }关键细节说明Enumerated(EnumType.STRING)确保数据库存的是CAT而非数字0便于后期 SQL 查询和前端展示PastOrPresent校验出生日期比NotNull更贴近业务不能让系统允许录入“2025年出生的猫”ManyToOne(fetch FetchType.LAZY)防止查询宠物时意外加载全部主人信息N1 问题nullable false强制关联存在cascade CascadeType.ALL表示删除宠物时自动删除其所有就诊记录orphanRemoval true保证从medicalRecords列表中移除某条记录后数据库对应行也被删除。3. 实现领养流程闭环从接口定义到事务边界控制3.1 设计 RESTful 接口路径与语义领养行为不是简单插入一条记录而是涉及主人创建 → 宠物绑定 → 领养时间戳记录三个原子操作。按 REST 规范应设计为方法路径说明POST/api/owners创建新主人含身份证号唯一校验POST/api/pets创建新宠物需指定ownerIdPOST/api/adoptions发起领养参数petId,ownerId,adoptionDate注意不提供/api/pets/{id}/adopt这样的 PUT 接口因为领养是独立业务事件可能涉及合同生成、费用结算等后续扩展点单独资源更利于演进。3.2 编写 AdoptionService用Transactional划定事务边界Service public class AdoptionService { private final OwnerRepository ownerRepository; private final PetRepository petRepository; private final AdoptionRepository adoptionRepository; public AdoptionService(OwnerRepository ownerRepository, PetRepository petRepository, AdoptionRepository adoptionRepository) { this.ownerRepository ownerRepository; this.petRepository petRepository; this.adoptionRepository adoptionRepository; } Transactional public Adoption adoptPet(Long petId, Long ownerId, LocalDate adoptionDate) { // 1. 检查宠物是否存在且未被领养 Pet pet petRepository.findById(petId) .orElseThrow(() - new EntityNotFoundException(宠物不存在: petId)); if (pet.getAdoptionDate() ! null) { throw new IllegalStateException(该宠物已被领养); } // 2. 检查主人是否存在 Owner owner ownerRepository.findById(ownerId) .orElseThrow(() - new EntityNotFoundException(主人不存在: ownerId)); // 3. 更新宠物状态并保存领养记录 pet.setAdoptionDate(adoptionDate); pet.setOwner(owner); // 维护双向关联 petRepository.save(pet); Adoption adoption Adoption.builder() .pet(pet) .owner(owner) .adoptionDate(adoptionDate) .build(); return adoptionRepository.save(adoption); } }这段代码的事务控制要点Transactional加在 service 方法上确保整个领养过程要么全部成功要么全部回滚比如更新宠物状态成功但保存领养记录失败时宠物状态也会被撤销显式调用petRepository.save(pet)而非依赖级联因为Pet实体的adoptionDate字段是业务关键状态必须由当前方法明确控制EntityNotFoundException和IllegalStateException是 Spring Boot 默认支持的异常会自动转为404 Not Found和409 ConflictHTTP 状态码无需额外配置ControllerAdvice。3.3 在 Controller 中处理参数校验与响应封装RestController RequestMapping(/api/adoptions) public class AdoptionController { private final AdoptionService adoptionService; public AdoptionController(AdoptionService adoptionService) { this.adoptionService adoptionService; } PostMapping public ResponseEntityApiResponseAdoption adopt( RequestBody Valid AdoptionRequest request) { Adoption adoption adoptionService.adoptPet( request.getPetId(), request.getOwnerId(), request.getAdoptionDate() ); return ResponseEntity.ok(ApiResponse.success(adoption)); } } // 统一响应体 public record ApiResponseT(String code, String message, T data) { public static T ApiResponseT success(T data) { return new ApiResponse(0000, 操作成功, data); } }关键参数说明Valid触发AdoptionRequest类中定义的校验注解如NotNull、FutureOrPresentApiResponse封装标准返回格式避免前端收到{id: 1, petId: 5, ownerId: 3}这种裸对象便于统一处理错误码ResponseEntity显式控制 HTTP 状态码比ResponseBody更精确例如领养失败时可返回ResponseEntity.status(HttpStatus.CONFLICT).body(...)。4. 集成 MyBatis-Plus 替代 JPA解决复杂查询性能瓶颈4.1 为什么在已有 JPA 的基础上引入 MyBatis-PlusJPA 在简单 CRUD 场景下足够优雅但当需求变成“查询近 3 个月未接种狂犬疫苗的猫的数量”或“按区域统计各宠物医院绝育手术完成率”时JPQL 会迅速变得臃肿。例如// JPQL 写法难以复用、调试困难 Query(SELECT COUNT(p) FROM Pet p WHERE p.species CAT AND p.vaccinationStatus UNVACCINATED AND p.lastVaccinationDate :cutoffDate) Long countUnvaccinatedCats(Param(cutoffDate) LocalDate cutoffDate);而 MyBatis-Plus 的LambdaQueryWrapper可以链式构建条件且 SQL 可直接在mapper.xml中优化// MyBatis-Plus 写法类型安全、易调试 LambdaQueryWrapperPet wrapper new LambdaQueryWrapper(); wrapper.eq(Pet::getSpecies, Species.CAT) .eq(Pet::getVaccinationStatus, VaccinationStatus.UNVACCINATED) .lt(Pet::getLastVaccinationDate, LocalDate.now().minusMonths(3)); return petMapper.selectCount(wrapper);注意MyBatis-Plus 与 Spring Data JPA可以共存只需在application.yml中配置mybatis-plus.mapper-locationsclasspath:mapper/*.xml并在PetMapper接口上加Mapper注解即可。不要删除原有 JPA Repository保留它们处理简单场景。4.2 配置 MyBatis-Plus 并编写动态查询 XML在pom.xml中添加依赖dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-spring-boot3-starter/artifactId version4.3.1/version /dependency创建src/main/resources/mapper/PetMapper.xml?xml version1.0 encodingUTF-8? !DOCTYPE mapper PUBLIC -//mybatis.org//DTD Mapper 3.0//EN http://mybatis.org/dtd/mybatis-3-mapper.dtd mapper namespacecom.example.pet.mapper.PetMapper select idselectUnvaccinatedCatsCount resultTypejava.lang.Long SELECT COUNT(*) FROM t_pet p WHERE p.species CAT AND p.vaccination_status UNVACCINATED AND (p.last_vaccination_date IS NULL OR p.last_vaccination_date lt; DATE_SUB(CURDATE(), INTERVAL 3 MONTH)) /select /mapper关键点说明lt;是 XML 转义符对应避免解析错误直接写原生 SQL 可利用 MySQL 的DATE_SUB函数比 JPQL 的CURRENT_DATE - 3 MONTH更可靠resultTypejava.lang.Long明确返回类型防止 MyBatis 因泛型擦除返回Integer导致ClassCastException。4.3 在 Service 层混合使用 JPA 与 MyBatis-PlusService public class PetReportService { private final PetRepository jpaRepository; // 处理简单查询 private final PetMapper mybatisMapper; // 处理复杂统计 public long countUnvaccinatedCats() { // 优先用 MyBatis-Plus 执行聚合查询 return mybatisMapper.selectUnvaccinatedCatsCount(); } public ListPet findRecentAdoptions(int limit) { // 简单分页仍用 JPA代码更简洁 Pageable pageable PageRequest.of(0, limit, Sort.by(adoptionDate).descending()); return jpaRepository.findByAdoptionDateIsNotNull(pageable).getContent(); } }这种混合模式的优势在于不推翻现有架构只在性能瓶颈处精准替换。学生毕设答辩时你能清晰解释“为什么这里用 MyBatis-Plus 而不是 JPA”比单纯堆砌技术更有说服力。5. 视频录制与源码交付让毕设成果真正可验证5.1 录制视频必须覆盖的 5 个关键操作节点一份合格的“源码视频录制”交付物视频不是功能演示录像而是可复现的操作指南。必须包含以下 5 个节点每个节点时长控制在 6090 秒节点操作步骤验证要点1. 环境准备展示 JDK 17、Maven 3.8.8、IDEA 2023.3 的版本号打开终端执行java -version和mvn -v确保观众环境一致避免“我这能跑”争议2. 启动与初始化运行Application.main()等待控制台出现Started Application in X.XXX seconds访问http://localhost:8080/h2-console登录后查看t_pet表结构证明 H2 数据库已按ddl-auto: create-drop正确建表3. 主人创建用 Postman 发送POST /api/ownersBody 为{name:张三,idCard:11010119900307251X}检查响应返回id和createdAt字段验证 LombokData生效且NotBlank校验拦截空姓名4. 领养全流程先POST /api/pets创建宠物再POST /api/adoptions发起领养刷新 H2 控制台确认t_pet.adoption_date字段被更新证明Transactional生效且Pet与Adoption表数据一致性5. 复杂查询验证在PetReportServiceTest中运行单元测试断言countUnvaccinatedCats()返回值为0初始无数据手动插入测试数据后再次运行返回1展示 MyBatis-Plus XML 查询可被单元测试覆盖提示用 OBS Studio 录制时开启“窗口捕获”模式而非全屏聚焦 IDEA 和 Postman 窗口每段操作前口述“现在执行第 X 步”方便后期剪辑。5.2 源码打包规范让评审老师 3 分钟内跑起来交付的 ZIP 包必须包含以下结构缺失任一目录将导致“无法验证”扣分pet-system/ ├── pom.xml # 顶级 Maven 配置含所有依赖 ├── src/ │ ├── main/ │ │ ├── java/com/example/pet/ │ │ │ ├── PetApplication.java # 启动类含 SpringBootApplication │ │ │ ├── controller/ # 所有 RestController │ │ │ ├── service/ # Service 实现类含 Transactional │ │ │ ├── repository/ # JPA Repository 和 MyBatis Mapper 接口 │ │ │ └── entity/ # Entity 实体类含 Lombok 注解 │ │ └── resources/ │ │ ├── application.yml # 已配置 H2 和日志级别 │ │ └── mapper/ # MyBatis-Plus XML 文件 │ └── test/ │ └── java/com/example/pet/ # 至少包含 AdoptionServiceTest └── README.md # 3 行说明JDK 版本、启动命令、默认账号如有README.md示例内容# 宠物信息管理系统Spring Boot 3.2.6 ## 环境要求 - JDK 17必须 - Maven 3.6.3 ## 启动方式 1. cd pet-system 2. mvn clean package 3. java -jar target/pet-system-0.0.1-SNAPSHOT.jar ## 默认访问 - H2 控制台http://localhost:8080/h2-consoleJDBC URL: jdbc:h2:mem:petdb - API 文档启动后访问 http://localhost:8080/swagger-ui/index.html需额外添加 springdoc-openapi 依赖5.3 避开源码交付的 3 个高频雷区雷区 1提交.idea或target目录这些是 IDEA 本地缓存和编译产物不仅增大 ZIP 体积还可能导致不同 IDEA 版本解析失败。在.gitignore中加入.idea/ target/ *.iml雷区 2数据库密码硬编码在application.yml即使是 H2 内存库也要写成password: ${H2_PASSWORD:sa}并在README.md中注明“默认密码为空”。避免评审老师看到明文password: 123456产生安全疑虑。雷区 3缺少SpringBootTest测试类至少提供一个AdoptionServiceTest用AutoConfigureTestDatabase(replace AutoConfigureTestDatabase.Replace.NONE)确保测试走 H2 内存库而非连接真实 MySQL。这是证明“代码真能跑”的最后一道防线。视频结尾处对着屏幕敲下curl -X POST http://localhost:8080/api/adoptions -H Content-Type: application/json -d {petId:1,ownerId:1,adoptionDate:2024-06-15}然后展示返回的 JSON 和 H2 控制台中adoption_date的变更——这个动作本身就是对“源码视频”价值最直接的诠释。本文还有配套的精品资源点击获取