谷粒商城全栈项目实战:Spring Boot+MyBatis+MySQL电商系统搭建与二次开发指南

发布时间:2026/10/16 9:39:37
谷粒商城全栈项目实战:Spring Boot+MyBatis+MySQL电商系统搭建与二次开发指南
简介这份资源是谷粒商城项目的完整开发压缩包面向正在学习Java Web与电商系统开发的中高级开发者尤其适合需要对照真实项目理解前后端协作与数据库设计的读者。包内整合了后端业务代码、前端HTML页面以及SQL脚本SQL部分涵盖商品、订单、用户等核心表结构设计、初始化数据插入、增删改查与查询脚本前端则包含页面结构、样式布局与交互逻辑后端代码涉及接口设计、业务处理、数据库操作及安全与性能优化思路。压缩包为zip格式整体约127.7MB文件类型以后端源码、SQL脚本和HTML页面为主便于按模块检索学习。目前已有416人学习下载读者可借助这套代码梳理电商平台从数据库建模到接口联调、从页面渲染到业务落地的完整链路适合作为课程设计、毕业项目或自主练手的参考素材。1. 从一份电商全栈压缩包说起为什么我建议你亲手跑一遍如果你正在找一个能同时练手 Spring Boot 后端、MyBatis 持久层、MySQL 建表和前端页面联调的完整电商项目那这份「谷粒商城」的代码加 SQL 加 HTML 压缩包大概率能省掉你到处拼凑 Demo 的时间。它不是那种只给几个接口的玩具工程而是把商品、订单、购物车、用户这些电商核心域都铺开了连建表语句和静态页面都一起打包给你。我第一次拿到类似结构的项目时最直观的感受是终于不用自己瞎编表结构了SQL 文件一导入后端实体类照着字段写就行。这份资源适合两类人——刚学完 SSM 或 Spring Boot 想找个真实场景练手的以及需要快速搭一个电商后台原型做二次开发验证的。下面我按「先看清结构、再跑起来、最后动手改」的顺序把这份压缩包拆开讲。2. 项目结构与技术栈压缩包里到底装了什么2.1 从压缩包目录看工程分层拿到一个 zip 包我习惯先解压看目录而不是急着找启动类。这份谷粒商城资源通常包含三块后端 Java 工程、数据库 SQL 脚本、前端 HTML 静态资源。后端部分一般是标准的 Maven 多模块或单模块结构src/main/java下按controller、service、mapper、entity分包resources里放application.yml和 MyBatis 的mapper.xml。SQL 脚本通常是一个或多个.sql文件包含建库、建表、插入初始数据。HTML 部分则是商品列表、详情、购物车这些页面的静态原型可能配合少量 JS 做接口调用。常见做法是把后端和前端分开部署后端跑在 8080前端用 Nginx 或直接浏览器打开。你解压后先别改任何东西用tree或文件管理器把三层结构看清楚后面排查问题会快很多。# 解压后查看目录结构Linux/macOS unzip 谷粒商城.zip -d gulimall cd gulimall find . -maxdepth 3 -type d | sort这段命令只是帮你快速列出三层目录-maxdepth 3避免输出太深。重点看有没有sql文件夹、src文件夹和html或static文件夹。如果 SQL 文件和 Java 工程混在一起建议手动分开导入数据库时不容易选错文件。2.2 技术栈组合与选型逻辑从项目命名和常见实现推断后端大概率是 Spring Boot MyBatis MySQL 的组合前端是原生 HTML jQuery 或 Vue 的轻量用法。为什么这类电商练手项目偏爱这套因为 Spring Boot 省去了 SSM 时代繁琐的 XML 配置MyBatis 对 SQL 的控制力又比 JPA 更直观——电商查询经常要拼多条件、分页、联表手写 SQL 反而清晰。MySQL 则是中小型电商最稳妥的选择建表脚本直接给出来省去设计表结构的时间。层次技术在项目中的角色后端框架Spring Boot提供 Web 容器、依赖注入、自动配置持久层MyBatis映射实体与 SQL处理商品/订单查询数据库MySQL存储用户、商品、订单、购物车数据前端HTML JS页面展示与接口调用构建Maven管理 jar 依赖与打包这张表不是让你背而是帮你判断如果你本地已经装了 MySQL 和 JDK跑起来的门槛就很低。缺哪个补哪个不用被“全栈”两个字吓到。2.3 SQL 脚本在项目里的位置关键词里单独点了sql说明这份资源的 SQL 脚本是核心卖点之一。电商项目的表结构设计直接决定后端代码怎么写。通常会有user、product、category、order、order_item、cart这几张核心表。你拿到 SQL 后不要直接全选执行先打开看有没有CREATE DATABASE和USE语句确认库名和字符集。-- 典型建表片段以商品表为例 CREATE TABLE product ( id bigint(20) NOT NULL AUTO_INCREMENT COMMENT 商品ID, name varchar(128) NOT NULL COMMENT 商品名称, price decimal(10,2) NOT NULL DEFAULT 0.00 COMMENT 单价, stock int(11) NOT NULL DEFAULT 0 COMMENT 库存, category_id bigint(20) DEFAULT NULL COMMENT 分类ID, status tinyint(4) DEFAULT 1 COMMENT 上下架状态, PRIMARY KEY (id), KEY idx_category (category_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT商品表;这段 SQL 里几个点值得注意decimal(10,2)存价格避免浮点误差utf8mb4支持 emoji 和特殊字符idx_category为分类查询加索引。你导入后可以用SHOW TABLES;和DESC product;验证。如果导入报错多半是字符集或外键顺序问题先关掉外键检查再导入。3. 把项目跑起来从导入 SQL 到页面能点3.1 数据库导入与连接配置跑这个项目的第一步不是打开 IDE而是把数据库准备好。我一般会新建一个干净的库避免和本地已有数据冲突。用命令行或 Navicat 都行关键是执行顺序先建库再导表最后插数据。如果 SQL 文件里已经包含这些直接 source 进去。# 登录 MySQL 并导入 SQL mysql -u root -p CREATE DATABASE gulimall DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE gulimall; SOURCE /path/to/gulimall.sql;SOURCE后面换成你解压出来的实际路径。执行完用SELECT COUNT(*) FROM product;看看有没有数据。如果表是空的检查 SQL 文件里有没有INSERT语句有些资源会把建表和插数据分成两个文件。数据库好了之后改后端配置文件。找到application.yml或application.properties把spring.datasource.url、username、password改成你本地的。常见坑是时区没配导致连接报错URL 后面加上serverTimezoneAsia/Shanghai就行。3.2 后端启动与接口验证配置改完用 Maven 拉依赖并启动。如果你用 IDEA直接找Application启动类右键运行命令行的话# 在项目根目录执行 mvn clean package -DskipTests java -jar target/*.jar-DskipTests是为了跳过测试加快打包第一次跑建议加上。启动日志里看到Started Application in x seconds就说明后端起来了。这时候别急着开页面先用 curl 或浏览器访问一个商品列表接口确认数据能查出来。# 测试商品列表接口端口以实际配置为准 curl http://localhost:8080/product/list?page1size10如果返回 JSON 且里面有商品数据说明后端和数据库已经通了。如果报 500看控制台异常多半是 mapper.xml 里的 SQL 字段和实体类对不上或者数据库连接没配好。3.3 前端页面联调与跨域处理HTML 页面通常放在resources/static或独立文件夹里。如果是独立文件夹你可以直接用浏览器打开但接口请求会遇到跨域。常见做法是在后端加一个全局 CORS 配置或者用 Nginx 做反向代理。练手阶段最省事的是后端加配置类Configuration public class CorsConfig implements WebMvcConfigurer { Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping(/**) .allowedOriginPatterns(*) .allowedMethods(GET, POST, PUT, DELETE) .allowCredentials(true) .maxAge(3600); } }allowedOriginPatterns(*)在开发阶段方便上线要改成具体域名。allowCredentials(true)允许携带 cookie配合前端withCredentials使用。加完重启后端再打开 HTML 页面商品列表应该能正常渲染。如果页面空白按 F12 看 Network 里接口是不是 404 或跨域被拦。4. 核心模块拆解商品、订单、购物车怎么串起来4.1 商品模块的查询与分页实现商品模块是电商项目里最基础也最常被二次开发的部分。典型链路是Controller 接收分页参数Service 调用 MapperMapper 执行带LIMIT的 SQL返回列表和总数。这里的关键是分页不要用内存分页一定要在 SQL 里做。// Controller 层 GetMapping(/list) public Result list(RequestParam(defaultValue 1) Integer page, RequestParam(defaultValue 10) Integer size) { PageHelper.startPage(page, size); ListProduct list productService.listAll(); PageInfoProduct pageInfo new PageInfo(list); return Result.success(pageInfo); }这段代码用了 PageHelper 插件startPage之后的第一条查询会自动加LIMIT。PageInfo封装了总记录数、总页数、当前页数据。如果你不想引入 PageHelper也可以手动传offset和size到 SQL 里。参数说明page从 1 开始size控制每页条数前端传参时注意别传 0 或负数。4.2 订单创建与库存扣减的时序订单模块的难点不在增删改查而在创建订单时怎么保证库存不超卖。常见做法是在一个事务里先查库存、再扣减、再写订单。但高并发下这样会有竞态条件更稳妥的是用UPDATE product SET stock stock - ? WHERE id ? AND stock ?靠数据库行锁保证原子性。Transactional public Result createOrder(Long userId, Long productId, Integer count) { // 原子扣减库存影响行数为 0 说明库存不足 int affected productMapper.reduceStock(productId, count); if (affected 0) { throw new BizException(库存不足); } Order order new Order(); order.setUserId(userId); order.setProductId(productId); order.setCount(count); order.setStatus(0); // 0 待支付 orderMapper.insert(order); return Result.success(order.getId()); }Transactional保证扣库存和写订单在同一个事务里任何一步失败都回滚。reduceStock对应的 SQL 里AND stock ?是防超卖的关键。参数count是购买数量userId和productId从登录态和前端传入。注意事务方法不要吞异常否则回滚不生效。4.3 购物车的数据结构选择购物车有两种常见实现存数据库和存 Redis。这份资源如果偏练手大概率是存 MySQL 的cart表字段包括user_id、product_id、count、checked。查询时联表商品表拿到名称和价格。-- 查询某用户购物车列表 SELECT c.id, c.count, c.checked, p.name, p.price, p.stock FROM cart c LEFT JOIN product p ON c.product_id p.id WHERE c.user_id #{userId} ORDER BY c.id DESC;LEFT JOIN保证即使商品被删除购物车记录还在前端可以提示失效。checked字段控制是否勾选结算。如果你要改成 Redis 存储可以把 key 设计成cart:userId用 Hash 存productId - count但要注意持久化和过期策略练手阶段 MySQL 更直观。5. 避坑与常见问题跑不起来时先查这几处5.1 数据库导入报外键约束错误现象执行 SQL 文件时中途报Cannot add or update a child row: a foreign key constraint fails。原因通常是插入数据的顺序和建表外键依赖不一致或者 SQL 文件里先插了子表数据再插主表。解决在导入前执行SET FOREIGN_KEY_CHECKS 0;导入完再SET FOREIGN_KEY_CHECKS 1;。如果还不行检查 SQL 文件里有没有DROP TABLE IF EXISTS有的话按顺序执行。5.2 后端启动报数据源配置失败现象启动日志里出现Failed to configure a DataSource: url attribute is not specified。原因一般是application.yml没改或者改错了位置比如改到了application-dev.yml但没激活 profile。解决确认spring.datasource下的url、username、password、driver-class-name四项都填了MySQL 8 的驱动类是com.mysql.cj.jdbc.DriverURL 里加serverTimezoneAsia/Shanghai。5.3 前端页面请求接口 404现象HTML 能打开但商品列表一直转圈Network 里接口返回 404。原因可能是前端请求的路径和后端 Controller 映射不一致或者后端没配静态资源映射。解决打开浏览器开发者工具看请求的完整 URL和后端RequestMapping对比。如果是路径前缀问题在后端加server.servlet.context-path或在 Nginx 里 rewrite。另外确认 HTML 里的baseURL变量指向的是localhost:8080而不是别的端口。5.4 MyBatis 字段映射为 null现象接口返回 200但 JSON 里商品名称、价格都是 null。原因通常是数据库字段名product_name和实体类属性productName没做驼峰映射。解决在application.yml里加mybatis.configuration.map-underscore-to-camel-case: true或者在 mapper.xml 里手动写AS别名。如果只有个别字段为 null检查resultMap有没有漏配。5.5 端口被占用导致启动失败现象启动报Port 8080 was already in use。原因是你本地已经有其他服务占了 8080。解决要么关掉那个服务要么在application.yml里改server.port为 8081 或其他空闲端口。改完记得前端请求地址也要同步改否则又会 404。用lsof -i:8080或netstat -ano | findstr 8080可以查是谁占的。6. 二次开发入手点从改一个字段到加一个模块6.1 先做最小改动验证链路拿到一个能跑的项目我建议第一件事不是加功能而是改一个字段看整条链路通不通。比如给商品表加一个subtitle字段然后从 SQL 到实体类到 mapper.xml 到前端页面全部改一遍。这个动作能帮你摸清数据流向后面加模块心里有底。ALTER TABLE product ADD COLUMN subtitle VARCHAR(255) DEFAULT NULL COMMENT 副标题 AFTER name;改完 SQL在Product实体类加private String subtitle;在ProductMapper.xml的resultMap和INSERT、UPDATE语句里补上最后在 HTML 列表页加一列显示。全程不用重启数据库但后端要重启。如果页面能显示出来说明你已掌握这个项目的扩展方式。6.2 加一个分类筛选接口商品列表通常需要按分类筛选。你可以在原有list接口上加一个categoryId参数SQL 里用动态if判断。select idlistByCondition resultMapBaseResultMap SELECT * FROM product where if testcategoryId ! null AND category_id #{categoryId} /if if testkeyword ! null and keyword ! AND name LIKE CONCAT(%, #{keyword}, %) /if /where ORDER BY id DESC /selectwhere标签会自动处理第一个AND避免语法错误。CONCAT是 MySQL 拼字符串的写法别用。Controller 层把categoryId和keyword作为可选参数接收前端加一个下拉框和搜索框就能用。这个改动不大但能让你熟悉 MyBatis 动态 SQL 的写法。6.3 把订单状态改成枚举管理原项目里订单状态可能是用0、1、2这种魔法数字。二次开发时建议改成枚举可读性和可维护性都会好很多。public enum OrderStatus { UNPAID(0, 待支付), PAID(1, 已支付), SHIPPED(2, 已发货), COMPLETED(3, 已完成), CANCELLED(4, 已取消); private final int code; private final String desc; OrderStatus(int code, String desc) { this.code code; this.desc desc; } public int getCode() { return code; } public String getDesc() { return desc; } public static OrderStatus of(int code) { for (OrderStatus s : values()) { if (s.code code) return s; } throw new IllegalArgumentException(未知订单状态: code); } }实体类里status字段仍然存int但业务逻辑里用OrderStatus.of(order.getStatus())来判断避免到处写if (status 1)。前端展示时把desc返回去用户看到的是「已支付」而不是「1」。这个改动不影响数据库但能让后续加状态流转逻辑清晰很多。6.4 一个我踩过的坑别急着改表结构最后说个血泪经验。我第一次跑这类项目时觉得原表结构不够用上来就加了好几个字段还改了类型结果 mapper.xml 里几十处 SQL 全要跟着改改到一半就乱了。后来我学乖了先跑通原版把每个模块的 SQL 和实体对应关系记清楚再动结构。如果确实要加字段一次只加一个加完立刻验证增删改查四条链路。从那以后我每次二次开发都强制走一遍「改字段 → 重启 → 页面验证 → 接口回归」的流程再也没出现过改一处崩一片的情况。希望这份拆解能帮你少走点弯路把这份谷粒商城资源真正跑起来、改起来。本文还有配套的精品资源点击获取