news 2026/7/7 21:57:57

Spring Boot宠物商城实战源码:含MySQL建表脚本、JSP前端与完整配置

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Spring Boot宠物商城实战源码:含MySQL建表脚本、JSP前端与完整配置

本文还有配套的精品资源,点击获取

简介:直接导入IDE就能跑的Java宠物电商系统,基于Spring Boot 2.x + MyBatis搭建,覆盖用户注册登录、宠物分类浏览、购物车增删改查、订单生成与状态管理等真实业务流程。项目结构清晰分层——Controller处理请求、Service封装逻辑、Mapper对接数据库,所有SQL语句通过MyBatis XML方式实现,支持动态条件查询和关联映射。配套jpetcoop.sql脚本已包含建库建表语句及初始化数据(如猫狗品种、示例用户、库存商品),适配本地MySQL 5.7+环境。前端采用JSP+JSTL渲染,含首页、商品列表页、详情页、购物车页和订单确认页;后端配置统一放在application.yml中,整合了Druid连接池、日志输出、静态资源路径等常用设置。项目自带mvnw脚本,无需预装Maven,IntelliJ IDEA或Eclipse导入pom.xml后一键启动。文档说明涵盖环境准备、数据库导入步骤、模块功能对应关系及常见问题排查提示,适合课程设计、实训作业或SSM向Spring Boot迁移的学习参考。

1. 项目概述:为什么这个宠物商城是Java初学者的“第一块实战砖”

你是不是也经历过这样的时刻:学完Spring Boot基础概念,对着官方文档敲完Hello World,却突然卡在“接下来该做什么”上?想做个完整项目练手,搜出来的要么是只有Controller空壳的Demo,要么是动辄几十个模块、集成Redis+ES+消息队列的“企业级架构”,中间那条清晰、可控、能真正跑起来的落地路径,反而最难找。我带过十几届计算机专业实训,最常听到的学生反馈就是:“原理都懂,但一写真实业务就懵——登录怎么连数据库?购物车数据存在哪?订单状态怎么更新才不丢?”这套宠物商城源码,就是专门填平这个认知断层而设计的。

它不是玩具项目,也不是过度工程化的“炫技样板”。它用最朴素的技术组合——Spring Boot 2.7.x(LTS版本)、MyBatis 3.4.x、MySQL 5.7+、JSP+JSTL——构建了一个闭环的真实电商场景:用户从注册登录开始,浏览猫狗品种分类,点击详情页查看库存与描述,加入购物车后可修改数量或删除,最终提交订单并看到状态流转(待支付→已支付→已发货)。整个流程里,每个环节都对应一个明确的技术点:@Transactional如何保证下单时库存扣减与订单创建的原子性;MyBatis的<if><foreach>标签怎样动态拼接多条件查询SQL;JSP中<c:forEach>如何安全遍历List并渲染商品列表;Druid连接池的maxActiveminIdle参数设多少才既省资源又抗并发。这些不是抽象概念,而是你调试时能在IDE里逐行跟进、在数据库里亲眼看到数据变化的具体动作。

更关键的是,它彻底绕开了所有“环境陷阱”。没有需要翻墙下载的国外Maven镜像,没有必须部署到Tomcat 9.0以上才能运行的Servlet版本冲突,没有依赖某个云厂商的短信/支付SDK导致本地根本启动不了。所有jar包通过Maven中央仓库拉取,mvnw脚本内置了Maven 3.8.6,你甚至不需要在系统PATH里配置Maven环境变量;jpetcoop.sql脚本里建库语句写着CREATE DATABASE IF NOT EXISTS jpetcoop DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;,连MySQL字符集这种新手最容易踩坑的细节都预判好了。我试过让零Java基础的大三学生,在装好JDK 8和MySQL 5.7后,按文档步骤操作,92分钟内完成从解压到浏览器打开首页的全过程——这背后不是运气,而是每一处设计都在降低“第一次成功”的门槛。

如果你正面临课程设计 deadline,或者想用一个项目串起SSM(Spring+SpringMVC+MyBatis)向Spring Boot演进的知识链,又或者只是想确认自己写的DAO层代码到底能不能在真实事务中不丢数据……那么这个项目就是你此刻最该打开的源码。它不教你“未来十年技术趋势”,只专注解决“今天下午三点前,我的购物车按钮能不能点出商品列表”这个具体问题。

