新闻详情

新闻详情

首页 / 资讯中心 / 详情

Java宠物管理系统课设实战:Spring Boot+MySQL避坑指南

发布时间:2026/9/28 16:02:03来源:尧图网络
Java宠物管理系统课设实战:Spring Boot+MySQL避坑指南
简介这是一份基于Java的宠物管理系统课程设计资源面向Java Web初学者与课程设计开发者完整覆盖SSMSpring、SpringMVC、MyBatis框架整合、Layui前端页面搭建及MySQL数据库设计等环节可用于宠物店或动物保护机构的信息化管理场景。压缩包共1442个文件约136.6MB包含java源码、jsp页面、class编译文件、jar依赖库、js/css等前端资源以及png/jpg/gif图片素材、sql数据库脚本和mp4演示视频结构完整便于对照学习。已有2171人浏览学习。通过该资源可掌握SSM项目从环境配置、表结构设计到前后端联调的全过程同时提供myeclipse工程配置文件与实验报告文档适合作为毕业设计或课程设计的参考模板。1. 基于Java的宠物管理系统这门课设/项目到底要做什么每个学期期末总有人拿着“基于Java的宠物管理系统”这个题目来找我有人要交课程设计有人想拿它当面试项目还有人真的开了宠物店想给自己做个后台。这个题目看着不难但坑一点都不少——技术路线选错、数据库连不上、中文乱码、寄养订单重复结算任何一项都能让你在验收前一晚对着控制台发呆。下面按我实际带人做这类系统的顺序来讲先选技术路线再建表再写一个 Spring Boot JdbcTemplate 能跑起来的完整雏形最后给出我踩过五次的坑和进阶验证清单。适合刚学完 Java 基础、想把手头的管理系统做成“能演示、能答辩、能讲清为什么”的读者如果你已经在背 Java 八股文不如先把这个系统跑通面试时反而更有话讲。2. 技术选型从 Swing 到 Spring Boot四条路线怎么选2.1 四条常见路线控制台、JavaFX、SwingJDBC、Spring Boot“基于Java的宠物管理系统”这种题目能落地的技术组合其实就四类。很多网上的课程设计案例源码还停留在 Swing 时代不是不能用而是你要想清楚做完之后它能不能继续生长。路线技术组成适合谁最大的坑控制台程序Java SE ArrayList/HashMap 文件读写只要交作业、不在意演示效果数据存文件重启就丢无说服力JavaFX JSON/CSVJava SE JavaFX 本地文件想做个本地单店演示界面现代但打包复杂多用户并发没法保证Swing JDBC MySQLJava SE Swing JDBC MySQL经典课设组合能讲清UI和SQL布局代码量大样式老旧难扩展成WebSpring Boot Thymeleaf MySQLJava Spring Boot JdbcTemplate Thymeleaf想真正落地、找工作和谈业务学习跨度稍大但值得投入我的判断标准很简单如果只有 48 小时并且明确“只要交作业”Swing JDBC 是保底方案因为它的演示路径最短老师问起来你也能把 JDBC 的 DriverManager、Connection、Statement 讲明白。但如果你想把“宠物管理系统”做成简历上的项目甚至以后让宠物店真的用起来直接走 Spring Boot 路线别在 JavaFX 上浪费时间——原因很简单Web 系统不用装客户端数据进 MySQL部署到云服务器上手机也能访问这些是本地桌面程序给不了的。2.2 为什么我推荐 Spring Boot JdbcTemplate而不是一上来就 MyBatis管理系统本质上是增删改查SQL 复杂度不高。用 JdbcTemplate 写你能清楚看到每一条 SQL、每一个参数、每一次类型转换换成 MyBatis 后多了一层 XML 映射和 Mapper 接口新手一旦 SQL 写错经常分不清是 XML 解析问题还是参数绑定问题等于多背一个黑匣子。Spring Data JPA 更不适合这个场景宠物列表往往要联查主人姓名、订单状态JPA 的懒加载很容易搞出 N1 条 SQL页面一开慢得明显。所以我的选型结论是Spring Boot Thymeleaf JdbcTemplate MySQL。具体创建项目时用你 IDE 里的 Spring Initializr 向导勾选 Web、Thymeleaf、JDBC、MySQL Driver 四个依赖即可不用手工建目录。运行环境建议按下面这个表对齐少踩很多版本坑环境项建议版本说明JDK8 / 11 / 17 均可pom 里的 java.version 必须和实际安装一致Maven3.6 以上用mvn -v看实际生效的 JDK别只看 JAVA_HOMEMySQL5.7 或 8.0推荐 8.0建库字符集用 utf8mb4Spring Boot2.7.x较新的 3.x 也能用但注意 javax 换成 jakarta2.3 这个项目里最容易走偏的两件事排序和内存存储很多同学拿到题目第一反应是“我要写个排序”甚至有人在 Service 里手写冒泡排序来给宠物按名字排序。这里有个原则管理系统的排序交给数据库不要在 Java 内存里自己排。一条ORDER BY created_at DESC, id DESC就能稳定解决你手写冒泡排序不仅性能更差还要处理中文排序规则纯属给自己挖坑。如果真遇到内存排序用Comparator.comparing也就够了别从排序算法开始造轮子。另一个更隐蔽的坑是存储方案。我见过有人用 ArrayList 文件序列化做了一个宠物系统演示当天没问题第二天开机数据全没了最后只能在答辩时硬着头皮说“这是测试版”。真实宠物店的数据是客户和活体宠物丢一条都是事故。所以从第一章开始就把 MySQL 放进去这是管理系统的底线不是可选项。3. 表结构先行宠物、主人、寄养订单的建库 SQL 与字段边界3.1 三张核心表的关系与建库 SQL宠物管理系统的核心实体就三个主人customer、宠物档案pet、寄养订单boarding_order。关系是一个主人可以养多只宠物一个宠物可以多次寄养。我一般会在订单表里冗余一个 customer_id不是为了省一次联查而是因为寄养订单本质是业务快照——宠物主人后来换了手机号历史订单里应该保留当时的客户指向。下面是建库建表 SQL直接复制到 MySQL 里执行即可CREATE DATABASE IF NOT EXISTS pet_manager CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE pet_manager; CREATE TABLE customer ( id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT 客户ID, name VARCHAR(50) NOT NULL COMMENT 主人姓名, phone VARCHAR(20) NOT NULL COMMENT 手机号, address VARCHAR(255) NOT NULL DEFAULT COMMENT 默认地址, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 建档时间 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT宠物主人表; CREATE TABLE pet ( id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT 宠物ID, customer_id BIGINT NOT NULL COMMENT 主人ID, name VARCHAR(50) NOT NULL COMMENT 宠物名, species VARCHAR(30) NOT NULL COMMENT 猫/狗/兔子, breed VARCHAR(50) NOT NULL DEFAULT COMMENT 品种, gender TINYINT NOT NULL DEFAULT 0 COMMENT 0未知 1公 2母, birthday DATE DEFAULT NULL COMMENT 出生日期, weight_kg DECIMAL(5,2) DEFAULT NULL COMMENT 体重kg, archived TINYINT NOT NULL DEFAULT 0 COMMENT 0正常 1归档, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 建档时间, KEY idx_customer_archived (customer_id, archived), KEY idx_species (species), CONSTRAINT fk_pet_customer FOREIGN KEY (customer_id) REFERENCES customer(id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT宠物档案表; CREATE TABLE boarding_order ( id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT 订单ID, pet_id BIGINT NOT NULL COMMENT 宠物ID, customer_id BIGINT NOT NULL COMMENT 主人ID, check_in_time DATETIME NOT NULL COMMENT 入住时间, check_out_time DATETIME DEFAULT NULL COMMENT 离店时间, fee_per_day DECIMAL(8,2) NOT NULL DEFAULT 0 COMMENT 每日寄养费, total_fee DECIMAL(10,2) DEFAULT NULL COMMENT 离店结算金额, status TINYINT NOT NULL DEFAULT 0 COMMENT 0寄养中 1已离店 2已取消, note VARCHAR(500) NOT NULL DEFAULT COMMENT 备注, version INT NOT NULL DEFAULT 0 COMMENT 乐观锁版本号, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 下单时间, KEY idx_status_checkin (status, check_in_time), KEY idx_pet_id (pet_id), CONSTRAINT fk_order_pet FOREIGN KEY (pet_id) REFERENCES pet(id), CONSTRAINT fk_order_customer FOREIGN KEY (customer_id) REFERENCES customer(id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT寄养订单表;这段 SQL 里最值得注意的不是字段多而是三个设计细节。第一外键约束保留下来虽然生产环境很多人会去掉外键但课程设计和中小型系统里外键能帮你挡住“删了正在寄养的宠物”这种低级错误。第二pet 表没有直接删数据而是加了 archived 字段做软删除这样历史寄养订单还能关联到宠物名不会出现“订单里宠物已不存在”的空洞。第三boarding_order 加了 version 字段这不是业务字段而是给后面的并发控制用的先建好后面写离店结算时直接受益。3.2 字段边界为什么手机号用 VARCHAR、金额用 DECIMAL、日期用 DATETIME这几张表的字段类型是有讲究的踩坑往往就踩在这里。手机号必须用 VARCHAR(20) 而不是 BIGINT。原因有两个手机号可能有前导 0也可能带 86 前缀如果用数值类型超过一定位数还会触发精度问题。虽然中国手机号目前都是 1 开头但你无法保证以后不做海外客户或分机号。金额字段用 DECIMAL 而不是 FLOAT 或 DOUBLE。这是 Java 面试八股里反复考的精度问题二进制浮点数无法精确表示 0.1寄养费按天累加会出现 19.99 变成 20.0000001 这种诡异情况。fee_per_day用DECIMAL(8,2)表示一天最多 99 万total_fee用DECIMAL(10,2)表示累计金额。体重用DECIMAL(5,2)够记录 999.99 公斤的宠物了。日期字段要区分场景生日用 DATE只存年月日寄养入住和离店用 DATETIME不带时区转换。很多人习惯用 TIMESTAMP这在高版本 MySQL 里会有 UTC 转换问题应用服务器和数据库时区不一致时查询结果经常差 8 小时。DATETIME 存的是字面时间配合 JDBC URL 里的serverTimezoneAsia/Shanghai能少踩一半时区坑。性别和状态我用 TINYINT 加注释不用 ENUM。ENUM 看着直观但以后要加一个“未知/绝育”状态得 ALTER TABLE 改枚举定义TINYINT 只需要在代码里约定数字含义扩展成本低得多。状态字段有三个值0 寄养中、1 已离店、2 已取消如果一开始用 BOOLEAN 来表示后面扩展就非常痛苦。3.3 索引怎么加外键自动索引与三个高频查询表建好后索引不是越多越好。这个系统的高频查询其实只有三类按主人查未归档宠物、按状态查寄养中的订单、按时间范围查订单用于月结对账。pet表我建了复合索引(customer_id, archived)因为最常见页面是“张三的宠物列表”查询条件是WHERE customer_id ? AND archived 0。单独建 customer_id 索引也能用但复合索引可以直接过滤 archived数据量大时性能差别明显。boarding_order表建了(status, check_in_time)复合索引因为前台要显示“当前寄养中”的列表后台要按月份统计收入这两个查询都能命中索引最左前缀。需要注意外键约束会自动为外键列创建索引所以pet.customer_id和boarding_order.pet_id不需要手动重复建单列索引。但复合索引不会自动生成这也是我特意加idx_customer_archived和idx_status_checkin的原因。索引设计的原则是“先写查询再建索引”而不是每个字段都来一个。4. 可抄作业的最小实现Spring Boot JdbcTemplate 跑通宠物管理4.1 项目结构与配置pom.xml 和 application.yml 怎么定这一章给出一个能完整跑起来的最小项目。我选用 Spring Boot 2.7.x因为很多读者本地还是 JDK 8切换成本最低。先看 Maven 依赖核心只需要四个 starterparent groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-parent/artifactId version2.7.18/version relativePath/ /parent properties java.version1.8/java.version /properties 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.springframework.boot/groupId artifactIdspring-boot-starter-jdbc/artifactId /dependency dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId scoperuntime/scope /dependency /dependenciesspring-boot-starter-parent已经把 MySQL 驱动版本管好了所以不用写版本号。如果你用 JDK 17 并且想上 Spring Boot 3.x记得把javax.servlet相关的包全部换成jakarta.servlet否则启动直接 NoClassDefFoundError。接着是src/main/resources/application.ymlspring: datasource: url: jdbc:mysql://localhost:3306/pet_manager?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/ShanghaiuseSSLfalseallowPublicKeyRetrievaltrue username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver thymeleaf: cache: false server: port: 8080URL 里的参数没有一个是多余的characterEncodingutf8保证中文不乱码serverTimezoneAsia/Shanghai解决 MySQL 8 的时区识别报错allowPublicKeyRetrievaltrue是配合 MySQL 8 默认的caching_sha2_password认证插件不加它连接时可能报“Public Key Retrieval is not allowed”。cache: false是让 Thymeleaf 模板改完刷新浏览器就能看到效果不用每次重启。4.2 实体类与 DAORowMapper、插入返回主键、软删除先写一个最朴素的 Pet 实体字段和表结构一一对应。这里不用 Lombok免得新手看不懂getter/setter 用 IDE 的 AltInsert 一键生成。import java.math.BigDecimal; import java.time.LocalDate; public class Pet { private Long id; private Long customerId; private String name; private String species; private String breed; private Integer gender; // 0未知 1公 2母 private LocalDate birthday; private BigDecimal weightKg; private Integer archived; // 0正常 1归档 // 生成 getter / setter }DAO 层直接用 JdbcTemplate核心是 RowMapper。这一步体现了“一行数据库记录变成一个 Java 对象”的完整过程Repository public class PetDao { private final JdbcTemplate jdbcTemplate; public PetDao(JdbcTemplate jdbcTemplate) { this.jdbcTemplate jdbcTemplate; } private final RowMapperPet rowMapper (rs, rowNum) - { Pet pet new Pet(); pet.setId(rs.getLong(id)); pet.setCustomerId(rs.getLong(customer_id)); pet.setName(rs.getString(name)); pet.setSpecies(rs.getString(species)); pet.setBreed(rs.getString(breed)); pet.setGender(rs.getInt(gender)); pet.setArchived(rs.getInt(archived)); java.sql.Date birthday rs.getDate(birthday); if (birthday ! null) { pet.setBirthday(birthday.toLocalDate()); } pet.setWeightKg(rs.getBigDecimal(weight_kg)); return pet; }; public ListPet listByCustomer(Long customerId) { return jdbcTemplate.query( SELECT * FROM pet WHERE customer_id ? AND archived 0 ORDER BY created_at DESC, id DESC, rowMapper, customerId); } public Pet findById(Long id) { return jdbcTemplate.queryForObject( SELECT * FROM pet WHERE id ? AND archived 0, rowMapper, id); } public Long insertAndReturnId(Pet pet) { KeyHolder keyHolder new GeneratedKeyHolder(); jdbcTemplate.update(connection - { PreparedStatement ps connection.prepareStatement( INSERT INTO pet (customer_id, name, species, breed, gender, birthday, weight_kg) VALUES (?, ?, ?, ?, ?, ?, ?), PreparedStatement.RETURN_GENERATED_KEYS); ps.setLong(1, pet.getCustomerId()); ps.setString(2, pet.getName()); ps.setString(3, pet.getSpecies()); ps.setString(4, pet.getBreed()); ps.setInt(5, pet.getGender()); ps.setDate(6, pet.getBirthday() null ? null : java.sql.Date.valueOf(pet.getBirthday())); ps.setBigDecimal(7, pet.getWeightKg()); return ps; }, keyHolder); return keyHolder.getKey().longValue(); } public int archive(Long id) { int rows jdbcTemplate.update( UPDATE pet SET archived 1 WHERE id ? AND archived 0, id); if (rows 0) { throw new IllegalArgumentException(宠物不存在或已归档); } return rows; } }代码里的参数顺序必须和 SQL 占位符一一对应这是新手最容易翻车的地方少传一个参数或传错位置报错信息往往还不好懂。insertAndReturnId用到了GeneratedKeyHolder因为JdbcTemplate.update返回的是影响行数不是自增主键如果不注册RETURN_GENERATED_KEYSkeyHolder.getKey()会抛异常这一点第 5 章还会展开讲。archive是软删除故意在 UPDATE 里加了archived 0条件这样重复点击删除时会抛异常提示而不是默默返回成功。寄养订单的 DAO 只需先实现一个“登记入住”的方法让主流程能跑通Repository public class BoardingOrderDao { private final JdbcTemplate jdbcTemplate; public BoardingOrderDao(JdbcTemplate jdbcTemplate) { this.jdbcTemplate jdbcTemplate; } public void startBoarding(Long petId, Long customerId, LocalDate checkInDate) { jdbcTemplate.update( INSERT INTO boarding_order (pet_id, customer_id, check_in_time, status, version) VALUES (?, ?, ?, 0, 0), petId, customerId, checkInDate); } }这个 INSERT 里故意把status和version写成固定值是因为新订单一定是“寄养中”且版本从 0 开始。check_in_time在 URL 参数里是字符串Spring MVC 会负责转成 LocalDate但要在 Controller 里加DateTimeFormat注解。4.3 Controller 与 Thymeleaf 页面先跑通新增宠物和寄养登记Controller 走最简单的 Spring MVC 写法一个类负责页面跳转和表单提交Controller RequestMapping(/pets) public class PetController { private final PetDao petDao; private final BoardingOrderDao boardingOrderDao; public PetController(PetDao petDao, BoardingOrderDao boardingOrderDao) { this.petDao petDao; this.boardingOrderDao boardingOrderDao; } GetMapping public String list(RequestParam(required false) Long customerId, Model model) { if (customerId null) { model.addAttribute(pets, petDao.listAll()); } else { model.addAttribute(pets, petDao.listByCustomer(customerId)); } return pet/list; } GetMapping(/new) public String createForm(Model model) { model.addAttribute(pet, new Pet()); return pet/form; } PostMapping public String create(ModelAttribute Pet pet) { Long petId petDao.insertAndReturnId(pet); return redirect:/pets/ petId; } PostMapping(/{id}/boarding) public String startBoarding(PathVariable Long id, RequestParam Long customerId, RequestParam DateTimeFormat(pattern yyyy-MM-dd) LocalDate checkInDate) { boardingOrderDao.startBoarding(id, customerId, checkInDate); return redirect:/pets/ id; } }ModelAttribute Pet pet会把表单里 name、species、breed 这些字段自动绑定到 Pet 对象上前提是表单控件的 name 属性和 Java 字段名一致。新增成功后用redirect:/pets/{id}做重定向避免用户刷新浏览器时重复提交表单这是 Post/Redirect/Get 的标准做法。Thymeleaf 模板放在src/main/resources/templates/pet/list.html核心片段是这样table thead tr th宠物名/th th种类/th th主人ID/th th体重(kg)/th th操作/th /tr /thead tbody tr th:eachpet : ${pets} td th:text${pet.name}宠物名/td td th:text${pet.species}种类/td td th:text${pet.customerId}主人ID/td td th:text${pet.weightKg}0.00/td td a th:href{/pets/{id}(id${pet.id})}查看/a /td /tr /tbody /table列表页不用写任何 Java 代码Thymeleaf 的th:each就是循环输出。需要留意的是${pet.weightKg}如果为 null页面会显示空白而不是 0.00所以要么在数据库层给默认值要么在实体 getter 里做空值兜底。4.4 启动与验证从 SQL 到浏览器的一分钟验证表结构建好后先插入一条测试主人和一只宠物INSERT INTO customer (name, phone) VALUES (张三, 13800001111); INSERT INTO pet (customer_id, name, species, breed, gender, birthday, weight_kg) VALUES (1, 煤球, 猫, 英短, 1, 2023-06-01, 4.50);然后在项目根目录执行mvn spring-boot:run启动日志出现Tomcat started on port 8080后浏览器访问http://localhost:8080/pets?customerId1应该能看到“煤球”在列表里。如果页面报 500先看控制台异常栈八成是数据库连接参数不对或表没建成功如果页面空白但后台没报错多半是模板路径写错Thymeleaf 找不到pet/list.html时会抛模板解析异常。这一套流程能通说明项目骨架、数据库、模板引擎三个环节已经串起来了。5. 避坑排查驱动、时区、乱码与自增主键的五个经典现场这一章写的都是我做这类系统时真实遇到过的现场。每条按“现象 → 原因 → 解决”展开你可以直接拿来当排查手册用。5.1 ClassNotFoundException: com.mysql.jdbc.Driver —— 驱动类名已经变了现象Spring Boot 启动时抛ClassNotFoundException: com.mysql.jdbc.Driver或者Cannot load driver class: com.mysql.jdbc.Driver。原因MySQL Connector/J 5.x 的驱动类名是com.mysql.jdbc.Driver从 Connector/J 8.0 开始改为com.mysql.cj.jdbc.Driver。如果你在网上复制了老项目的配置或者 pom 里引入的驱动版本停在 5.x就会遇到这个错误。Spring Boot 2.7 自带的驱动版本是 8.x所以driver-class-name必须写新类名。解决把 application.yml 里的驱动类名改成spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver同时用 Maven 确认实际依赖版本mvn dependency:tree -Dincludesmysql:mysql-connector-java如果 pom 里显式写了一个很老的版本号把这个依赖声明删掉交给 Spring Boot 的 parent 统一管理。5.2 时间少 8 小时、时区报错 —— serverTimezone 与 DATETIME 的配合现象插入一条寄养记录入住时间填的是 2024-06-01 10:00:00查出来变成 2024-06-01 02:00:00或者启动时报The server time zone value й׼ʱ is unrecognized后面跟一串乱码。原因MySQL 8 的 JDBC 驱动要求客户端显式指定时区否则它读服务器系统时区读出来是中文“中国标准时间”但字符集不对就成了乱码。TIMESTAMP 类型在存储时会做 UTC 转换应用服务器与数据库时区不一致时间就会偏移 8 小时。解决JDBC URL 加serverTimezoneAsia/Shanghai同时尽量把业务时间字段用 DATETIME 而不是 TIMESTAMP。DATETIME 不做时区转换存进去是什么就是什么对宠物寄养这种纯业务场景更友好。如果项目里已经有 TIMESTAMP 字段还可以在 MySQL 里执行SET time_zone 08:00临时验证但长期方案还是统一时区配置。5.3 新增宠物后详情页 404 —— 自增主键根本没取回来现象新增宠物表单提交后页面跳到/pets/null或者/pets/0详情页查不到数据。控制台没有异常数据库里也确实多了一条记录。原因最常见的写法是把JdbcTemplate.update()的返回值当成主键。这个方法返回的是受影响行数新增成功时返回 1而不是自增 ID。如果用普通PreparedStatement执行 INSERT也没有注册RETURN_GENERATED_KEYS数据库生成的主键根本不会传回 Java 端。解决用GeneratedKeyHolder接住自增主键参考第 4 章的insertAndReturnId。这里再强调一个细节prepareStatement的第二个参数必须是PreparedStatement.RETURN_GENERATED_KEYS否则keyHolder.getKey()会直接抛异常。KeyHolder keyHolder new GeneratedKeyHolder(); jdbcTemplate.update(connection - { PreparedStatement ps connection.prepareStatement(sql, PreparedStatement.RETURN_GENERATED_KEYS); // 设置参数 return ps; }, keyHolder); Long id keyHolder.getKey().longValue();5.4 页面中文乱码 —— 数据库、连接、模板三个源头现象新增一只叫“煤球”的猫列表页显示“???”或“ç…”但数据库命令行里查出来是正常的。有时候数据库里就是乱码有时候只有页面乱码。原因中文乱码在 Java Web 里是老三样数据库表字符集不是 utf8mb4JDBC 连接没有指定字符编码HTML 页面没有声明 UTF-8。三者只要有一个不对中文就保不住。解决第一步建库时用CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci并且建表语句也用DEFAULT CHARSETutf8mb4。第二步JDBC URL 加useUnicodetruecharacterEncodingutf8。第三步Thymeleaf 模板的head里写meta charsetUTF-8。排查时先用 MySQL 命令行SELECT name FROM pet;看数据本身是否正常如果命令行乱码说明数据入库时就已经错了问题在 JDBC 连接如果命令行正常页面乱码问题在模板或浏览器编码。5.5 mvn compile 报“源发行版 17 需要目标发行版 17” —— JAVA_HOME 与 pom 不一致现象执行mvn compile成功但 IDE 里编译报错红字提示“java: 警告: 源发行版 17 需要目标发行版 17”或者直接 javac 错误。用命令java -version看是 17pom 里写的却是 1.8。原因IDE 默认用当前 JDK 的版本作为编译器级别而 pom 里maven.compiler.source/target或java.version也是另一个值两边不一致时 Maven 就会拿 17 的 javac 去编 target 1.8 的代码。更隐蔽的是JAVA_HOME环境变量指向 JDK 17但 IDE 内置的 Maven 用的是自己的 JRE 配置。解决在 pom 的 properties 里显式声明版本Spring Boot parent 会自动把 source 和 target 都设成这个值properties java.version1.8/java.version /properties然后检查 IDE 的 Project Structure 里 SDK 是否和 pom 一致。不要相信echo $JAVA_HOME要以mvn -v输出里的 Java home 为准。如果你本地装的是 JDK 17直接把java.version改成 17 更省事但别同时用 JDK 17 编译 target 1.8 又开着 JDK 8 的依赖这种错位最容易把人绕晕。6. 进阶验证登录拦截、乐观锁结算与交付前的回归检查6.1 先加登录拦截器再加乐观锁离店结算基础功能跑通后最值得加的两个能力是登录校验和并发控制。先说登录Spring Boot 里加一个拦截器就能守住所有管理端点public class LoginInterceptor implements HandlerInterceptor { Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { if (request.getSession().getAttribute(loginUser) null) { response.sendRedirect(/login); return false; } return true; } }然后在WebMvcConfigurer里注册addPathPatterns(/pets/**, /boardings/**)拦截所有业务页面excludePathPatterns(/login, /css/**, /js/**)放行登录页和静态资源。这段代码能挡住“直接在浏览器输入 URL 访问详情页”的越权路径。寄养离店结算是另一个经典并发场景。两个前台同时点“结算”如果代码是“先查订单判断状态再 UPDATE”就会有两个线程都读到 status0然后都执行更新最终重复结算。Java 面试喜欢问的“怎么保证数据一致性”在这个系统里最务实的答案是乐观锁。利用表里已经建好的 version 字段UPDATE boarding_order SET status 1, total_fee ?, version version 1 WHERE id ? AND status 0 AND version ?;执行后影响行数为 1说明结算成功影响行数为 0说明订单已经被别人结算过或状态不对此时页面明确提示“请刷新后重试”而不是静默失败。这里的version ?必须传之前查询出来的旧值version version 1让每次成功更新都改变版本号下一次旧版本号再提交就匹配不上。6.2 交付前只跑这 12 个回归检查点我现在的习惯是功能写完先不急着拍照演示而是固定过一遍 12 个检查点1 未登录访问 /pets 要跳转登录页2 登录后直接访问别人的宠物详情要被拦截或校验归属3 中文新增和查询全程不乱码4 日期显示没有 8 小时偏移5 新增宠物后跳转的详情页能打开且数据正确6 金额计算不出现浮点误差保留两位小数7 同一订单重复点击离店结算只有第一次成功8 订单状态只能按 0→1 或 0→2 的方向流转不能从已取消变回寄养中9 归档宠物不再出现在列表但历史订单仍能显示宠物名10 新客户没有宠物时列表显示空态而不是报 50011 列表排序稳定同一秒创建的多条记录按 ID 倒序排列12 重启应用后数据仍在确认用的是 MySQL 而不是内存存储。这套检查点是我踩了多次数据丢失和重复结算后沉淀下来的比在答辩现场被老师问倒再补救要体面得多。你可以按自己的项目裁剪但 7、9、12 三条建议永远保留。希望帮到你。本文还有配套的精品资源点击获取
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

页面加了 Schema 标记,AI 回答里为什么还是不带我? 2026/9/28 20:31:47

页面加了 Schema 标记,AI 回答里为什么还是不带我?

你已经在页面里埋了 JSON-LD,结构化数据测试工具显示“0 个错误”,搜索结果里也偶尔出现 FAQ 折叠面板。但用户在 AI 对话里问同一个问题,答案引用的还是别人的页面。你打开 AI 的回答,发现它引用的那段话,来自一个连 …

阅读更多 →
基于SpringBoot+Vue的宠物店交易服务平台设计与实现 2026/9/28 20:31:47

基于SpringBoot+Vue的宠物店交易服务平台设计与实现

选题背景与意义 随着社会经济的持续发展和人们生活水平的不断提高,宠物逐渐从传统的看家护院角色转变为家庭成员,成为现代都市生活中不可或缺的情感陪伴者。根据中国宠物行业白皮书数据显示,2023年中国城镇宠物犬猫数量已突破1.8亿只&#xf…

阅读更多 →
【七】新手必会:别只会“加词”,学会用负向提示词给画面“排雷”-元界深掘 2026/9/28 20:31:47

【七】新手必会:别只会“加词”,学会用负向提示词给画面“排雷”-元界深掘

你有没有过这种挫败感: 正向提示词写了半页——“电影感、高级质感、氛围拉满、细节爆炸”——结果出图还是油、糊、假、廉价。 你开始怀疑自己是不是不会写提示词。 其实很多时候,问题不在“你写少了”,而在: 你只负责告诉 AI“要…

阅读更多 →
促销那天流量涨了 8 倍,我靠手动加节点熬到天亮 2026/9/28 20:31:47

促销那天流量涨了 8 倍,我靠手动加节点熬到天亮

促销活动那天晚上八点,我盯着监控面板上那条一直往上蹿的线,手心里全是汗。QPS(每秒请求数,Queries Per Second)从平时的三千冲到两万四,翻了正好八倍。扩容按钮我按了四回,第五回按下去的时候&…

阅读更多 →
AI单据识别接入管理系统,怎样定义字段级验收? 2026/9/28 20:31:47

AI单据识别接入管理系统,怎样定义字段级验收?

AI把单据内容转成表格后,企业应按业务字段核对结果,再决定哪些数据可以进入正式系统。全文识别得顺畅,不代表供应商名称、金额、日期等关键字段都正确;采购验收不能只看一项笼统的“识别率”。 本文提供评估方法,没有引…

阅读更多 →
场景题和计网逐字稿复习笔记 2026/9/28 20:31:40

场景题和计网逐字稿复习笔记

1.怎么设计一个分布式限流器使用 Redis,采用令牌桶算法,在 Redis 中存储桶容量,当前 token 数,令牌生成速率以及上次更新时间,每次有需要访问的时候就使用 lua 脚本判断当前 token 数目,如果满足就扣减。2.…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