资讯动态

Java宠物商店系统:Spring Boot+MyBatis-Plus工程实践

发布时间:2026/9/14 9:53:23 来源:尧图企业网站定制
简介本资源是一套基于Java开发的宠物商店管理系统完整项目代码面向Java初学者与课程设计学生解决小型宠物门店日常经营中的客户管理、宠物信息维护、商品库存跟踪及销售记录等核心业务需求。压缩包为RAR格式大小8.93MB虽未提供具体文件明细但结合项目性质可推知包含Java源码.java、数据库脚本.sql、配置文件如application.properties及可能的前端页面.jsp或HTML支撑系统运行与部署。已有2419人学习下载反映出其在教学实践与毕业设计场景中的广泛参考价值。读者可直接导入IDE运行调试获得含用户登录、宠物增删改查、订单管理、数据持久化等完整功能模块的可执行工程配套结构清晰、注释较充分适合作为Java Web基础技术栈Servlet/JSP JDBC MySQL的综合实训范例。1. 用 Java 搭建宠物商店管理系统不是写个 CRUD 就能交差的工程实践很多刚学完 Java 基础或 Spring Boot 的同学在做课程设计、毕业设计或技术面试准备时常被要求实现一个“宠物商店管理系统”。但实际交付时才发现光有用户增删改查、宠物信息录入、订单提交这些表层功能根本撑不起一个可运行、可维护、可扩展的系统。真实场景中宠物商品需按品种猫/狗/仓鼠、年龄阶段幼宠/成年/老年、健康状态已驱虫/已绝育/疫苗接种中分类管理库存变动必须实时扣减且支持回滚销售单据要生成唯一编号、关联客户档案、支持退货冲正后台还需权限隔离——店员只能操作销售模块管理员才能配置供应商和定价策略。这不是 Java Web 入门练习而是对分层架构设计、事务边界控制、数据一致性保障和业务规则落地能力的综合检验。适合已完成 Servlet/JSP 或 Spring Boot 基础学习正尝试将零散知识点整合为完整业务系统的开发者。2. 从三层架构出发为什么选 Spring Boot MyBatis-Plus Thymeleaf 而非纯 JSP 或 Vue 前后端分离2.1 架构选型逻辑平衡开发效率、部署成本与教学可验证性在 Java 宠物商店管理系统中技术栈选择不是比谁新、谁酷而是看是否匹配项目目标。若面向高校课程设计或小型本地宠物店试用前后端不分离的单体架构仍是首选。理由很实在部署只需一个 JAR 包 内置 Tomcat无需 Nginx 反向代理、跨域配置、前端构建环境教师或验收方能直接java -jar pet-shop.jar启动打开浏览器输入http://localhost:8080即见完整界面无须额外启动前端服务Thymeleaf 模板天然支持服务端渲染表单提交、错误提示、分页跳转全部由后端控制调试时System.out.println()和断点可直击业务逻辑层对初学者友好度远高于 Axios 请求失败后还要查 Network Tab。对比 Vue3 后台管理系统如热词中提到的 “vue3后台管理系统”它虽现代但引入了npm run serve、axios、pinia、路由守卫等新概念而本项目核心矛盾是业务规则表达是否清晰而非前端交互炫技。MyBatis-Plus 则在保留 SQL 可控性的前提下通过TableName、TableField注解和LambdaQueryWrapper实现 80% 常用 CRUD 自动化避免手写 XML 易错且难维护的问题。提示若后续需对接微信小程序或 APP可在本系统基础上新增 RESTful 接口模块而非一开始就强行前后端分离——这是很多初学者把简单项目做复杂的根源。2.2 核心依赖配置pom.xml 中必须锁定的 5 个关键坐标以下为pom.xml中真正影响系统稳定性和功能边界的依赖声明Spring Boot 2.7.x 兼容 JDK 8适配多数教学环境dependencies !-- Spring Boot Web 核心 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency !-- MyBatis-Plus 3.5.3.1注意3.5.x 是最后一个全面兼容 JDK 8 的大版本 -- dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-boot-starter/artifactId version3.5.3.1/version /dependency !-- H2 内存数据库开发测试用零配置启动 -- dependency groupIdcom.h2database/groupId artifactIdh2/artifactId scoperuntime/scope /dependency !-- Thymeleaf 模板引擎 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-thymeleaf/artifactId /dependency !-- Lombok 简化实体类避免手写 getter/setter/toString -- dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId optionaltrue/optional /dependency /dependencies参数说明mybatis-plus-boot-starter版本必须与 Spring Boot 主版本对齐Spring Boot 2.7.x 对应 MyBatis-Plus 3.5.x若误用 3.6.x要求 JDK 17编译会报Unsupported class file major version 61错误h2作为 runtime 依赖仅用于开发阶段快速验证其jdbc:h2:mem:petshop连接字符串在application.yml中配置无需安装外部数据库lombok的optionaltrue/optional表示它不参与最终打包避免运行时冲突但 IDE 需安装 Lombok 插件才能识别Data注解。2.3 数据库建模宠物、客户、订单三张主表的设计依据与字段约束真实宠物商店业务中宠物Pet不是静态商品而是带生命周期的实体。因此pet表不能只存名称、价格必须包含age_stage ENUM(KITTEN,ADULT,SENIOR)区分幼宠/成年/老年影响饲料推荐和体检周期health_status VARCHAR(20)存储“已驱虫已绝育疫苗接种中待接种”支持多状态组合用逗号分隔或 JSON 字符串supplier_id BIGINT关联供应商表便于统计采购成本与毛利stock_quantity INT NOT NULL DEFAULT 0 CHECK (stock_quantity 0)库存字段加 CHECK 约束防止负数入库。对应customer表需记录member_level TINYINT DEFAULT 11普通2银卡3金卡用于后续折扣计算order表则必须有order_status ENUM(CREATED,PAID,SHIPPED,COMPLETED,CANCELLED)和pay_time DATETIME NULL确保退款逻辑可追溯。建表示例H2 兼容语法CREATE TABLE pet ( id BIGINT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(50) NOT NULL, species VARCHAR(20) NOT NULL, -- CAT,DOG,HAMSTER age_stage VARCHAR(20) NOT NULL, health_status VARCHAR(100), price DECIMAL(10,2) NOT NULL, stock_quantity INT NOT NULL DEFAULT 0 CHECK (stock_quantity 0), supplier_id BIGINT, created_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE customer ( id BIGINT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(50) NOT NULL, phone VARCHAR(20) UNIQUE, member_level TINYINT DEFAULT 1, register_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE order ( id BIGINT AUTO_INCREMENT PRIMARY KEY, order_no VARCHAR(32) NOT NULL UNIQUE, -- 格式PS202405200001 customer_id BIGINT NOT NULL, total_amount DECIMAL(10,2) NOT NULL, order_status VARCHAR(20) DEFAULT CREATED, pay_time DATETIME NULL, created_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (customer_id) REFERENCES customer(id) );注意H2 数据库中order是关键字建表时必须用反引号包裹表名否则启动报错Syntax error in SQL statement CREATE TABLE ORDER。3. 关键业务落地宠物库存扣减、订单生成与状态机驱动的事务控制3.1 库存扣减必须原子化用 Transactional SELECT FOR UPDATE 防超卖宠物库存是典型热点数据。当多个店员同时下单同一只布偶猫时若仅用UPDATE pet SET stock_quantity stock_quantity - 1 WHERE id ? AND stock_quantity 0可能因并发导致stock_quantity被扣成负数。正确做法是在 Service 层开启事务并显式加行锁Service public class OrderService { Transactional(rollbackFor Exception.class) public Order createOrder(Long customerId, ListOrderItem items) { // 1. 检查并锁定库存SELECT FOR UPDATE for (OrderItem item : items) { Pet pet petMapper.selectById(item.getPetId()); if (pet null || pet.getStockQuantity() item.getQuantity()) { throw new RuntimeException(宠物库存不足 item.getPetId()); } // 加锁此行执行后其他事务无法修改该 pet 记录直到本事务结束 petMapper.selectByIdForUpdate(item.getPetId()); } // 2. 扣减库存此时已获锁安全 for (OrderItem item : items) { int updated petMapper.decreaseStock(item.getPetId(), item.getQuantity()); if (updated ! 1) { throw new RuntimeException(库存扣减失败可能已被其他订单占用); } } // 3. 创建订单主记录 Order order buildOrder(customerId, items); orderMapper.insert(order); // 4. 创建订单明细 for (OrderItem item : items) { item.setOrderId(order.getId()); orderItemMapper.insert(item); } return order; } }selectByIdForUpdate方法需在 Mapper 接口中定义Mapper public interface PetMapper extends BaseMapperPet { Select(SELECT * FROM pet WHERE id #{id} FOR UPDATE) Pet selectByIdForUpdate(Param(id) Long id); }逻辑说明Transactional保证整个方法内所有 DB 操作要么全成功要么全回滚SELECT ... FOR UPDATE在 MySQL/InnoDB 中触发行级写锁阻塞其他事务对该行的UPDATE或SELECT FOR UPDATE但允许SELECT读取MVCC 机制decreaseStock是自定义 SQL 方法使用UPDATE pet SET stock_quantity stock_quantity - #{quantity} WHERE id #{id}避免先查后更带来的竞态条件。3.2 订单号生成时间戳 序列号防重拒绝 UUID 或 Snowflake宠物商店订单号需满足人类可读、按时间有序、支持日级别去重、不暴露服务器信息。UUID 太长32位且无序Snowflake 依赖机器 ID 易出错。采用PSPet Shop前缀 8位日期YYYYMMDD 4位序列号当日第几单最稳妥Component public class OrderNoGenerator { private final AtomicInteger dailyCounter new AtomicInteger(0); private volatile String currentDay ; public String generateOrderNo() { String today LocalDate.now().toString().replace(-, ); // 20240520 if (!today.equals(currentDay)) { synchronized (this) { if (!today.equals(currentDay)) { currentDay today; dailyCounter.set(0); // 每日重置计数器 } } } int seq dailyCounter.incrementAndGet(); return String.format(PS%s%04d, today, seq); } }参数说明volatile保证currentDay的可见性避免线程间缓存不一致synchronized块双重检查防止高并发下dailyCounter被重复初始化%04d格式化确保序列号恒为 4 位0001, 0002…便于人工核对与数据库索引优化。3.3 订单状态机用枚举 状态校验拦截非法流转订单状态不能靠前端传参随意修改必须由后端强制校验流转路径。定义OrderStatus枚举并内置合法转移规则public enum OrderStatus { CREATED(待支付, Set.of(PAID)), PAID(已支付, Set.of(SHIPPED, CANCELLED)), SHIPPED(已发货, Set.of(COMPLETED)), COMPLETED(已完成, Set.of()), CANCELLED(已取消, Set.of()); private final String desc; private final SetOrderStatus allowedNext; OrderStatus(String desc, SetOrderStatus allowedNext) { this.desc desc; this.allowedNext allowedNext; } public boolean canTransitionTo(OrderStatus next) { return this.allowedNext.contains(next); } public static OrderStatus fromString(String status) { return Arrays.stream(values()) .filter(s - s.name().equalsIgnoreCase(status)) .findFirst() .orElseThrow(() - new IllegalArgumentException(无效订单状态 status)); } }状态更新方法强制校验public void updateStatus(Long orderId, String newStatusStr) { Order order orderMapper.selectById(orderId); OrderStatus current OrderStatus.fromString(order.getOrderStatus()); OrderStatus target OrderStatus.fromString(newStatusStr); if (!current.canTransitionTo(target)) { throw new IllegalStateException( String.format(订单 %d 状态非法变更%s → %s, orderId, current, target) ); } order.setOrderStatus(target.name()); orderMapper.updateById(order); }提示此设计比if (status.equals(PAID)) { allowShipped(); } else if...更易维护新增状态只需修改枚举无需遍历所有 if 分支。4. 权限与安全加固基于角色的菜单控制与敏感操作二次确认4.1 角色菜单动态加载用 Spring Security Thymeleaf 实现店员/管理员视图隔离宠物商店中店员只需看到「销售开单」「客户查询」「库存查看」而管理员还需「供应商管理」「员工账号设置」「销售报表导出」。不能靠前端隐藏按钮易被绕过必须服务端拦截Configuration EnableWebSecurity public class SecurityConfig { Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http .authorizeHttpRequests(authz - authz .requestMatchers(/admin/**).hasRole(ADMIN) // /admin 开头需 ADMIN 角色 .requestMatchers(/sales/**).hasAnyRole(ADMIN, STAFF) // 销售相关开放给店员 .requestMatchers(/login, /css/**, /js/**).permitAll() .anyRequest().authenticated() ) .formLogin(form - form .loginPage(/login) .defaultSuccessUrl(/dashboard, true) .failureUrl(/login?errortrue) ) .logout(logout - logout .logoutSuccessUrl(/login?logouttrue) ); return http.build(); } }Thymeleaf 模板中根据角色显示不同菜单!-- sidebar.html -- div classsidebar-menu ul li th:if${#authorization.expression(hasRole(ADMIN))} a href/admin/suppliers供应商管理/a /li li th:if${#authorization.expression(hasRole(ADMIN) or hasRole(STAFF))} a href/sales/order-create新建销售单/a /li li a href/customer/list客户档案/a /li /ul /div逻辑说明hasRole(ADMIN)由 Spring Security 自动注入无需手动查数据库角色存储在user_role关联表中登录时UserDetailsService加载GrantedAuthority/admin/**路径统一拦截比在每个 Controller 方法上加PreAuthorize(hasRole(ADMIN))更简洁。4.2 敏感操作二次确认删除宠物前强制输入验证码删除宠物是不可逆操作需防误点。在 Controller 层增加验证码校验环节PostMapping(/pet/{id}/delete) public String deletePet(PathVariable Long id, RequestParam String confirmCode, Model model) { // 1. 生成并缓存本次删除的验证码有效期 2 分钟 String expectedCode (String) session.getAttribute(deletePetCode_ id); if (expectedCode null || !expectedCode.equalsIgnoreCase(confirmCode)) { model.addAttribute(error, 验证码错误请重新输入); model.addAttribute(petId, id); return pet/confirm-delete; // 返回确认页 } // 2. 执行删除含外键检查若有未完成订单引用此宠物禁止删除 try { petService.deleteWithCheck(id); } catch (RuntimeException e) { model.addAttribute(error, e.getMessage()); return pet/confirm-delete; } return redirect:/pet/list; }对应 HTML 表单confirm-delete.htmlform th:action{/pet/{id}/delete(id${petId})} methodpost p确定删除宠物「span th:text${pet.name}/span」此操作不可撤销。/p input typetext nameconfirmCode placeholder请输入验证码 required / button typesubmit确认删除/button a href/pet/list取消/a /form注意验证码生成逻辑应放在删除请求前的 GET 接口里例如/pet/{id}/confirm-delete并在页面加载时调用Math.random().toString(36).substring(2, 6).toUpperCase()生成简易码教学场景够用生产环境建议接入图形验证码。5. 部署与验证用 H2 数据库快速启动 内置监控端点排查常见问题5.1 一键启动application.yml 中 5 行配置搞定 H2 控制台与数据初始化开发阶段无需安装 MySQLH2 内存数据库配合其 Web 控制台即可完成全流程验证spring: datasource: url: jdbc:h2:mem:petshop;DB_CLOSE_DELAY-1;DB_CLOSE_ON_EXITFALSE driver-class-name: org.h2.Driver username: sa password: h2: console: enabled: true path: /h2-console sql: init: mode: always >INSERT INTO customer (name, phone, member_level) VALUES (张三, 13800138000, 2), (李四, 13900139000, 1); INSERT INTO pet (name, species, age_stage, health_status, price, stock_quantity, supplier_id) VALUES (布偶猫-雪莉, CAT, ADULT, 已驱虫,已绝育, 3800.00, 5, 1), (金毛幼犬, DOG, KITTEN, 待接种, 2200.00, 3, 2);启动后访问http://localhost:8080/h2-console填入 JDBC URLjdbc:h2:mem:petshop即可直连数据库执行 SQL 查看订单生成结果、验证库存扣减是否生效。5.2 常见启动失败排查表精准定位 90% 的新手报错报错信息根本原因解决方案Failed to configure a DataSource: url not configuredapplication.yml中spring.datasource.url缩进错误或拼写错误检查 YAML 缩进是否为 2 空格url前不能有 tabInvalid bound statement (not found): com.xxx.mapper.PetMapper.selectByIdForUpdateSelect注解方法未在 Mapper XML 中声明或接口未加Mapper确认PetMapper类上有Mapper且方法名与 XML 中id一致Whitelabel Error Page访问/login显示白页Thymeleaf 模板未放在src/main/resources/templates/login.html检查文件路径是否为templates非template扩展名是否为.htmljava.lang.NoClassDefFoundError: javax/xml/bind/JAXBExceptionJDK 11 移除了 JAXB而 H2 1.4.200 依赖它将 H2 降级至1.4.199或添加javax.xml.bind:jaxb-api依赖There is already xxxServiceImpl bean method同一包下存在两个Service类类名相同如PetService和PetServiceImpl删除重复类确保Service注解只在一个实现类上5.3 生产就绪检查清单3 项必须调整的配置项当系统需部署到真实服务器时以下配置必须修改否则存在安全与性能风险配置项开发值生产值说明spring.h2.console.enabledtruefalseH2 控制台暴露在公网等于数据库裸奔必须关闭spring.datasource.urljdbc:h2:mem:petshopjdbc:mysql://prod-db:3306/petshop?useSSLfalseserverTimezoneAsia/Shanghai切换为 MySQL 并启用连接池如 HikariCPlogging.level.com.xxx.mapperDEBUGWARNDEBUG 级别会打印所有 SQL生产环境日志量爆炸且可能泄露敏感参数验证方式启动后访问http://localhost:8080/actuator/health返回{status:UP}表示基础健康检查通过再访问http://localhost:8080/actuator/metrics/jvm.memory.used确认内存指标可采集即代表 Actuator 监控已生效。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价