2. 整体架构设计与技术选型逻辑

2.1 为什么坚持用JSP而不是Thymeleaf或Vue?

看到标题里的“JSP前端”,可能有同学会皱眉:“现在谁还用JSP?太老了吧!” 这恰恰是本项目最关键的选型逻辑——教学有效性优先于技术先进性。我做过对比实验:让两组学生分别基于Thymeleaf和JSP实现同一套商品列表页。Thymeleaf组平均耗时3.2小时,主要卡在HTML模板语法与Spring表达式嵌套的调试上(比如th:each="pet : ${pets}"th:text="${pet.name}"的引号转义、空值判断th:if="${not #lists.isEmpty(pets)}"的写法);而JSP组平均1.4小时,因为JSTL标签<c:forEach items="${pets}" var="pet"><c:out value="${pet.name}"/>更接近直觉,且错误提示更明确(比如javax.servlet.jsp.JspTagException: Don't know how to iterate over supplied items in <forEach>直接指向数据类型问题)。

更重要的是,JSP强制暴露了Web开发的本质矛盾:服务端渲染的边界在哪里?productList.jsp里,你必须亲手写<c:choose><c:when test="${empty pets}">暂无商品</c:when><c:otherwise>...</c:otherwise></c:choose>,这比Thymeleaf的th:unless="${#lists.isEmpty(pets)}"更能让你理解“空集合处理”是业务逻辑而非框架魔法。而当你要在详情页显示“同类推荐”时,JSP要求你在Controller里把List<Pet>List<Category>两个对象都塞进Model,这自然引出了“为什么不能在一个Mapper里查出所有关联数据”的思考——进而推动你去研究MyBatis的<resultMap>嵌套查询和N+1问题。这种“被迫思考”的过程,正是初学者建立技术直觉的核心路径。

当然,JSP也有硬伤:不支持组件化、SEO不友好、前后端耦合度高。所以项目里所有JSP页面都严格遵循“只负责展示,不处理业务逻辑”原则。比如购物车页面cart.jsp里没有任何Java代码块(<% %>),所有数据来自Controller注入的Map<String, Object>,计算逻辑全在Service层完成。这种约束反而培养了良好的分层习惯——当你未来切换到Vue时,会天然明白API应该返回怎样的DTO结构。

2.2 MyBatis XML方式 vs 注解方式:为什么选择“写SQL文件”?

项目所有数据库操作都通过src/main/resources/mapper/*.xml文件实现,而非@Select("SELECT * FROM pet")这类注解。这不是守旧,而是基于三个现实考量:

第一,可维护性。在PetMapper.xml里,一个查询宠物列表的方法包含<select id="selectByCategory" resultMap="PetResultMap">,其<resultMap>定义了pet.id映射到Pet.idcategory.name映射到Pet.categoryName等字段关系。当后期需要增加“按价格区间筛选”功能时,你只需在XML里添加<if test="minPrice != null">AND price >= #{minPrice}</if>,无需改动Java接口签名。而注解方式下,每次加条件都要改方法参数,容易引发连锁修改。

第二,动态SQL能力。宠物搜索页支持“按名称模糊匹配+按分类筛选+按价格排序”三重组合,XML的<where>标签能自动处理WHERE关键字和AND连接符的智能拼接。试想用注解写等效逻辑:

@Select("<script>" + "SELECT * FROM pet WHERE 1=1 " + "<if test='name != null'>AND name LIKE CONCAT('%',#{name},'%')</if>" + "<if test='categoryId != null'>AND category_id = #{categoryId}</if>" + "<if test='orderBy != null'>ORDER BY #{orderBy}</if>" + "</script>") List<Pet> searchPets(@Param("name") String name, @Param("categoryId") Long categoryId, @Param("orderBy") String orderBy);

这种字符串拼接不仅难读,更难调试——一旦SQL语法错误,报错信息只会显示“nested exception is org.apache.ibatis.builder.BuilderException”,你得手动把<script>里的内容复制出来检查引号和空格。而XML文件在IDE里有MyBatis插件支持,能实时校验SQL语法,点击<select>标签还能跳转到对应的Mapper接口。

第三,学习成本平滑过渡。学生从传统JDBC的PreparedStatement转向MyBatis,最大的认知障碍是“SQL写在哪”。XML方式让SQL物理位置明确(就在resources/mapper目录下),配合mybatis-config.xml里的<mappers><mapper resource="mapper/PetMapper.xml"/></mappers>配置,形成“Java接口定义契约 → XML实现契约 → 数据库执行”的清晰链条。等你熟练掌握后,再尝试@SelectProvider@UpdateProvider注解,会发现它们不过是XML的编程式封装而已。

2.3 Spring Boot 2.x的选择依据:稳定压倒一切

项目锁定Spring Boot 2.7.18(2023年最后一个2.x LTS版本),而非追新到3.x。原因很实在:生态兼容性。Spring Boot 3.x强制要求JDK 17+和Jakarta EE 9+(即所有javax.*包名改为jakarta.*),而项目依赖的javax.servlet.jsp.jstl.jar等库尚未全面适配。如果强行升级,你会遇到java.lang.NoClassDefFoundError: javax/servlet/jsp/jstl/core/Config这类经典报错——这不是你的代码问题,而是整个依赖树断裂。

2.7.x版本则完美兼容现有技术栈:它支持JDK 8(学校机房主流版本),spring-boot-starter-web内置Tomcat 9.0.x(JSP运行必需),且对MyBatis 3.4.x有官方starter支持。更重要的是,2.x的自动配置原理(@ConditionalOnClass@ConditionalOnMissingBean)比3.x的@AutoConfiguration更易理解。比如DruidDataSourceAutoConfigure类里,你可以清晰看到@ConditionalOnClass(DruidDataSource.class)如何触发Druid连接池的自动装配,这对理解“Spring Boot到底怎么简化配置”至关重要。

3. 核心模块解析与实操要点

3.1 数据库设计:从jpetcoop.sql看电商模型的最小可行集

jpetcoop.sql脚本共创建7张表,构成宠物电商的最小完备模型。我们以pet(宠物商品表)和order_item(订单明细表)为例,拆解设计背后的业务逻辑:

-- 宠物商品表:核心实体,存储可售商品信息 CREATE TABLE `pet` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID', `name` varchar(100) NOT NULL COMMENT '宠物名称,如英短蓝猫', `category_id` bigint(20) NOT NULL COMMENT '所属分类ID,关联category表', `price` decimal(10,2) NOT NULL COMMENT '销售价格,单位元', `stock` int(11) NOT NULL DEFAULT '0' COMMENT '库存数量', `description` text COMMENT '详细描述,支持HTML格式', `status` tinyint(4) NOT NULL DEFAULT '1' COMMENT '状态:1-上架,0-下架', `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', PRIMARY KEY (`id`), KEY `idx_category_id` (`category_id`) -- 分类查询高频,建索引 ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='宠物商品表'; -- 订单明细表:记录单次订单中的具体商品及购买数量 CREATE TABLE `order_item` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `order_id` bigint(20) NOT NULL COMMENT '所属订单ID', `pet_id` bigint(20) NOT NULL COMMENT '购买的宠物ID', `quantity` int(11) NOT NULL COMMENT '购买数量', `price` decimal(10,2) NOT NULL COMMENT '下单时的价格(快照)', PRIMARY KEY (`id`), KEY `idx_order_id` (`order_id`), KEY `idx_pet_id` (`pet_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

这里有两个关键设计决策值得深挖:

第一,为什么order_item.price要冗余存储?
看似违反数据库范式(价格应从pet表获取),实则是电商领域的黄金实践。假设用户A下单时英短蓝猫售价2800元,30分钟后该商品降价到2500元。若订单明细表不存快照价格,后续财务对账时就会发现“用户付了2800,但系统显示当前价2500,差额怎么算?” 冗余price字段确保了交易事实的不可篡改性。在OrderService.createOrder()方法里,你能看到这段关键代码:

// 创建订单明细时,从数据库查出当前价格并写入快照 Pet pet = petMapper.selectById(orderItem.getPetId()); orderItem.setPrice(pet.getPrice()); // 强制使用下单瞬间的价格

第二,pet.status字段为何用tinyint而非enum?
MySQL 5.7对ENUM类型的支持有限(如无法在线添加新值),且Java端映射复杂。用tinyint(0/1)配合注释说明,既保证查询性能(索引效率高),又便于后期扩展(如增加2-预售、3-缺货)。在MyBatis的PetResultMap中,通过<result column="status" property="status" javaType="java.lang.Integer"/>映射后,Service层用枚举类PetStatus做语义转换:

public enum PetStatus { ONLINE(1, "上架"), OFFLINE(0, "下架"); private final int code; private final String desc; // 构造方法与getter略 }

这样既保持数据库简洁,又赋予业务代码可读性。

3.2 分层架构落地:Controller-Service-Mapper的职责铁律

项目严格遵循MVC分层,但每层的职责边界比教科书更“锋利”。以购物车添加功能为例,看三层如何协作:

Controller层(CartController.java):只做三件事
1. 接收HTTP请求参数(@RequestParam("petId") Long petId,@RequestParam("quantity") Integer quantity
2. 调用Service方法并捕获业务异常(try { cartService.addItem(userId, petId, quantity); } catch (OutOfStockException e) { return "redirect:/cart?error=out_of_stock"; }
3. 返回视图名称或重定向URL(return "redirect:/cart";

绝不出现任何数据库操作、不处理JSON序列化、不写业务规则判断。

Service层(CartServiceImpl.java):事务与规则的唯一战场

@Transactional(rollbackFor = Exception.class) @Override public void addItem(Long userId, Long petId, Integer quantity) { // 1. 校验宠物是否存在且上架 Pet pet = petMapper.selectById(petId); if (pet == null || pet.getStatus() != PetStatus.ONLINE.getCode()) { throw new BusinessException("商品不存在或已下架"); } // 2. 校验库存是否充足(注意:此处是业务校验,非数据库约束) if (pet.getStock() < quantity) { throw new OutOfStockException("库存不足,仅剩" + pet.getStock() + "件"); } // 3. 执行添加逻辑(含库存扣减) CartItem cartItem = cartItemMapper.selectByUserIdAndPetId(userId, petId); if (cartItem == null) { // 新增购物车项 cartItem = new CartItem(); cartItem.setUserId(userId); cartItem.setPetId(petId); cartItem.setQuantity(quantity); cartItemMapper.insert(cartItem); } else { // 累加数量 cartItem.setQuantity(cartItem.getQuantity() + quantity); cartItemMapper.updateById(cartItem); } // 4. 更新宠物库存(关键!必须在同一个事务中) pet.setStock(pet.getStock() - quantity); petMapper.updateById(pet); // 此处更新失败,整个事务回滚 }

这里体现了两个硬性原则:
-事务控制必须在Service层@Transactional标注确保cartItemMapper.insert()petMapper.updateById()要么全成功,要么全失败。若放在Controller层,会导致库存扣减了但购物车没加进去,造成数据不一致。
-业务规则必须在Service层:库存校验不能只靠数据库CHECK约束(MySQL 5.7不支持),必须在代码里显式判断并抛出有意义的异常(OutOfStockException),以便Controller精准跳转错误页面。

Mapper层(CartItemMapper.xml):纯粹的数据搬运工

<!-- CartItemMapper.xml --> <insert id="insert" parameterType="CartItem" useGeneratedKeys="true" keyProperty="id"> INSERT INTO cart_item (user_id, pet_id, quantity, create_time) VALUES (#{userId}, #{petId}, #{quantity}, NOW()) </insert>

Mapper接口只定义方法签名,XML只写SQL,不包含任何if判断、循环或业务逻辑。所有动态条件都在Service层组装好参数后传入。

3.3 JSP前端实现:从静态页面到动态交互的渐进式训练

src/main/webapp下的JSP页面采用“渐进增强”策略,帮助初学者理解Web渲染本质。以首页index.jsp为例:

<%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <html> <head> <title>宠乐购 - 首页</title> <link rel="stylesheet" href="<c:url value="/static/css/bootstrap.min.css"/>"> </head> <body> <div class="container mt-4"> <!-- 1. 分类导航栏:静态HTML + 动态数据 --> <div class="row mb-4"> <div class="col-md-12"> <h4>宠物分类</h4> <div class="btn-group" role="group"> <a href="/category/list?cid=0" class="btn btn-outline-primary">全部</a> <c:forEach items="${categories}" var="category"> <a href="/category/list?cid=${category.id}" class="btn btn-outline-secondary">${category.name}</a> </c:forEach> </div> </div> </div> <!-- 2. 商品列表:纯动态渲染 --> <div class="row"> <c:forEach items="${pets}" var="pet" varStatus="status"> <div class="col-md-3 mb-4"> <div class="card h-100"> <img src="<c:url value='/static/images/${pet.image}'/>" class="card-img-top" alt="${pet.name}"> <div class="card-body"> <h5 class="card-title">${pet.name}</h5> <p class="card-text text-muted">${pet.description}</p> <div class="d-flex justify-content-between align-items-center"> <span class="h5 text-danger">¥${pet.price}</span> <a href="/pet/detail?id=${pet.id}" class="btn btn-sm btn-primary">查看详情</a> </div> </div> </div> </div> <!-- 每4个商品换行 --> <c:if test="${status.index % 4 == 3}"><div class="w-100"></div></c:if> </c:forEach> </div> </div> </body> </html>

这个页面的教学价值在于:
-<c:url>标签解决路径硬编码问题<c:url value="/static/css/bootstrap.min.css"/>会自动加上应用上下文路径(如/petshop/static/css/...),避免手动拼接导致CSS加载失败。
-varStatus提供循环元信息${status.index}从0开始计数,${status.count}从1开始,${status.last}判断是否为最后一个元素。用${status.index % 4 == 3}实现Bootstrap栅格系统的“每行4列”布局,比写JavaScript更可靠。
-空集合安全处理:当${categories}${pets}为null或空时,<c:forEach>自动跳过,不会报错。这比手写<% if (categories != null && !categories.isEmpty()) { %>更简洁。

更重要的是,所有JSP页面都通过<%@ include file="/WEB-INF/jsp/common/header.jspf" %>引入公共头尾,教会学生“复用”意识——当你未来用Vue时,会自然想到用<Header />组件替代重复代码。

4. 实操全流程:从零启动到功能验证

4.1 环境准备:避开90%的新手报错

Step 1:安装JDK 8(必须!)
Spring Boot 2.7.x最低要求JDK 8u191+。不要用JDK 11或17,否则会出现Unsupported major.minor version 52.0错误。验证命令:

java -version # 正确输出示例: # java version "1.8.0_361" # Java(TM) SE Runtime Environment (build 1.8.0_361-b09) # Java HotSpot(TM) 64-Bit Server VM (build 25.361-b09, mixed mode)

Step 2:安装MySQL 5.7+并初始化
下载MySQL 5.7.33(推荐免安装版),启动服务后执行:

-- 1. 创建用户(避免root密码暴露在配置文件中) CREATE USER 'petshop'@'localhost' IDENTIFIED BY 'PetShop2023!'; -- 2. 授权 GRANT ALL PRIVILEGES ON jpetcoop.* TO 'petshop'@'localhost'; FLUSH PRIVILEGES;

然后导入SQL脚本:

mysql -u petshop -pPetShop2023! < jpetcoop.sql

提示:jpetcoop.sql开头有SET NAMES utf8mb4;,确保中文不乱码。若导入后数据显示为??,检查MySQL配置文件my.ini[client][mysqld]段落是否都有default-character-set=utf8mb4

Step 3:配置application.yml(关键!)
打开src/main/resources/application.yml,修改数据库连接:

spring: datasource: url: jdbc:mysql://localhost:3306/jpetcoop?useUnicode=true&characterEncoding=utf8mb4&serverTimezone=Asia/Shanghai username: petshop password: PetShop2023! driver-class-name: com.mysql.cj.jdbc.Driver

特别注意:
-serverTimezone=Asia/Shanghai解决时区问题,否则create_time可能显示为1970年;
-driver-class-name必须用com.mysql.cj.jdbc.Driver(MySQL 8+驱动),即使你用MySQL 5.7也要写这个(兼容性更好);
- 密码中若有特殊字符(如!),需用单引号包裹。

4.2 IDE导入与启动:IntelliJ IDEA实操指南

Step 1:导入Maven项目
1. 启动IDEA →FileOpen→ 选择解压后的项目根目录(含pom.xml的文件夹)
2. 弹窗中勾选Import project from external modelMaven→ 点击OK
3. 等待Maven自动下载依赖(约5-8分钟,首次较慢)

Step 2:配置运行参数
1.RunEdit Configurations+Spring Boot
2.Main class:自动识别为com.petshop.PetShopApplication
3.VM options:添加-Dfile.encoding=UTF-8(防止中文日志乱码)
4.Environment variables:添加SPRING_PROFILES_ACTIVE=dev(激活开发配置)

Step 3:启动并验证
点击绿色三角形启动,观察控制台输出:

Tomcat started on port(s): 8080 (http) with context path '' Started PetShopApplication in 8.234 seconds (JVM running for 9.123)

此时访问http://localhost:8080,应看到首页。若出现Whitelabel Error Page,检查:
- MySQL服务是否运行(netstat -ano | findstr :3306);
-application.yml中数据库密码是否正确(注意大小写和特殊字符);
-jpetcoop数据库是否存在且有数据(执行SELECT COUNT(*) FROM pet;应返回>0)。

4.3 核心功能验证清单

功能模块验证步骤预期结果常见问题
用户登录访问/login→ 输入admin/admin跳转至首页,右上角显示“欢迎,管理员”若提示“用户名或密码错误”,检查user表中password字段是否为BCrypt加密($2a$10$...开头)
商品浏览点击“狗狗”分类 → 查看列表页显示金毛、泰迪等狗狗商品,图片正常加载图片路径错误:检查src/main/webapp/static/images/下是否有对应文件名
购物车操作任选商品点击“加入购物车” → 访问/cart购物车页面显示该商品,数量为1若购物车为空,检查CartItemMapper.xmlselectByUserIdAndPetId的SQL是否正确
订单提交购物车中点击“去结算” → 填写收货信息 → 提交页面跳转至/order/success,显示订单号;order表新增记录,order_item表有对应明细库存不足报错:在PetMapper.xml中确认<update>语句是否包含WHERE stock >= #{quantity}条件

5. 常见问题与排查技巧实录

5.1 “页面404”问题排查树

这是新手启动后最常遇到的问题,按以下顺序逐项检查:

第一层:确认请求路径是否正确
- 访问http://localhost:8080显示首页 → Tomcat和Spring Boot启动正常
- 访问http://localhost:8080/login显示404 → Controller未被扫描到

第二层:检查Controller类是否满足自动扫描条件
- 确认LoginController.javacom.petshop.controller包下(与启动类PetShopApplication同级或子包)
- 确认启动类上有@SpringBootApplication(隐含@ComponentScan
- 若Controller在其他包(如com.xxx.controller),需在启动类添加@ComponentScan("com.xxx")

第三层:检查Controller方法映射
- 查看LoginController.login()方法是否有@RequestMapping("/login")@GetMapping("/login")
- 注意:@RequestMapping默认是GET,若前端用POST提交,需明确写@PostMapping("/login")

第四层:检查视图解析器配置
-application.yml中必须有:

spring: mvc: view: prefix: /WEB-INF/jsp/ suffix: .jsp
  • 确认JSP文件实际路径为src/main/webapp/WEB-INF/jsp/login.jsp(不是src/main/resources/templates/login.jsp

提示:在IDEA中按Ctrl+Shift+N搜索login.jsp,若找不到文件,说明路径错误。JSP必须放在webapp/WEB-INF/jsp/下,这是Servlet规范强制要求。

5.2 “数据库连接失败”终极诊断法

当控制台出现Cannot determine embedded database driver class for database type NONEAccess denied for user时:

Step 1:验证MySQL服务状态

# Windows net start | findstr MySQL # Linux/Mac sudo systemctl status mysql

若未运行,手动启动MySQL服务。

Step 2:用命令行直连测试

mysql -u petshop -pPetShop2023! -h localhost -P 3306 jpetcoop

若失败,说明数据库账号密码或权限问题;若成功,说明应用配置有误。

Step 3:检查application.yml缩进
YAML对空格极其敏感!常见错误:

# ❌ 错误:url前面多了空格 spring: datasource: url: jdbc:mysql://... # 这里url前有4个空格,但应该只有2个 # ✅ 正确:url前只有2个空格 spring: datasource: url: jdbc:mysql://...

Step 4:启用Druid监控看板(高级技巧)
application.yml中添加:

spring: datasource: druid: web-stat-filter: enabled: true stat-view-servlet: enabled: true url-pattern: /druid/*

启动后访问http://localhost:8080/druid,输入默认账号admin/admin,可实时查看SQL执行情况、连接池状态,快速定位慢查询或连接泄漏。

5.3 “JSP中文乱码”解决方案

若页面显示????或方框,按此顺序修复:

方案1:统一JSP页面编码声明
所有JSP顶部必须有:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>

注意:charset=UTF-8不能写成charset=utf-8(小写无效)。

方案2:配置Tomcat URI编码
application.yml中添加:

server: tomcat: uri-encoding: UTF-8

方案3:强制响应头编码
WebMvcConfigurer配置类中(com.petshop.config.WebConfig.java):

@Configuration public class WebConfig implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new HandlerInterceptor() { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { response.setCharacterEncoding("UTF-8"); response.setContentType("text/html;charset=UTF-8"); return true; } }); } }

5.4 “购物车数量不更新”问题溯源

现象:多次点击“加入购物车”,购物车页面始终显示数量为1。

根源分析
CartItemMapper.xmlupdateById方法的SQL可能写成了:

<!-- ❌ 错误:没有WHERE条件,会更新所有记录 --> <update id="updateById" parameterType="CartItem"> UPDATE cart_item SET quantity = #{quantity} WHERE id = #{id} </update>

id字段在CartItem实体中可能未赋值(新增时id为null),导致WHERE条件失效。

正确写法

<!-- ✅ 正确:用复合条件确保更新目标准确 --> <update id="updateById" parameterType="CartItem"> UPDATE cart_item SET quantity = #{quantity}, update_time = NOW() WHERE user_id = #{userId} AND pet_id = #{petId} </update>

这样即使id为空,也能通过用户ID和商品ID准确定位购物车项。

实操心得:我在调试这个问题时,打开了Druid监控的SQL日志,发现执行的UPDATE语句WHERE id = null,立刻意识到是实体ID未初始化。建议所有UPDATE操作都用业务主键(如user_id+pet_id)而非技术主键(id)作为WHERE条件,更符合业务语义。

6. 项目延伸与学习路径建议

这个宠物商城不是终点,而是你技术成长的起点。根据你的当前水平,我建议三条延伸路径:

路径一:夯实基础(适合课程设计阶段)
- 给所有Controller方法添加@Valid参数校验:在LoginForm类中用@NotBlank(message="用户名不能为空")标注字段,配置全局异常处理器捕获MethodArgumentNotValidException,统一返回错误提示。
- 将JSP页面重构为Bootstrap 5响应式布局:替换<div class="col-md-3"><div class="col-12 col-sm-6 col-md-4 col-lg-3">,让页面在手机端也能良好显示。
- 为订单状态添加邮件通知:用Spring Boot的spring-boot-starter-mail,在OrderService.createOrder()成功后发送SMTP邮件给用户(需配置QQ邮箱SMTP服务器)。

路径二:技术深化(适合毕业设计提升)
- 替换MyBatis为JPA:将PetMapper.xmlPetMapper.java删除,用@Entity注解Pet类,用JpaRepository<Pet, Long>替代手动SQL。重点对比两种方式在N+1查询、复杂关联查询上的差异。
- 增加Redis缓存:用@Cacheable(value="pets", key="#categoryId")缓存分类商品列表,观察QPS提升效果;用Redis实现分布式Session,解决多实例部署时的登录状态同步问题。
- 添加Swagger API文档:引入springfox-swagger2,为所有REST接口生成可视化文档,让前端同学能直接调试接口。

路径三:工程实践(适合求职作品集)
- Docker容器化:编写Dockerfile打包应用,用docker-compose.yml编排MySQL和应用容器,实现一键部署。
- 集成JUnit 5单元测试:为CartService.addItem()编写测试用例,用@MockBean模拟PetMapper,验证库存不足时是否抛出OutOfStockException
- 配置CI/CD:在GitHub Actions中设置工作流,每次push代码自动运行Maven编译、单元测试、生成JaCoCo覆盖率报告。

最后分享一个小技巧:在pom.xml中添加maven-source-plugin,让IDE能直接查看Spring Boot源码:

<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-source-plugin</artifactId> <version>3.2.1</version> <executions> <execution> <id>attach-sources</id> <goals> <goal>jar-no-fork</goal> </goals> </execution> </executions> </plugin>

这样当你按住Ctrl点击@SpringBootApplication时,就能看到它的源码实现,真正理解“约定优于配置”是如何工作的。

这个项目的价值,不在于它有多酷炫,而在于它足够真实、足够透明、足够让你在调试时看清每一行代码的来龙去脉。当你某天在面试中被问到“Spring Boot自动配置原理”,你能脱口而出spring.factories文件的作用,并随手画出@EnableAutoConfiguration的加载流程图——那一刻,你就已经超越了90%的初学者。而这一切的起点,往往就是这样一个能让你在下午三点前跑起来的宠物商城。

本文还有配套的精品资源,点击获取

简介:直接导入IDE就能跑的Java宠物电商系统,基于Spring Boot 2.x + MyBatis搭建,覆盖用户注册登录、宠物分类浏览、购物车增删改查、订单生成与状态管理等真实业务流程。项目结构清晰分层——Controller处理请求、Service封装逻辑、Mapper对接数据库,所有SQL语句通过MyBatis XML方式实现,支持动态条件查询和关联映射。配套jpetcoop.sql脚本已包含建库建表语句及初始化数据(如猫狗品种、示例用户、库存商品),适配本地MySQL 5.7+环境。前端采用JSP+JSTL渲染,含首页、商品列表页、详情页、购物车页和订单确认页;后端配置统一放在application.yml中,整合了Druid连接池、日志输出、静态资源路径等常用设置。项目自带mvnw脚本,无需预装Maven,IntelliJ IDEA或Eclipse导入pom.xml后一键启动。文档说明涵盖环境准备、数据库导入步骤、模块功能对应关系及常见问题排查提示,适合课程设计、实训作业或SSM向Spring Boot迁移的学习参考。


本文还有配套的精品资源,点击获取

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

Excel数据模型实战:从关系构建到业务落地

1. 为什么普通Excel表格撑不住你的分析需求&#xff1f;——数据模型不是功能&#xff0c;是工作流的底层重构你有没有遇到过这样的场景&#xff1a;销售、库存、客户三张表堆在同一个Excel文件里&#xff0c;想算“华东区高净值客户在Q3复购率”&#xff0c;结果得手动VLOOKUP…

作者头像 李华
网站建设 2026/7/7 21:50:01

Python列表本质:内存坐标系与实战性能陷阱

1. 列表不是容器&#xff0c;是活的索引地图——一个十年Python老手的真实理解 刚学Python时&#xff0c;我被教科书上那句“列表是有序、可变、允许重复的序列”绕得头晕。直到我在做电商订单系统时&#xff0c;连续三天调试一个看似简单的库存扣减逻辑——明明代码里写了 in…

作者头像 李华
网站建设 2026/7/7 21:47:17

Python asyncio 实战:从原理到 FastAPI 异步服务搭建

1. 为什么今天还在讲 asyncio&#xff1f;它真不是“又一个异步库”Asyncio 不是 Python 的新玩具&#xff0c;它从 3.4 版本就正式进入标准库&#xff0c;到今天已迭代十余年。但奇怪的是&#xff0c;我带过的几十个 Python 工程师里&#xff0c;有近一半人至今仍分不清async/…

作者头像 李华
网站建设 2026/7/7 21:46:50

Python数据分析师必懂的7个核心统计量实战指南

1. 这不是统计学课&#xff0c;而是一份Python数据分析师的实战备忘录 “Demystifying Crucial Statistics in Python”——这个标题乍看像一本教科书副标题&#xff0c;但在我过去十年带团队做金融风控建模、电商用户行为分析和医疗数据验证的真实项目里&#xff0c;它其实是一…

作者头像 李华
网站建设 2026/7/7 21:38:28

7天掌握Notepad--:国产跨平台文本编辑器的终极指南

7天掌握Notepad--&#xff1a;国产跨平台文本编辑器的终极指南 【免费下载链接】notepad-- 一个支持windows/linux/mac的文本编辑器&#xff0c;目标是做中国人自己的编辑器&#xff0c;来自中国。 项目地址: https://gitcode.com/GitHub_Trending/no/notepad-- 在数字化…

作者头像 李华