SpringBoot+MyBatis+Thymeleaf购书商城源码实战:从环境搭建到二次开发

发布时间:2026/10/27 7:23:32
SpringBoot+MyBatis+Thymeleaf购书商城源码实战:从环境搭建到二次开发

简介:这是一套面向Java Web初学者与课程设计开发者的购书商城系统完整源码,基于SpringBoot、Mybatis、Thymeleaf与MySQL构建,适合作为毕业设计、实训项目或二次开发的学习起点。资源包共109个文件,约4.81MB,其中35个Java文件承载控制层、服务层与实体逻辑,10个HTML页面配合8个CSS与9个JS文件构成前端界面,另有6个XML及yml、properties等配置文件负责框架与数据库连接,并附带SQL建表脚本与Maven构建文件,目录结构清晰。系统覆盖用户注册登录、图书浏览与搜索、分类筛选、购物车、下单购买、订单管理及后台商品与用户管理等完整购物流程,同时预留了权限控制、搜索引擎与第三方支付等扩展方向。目前已有51人学习下载,适合希望快速理解SSM到SpringBoot技术栈演进、掌握电商业务建模与前后端协作的读者参考借鉴。

1. 从一份购书商城源码说起:SpringBoot+MyBatis+Thymeleaf 能跑出什么

如果你手头正好有一套基于 SpringBoot + MyBatis + Thymeleaf + MySQL 的购书商城系统源码,第一反应大概率不是“我要学”,而是“这玩意儿能不能直接跑起来、跑起来之后长什么样”。我拆过不少 JavaWeb 整站项目,这套技术栈组合算是国内高校课设和中小型电商后台里最稳的一档:SpringBoot 负责把 Tomcat、数据源、事务这些脏活全包了,MyBatis 管 SQL 映射,Thymeleaf 做服务端渲染,MySQL 存书、订单、用户。它解决的不是高并发秒杀,而是“一个人能在一周内把一套带前后台的购书流程跑通”这件事。适合谁?正在做课程设计、想拿一个完整 CRUD 项目练手、或者需要一套能改造成自己业务原型的开发者。下面我按实际拆包顺序,把环境、配置、表结构、启动和排错一条条讲清楚。

2. 环境准备与依赖版本:别让 JDK 和 MySQL 先把你拦在门外

2.1 为什么这套组合对版本特别敏感

SpringBoot 和 MyBatis 的整合方式在 2.x 和 3.x 之间差异很大,而 Thymeleaf 的命名空间解析又跟 SpringBoot 的自动配置强绑定。我见过太多人拿着 SpringBoot 3.x 的骨架去跑一份 2.x 的源码,结果javax.servlet和jakarta.servlet直接对不上,启动就报ClassNotFoundException。所以第一步不是急着mvn spring-boot:run,而是先确认源码里的pom.xml到底锁了哪个版本。常见做法是打开pom.xml,看<parent>里的spring-boot-starter-parent版本号,再看mysql-connector-java的 scope 和版本。如果 parent 是 2.7.x,那 JDK 用 8 或 11 最稳;如果是 3.x,JDK 必须 17 起步。MySQL 这边,5.7 和 8.0 的驱动类名和连接串参数不一样,8.0 要加serverTimezone,5.7 不加也能跑但时区容易出玄学问题。

2.2 本地环境搭建的实操步骤

先装 JDK,再装 MySQL,最后配 Maven。这三步的顺序不能乱,因为 Maven 编译时要调 JDK,项目启动时要连 MySQL。下面是我一般会走的命令流程,Windows 和 macOS 通用,路径按自己机器改。

# 1. 确认 JDK 版本,源码是 2.x 就用 8 或 11 java -version # 2. 确认 Maven 可用,并设置阿里云镜像加速依赖下载 mvn -v # 3. 登录 MySQL,创建数据库(库名以源码 application.yml 为准) mysql -u root -p
-- 建库,字符集用 utf8mb4,否则中文书名会乱码 CREATE DATABASE bookstore DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; -- 建一个专用用户,避免直接用 root 跑项目 CREATE USER 'bookuser'@'localhost' IDENTIFIED BY 'Book@123456'; GRANT ALL PRIVILEGES ON bookstore.* TO 'bookuser'@'localhost'; FLUSH PRIVILEGES;

上面这段 SQL 的逻辑很直白:utf8mb4是为了存 emoji 和生僻字,购书系统里书名带特殊符号很常见;单独建用户是为了后面改配置时不用动 root 密码,也方便排错时区分权限问题。参数上,Book@123456这种密码强度够用,但你要确保application.yml里的username和password跟这里一致,否则启动时报Access denied别怪源码有问题。

2.3 依赖拉取与 IDE 导入

Maven 拉依赖慢是常态,建议在settings.xml里配好阿里云镜像。导入 IDEA 后,先等 Maven 索引完,再检查pom.xml里有没有<scope>provided</scope>的坑——有些源码会把spring-boot-starter-tomcat设成 provided,导致内置 Tomcat 不启动。如果发现启动类跑起来没端口,先看这个。另外,Thymeleaf 的模板默认放在src/main/resources/templates,静态资源在static,如果源码目录结构不对,页面会 404,这个后面排错章节会细说。

3. 数据库表结构与 MyBatis 映射:把 ER 图落到 SQL 和 XML 里

3.1 购书商城的核心表设计

一套购书系统,核心表跑不出用户、图书、分类、订单、订单明细这几张。我拆过的源码里,表名和字段命名风格差异很大,有的用下划线,有的用驼峰,MyBatis 的map-underscore-to-camel-case配置就决定了要不要手动写resultMap。下面这张表是我根据常见购书商城源码整理的核心表清单,你拿到源码后可以对照schema.sql或db目录里的 SQL 文件核对。

表名作用关键字段注意点
user用户信息id, username, password, phone, address密码是否加密要看源码,明文的话登录逻辑简单但别上生产
book图书主表id, name, author, price, stock, category_id, cover库存字段更新时要用乐观锁或stock = stock - 1防超卖
category图书分类id, name, parent_id有的源码只做一级分类,parent_id 可能没用上
orders订单主表id, user_id, total_price, status, create_timestatus 枚举值要跟前端按钮逻辑对上
order_item订单明细id, order_id, book_id, quantity, price下单时要把 book 的当前价格快照进来,不能只存 book_id

建表时最容易翻车的是外键和索引。源码里如果没加外键,你别自己加,因为 MyBatis 批量插入时外键约束会拖慢速度,而且删数据时顺序不对直接报错。索引方面,book.name和orders.user_id建议手动加普通索引,否则后台按书名搜索和用户查订单会全表扫。

3.2 MyBatis 映射文件与 SQL 写法

MyBatis 的 Mapper XML 一般放在src/main/resources/mapper下,接口在dao或mapper包。下面这段是一个典型的图书分页查询 XML,我加了注释说明每个参数的作用。

<!-- BookMapper.xml 片段:按分类和关键词分页查书 --> <select id="selectByPage" resultType="com.example.bookstore.entity.Book"> SELECT id, name, author, price, stock, cover, category_id FROM book <where> <if test="categoryId != null"> AND category_id = #{categoryId} </if> <if test="keyword != null and keyword != ''"> AND name LIKE CONCAT('%', #{keyword}, '%') </if> </where> ORDER BY id DESC LIMIT #{offset}, #{pageSize} </select>

逻辑说明:<where>标签会自动处理第一个AND,避免WHERE AND的语法错误;CONCAT('%', #{keyword}, '%')是 MySQL 的模糊匹配写法,比LIKE '%${keyword}%'安全,后者有 SQL 注入风险。参数上,offset是(pageNum - 1) * pageSize,pageSize一般设 10 或 12,看前端每行放几本书。如果你发现分页查出来的总数不对,检查有没有对应的count查询,很多源码只写了列表查询,分页插件没配好就会导致页码乱跳。

3.3 事务与库存扣减的边界

购书系统里最核心的业务是下单。下单要同时写orders、order_item,还要扣book.stock,这三步必须在同一个事务里。SpringBoot 里用@Transactional注解就能搞定,但要注意默认只回滚RuntimeException,如果源码里抛的是Exception,事务不会回滚,库存扣了订单没生成,这就是血泪经验。我一般会显式写成@Transactional(rollbackFor = Exception.class)。另外,扣库存的 SQL 最好写成UPDATE book SET stock = stock - #{quantity} WHERE id = #{bookId} AND stock >= #{quantity},用stock >= quantity做条件更新,返回影响行数为 0 就说明库存不足,直接抛异常回滚,比先查再扣更稳。

4. Thymeleaf 页面渲染与前后端交互:模板语法和静态资源路径

4.1 Thymeleaf 常用语法与购书场景

Thymeleaf 的核心是th:属性,服务端渲染时把数据填进 HTML。购书系统里高频用的是th:each循环图书列表、th:text显示书名价格、th:href拼详情页链接、th:if判断库存和登录状态。下面这段是图书列表页的典型写法。

<!-- templates/book/list.html 片段 --> <div class="book-list"> <div class="book-item" th:each="book : ${bookPage.list}"> <img th:src="@{/images/}+${book.cover}" alt="封面"> <h3 th:text="${book.name}">书名占位</h3> <p th:text="'¥' + ${book.price}">价格占位</p> <a th:href="@{/book/detail(id=${book.id})}">查看详情</a> <span th:if="${book.stock <= 0}" style="color:red;">缺货</span> </div> </div>

逻辑说明:${bookPage.list}是后端传过来的分页对象里的列表,@{/images/}是 Thymeleaf 的链接表达式,会自动加上应用上下文路径,避免部署到子路径时图片 404。th:if="${book.stock <= 0}"是库存判断,注意 Thymeleaf 里小于号要写成&lt;或者用le,直接写<在 HTML 里可能被解析成标签开头。参数上,book.cover存的是文件名还是完整路径,要看源码里上传逻辑怎么写的,如果是完整 URL,前面的@{/images/}就要去掉。

4.2 静态资源映射与登录拦截

SpringBoot 默认把static目录映射到根路径,但很多购书源码会把 CSS、JS、图片放在static/assets或static/css下,Thymeleaf 引用时路径要写对。如果页面样式全丢,打开浏览器控制台看是不是 404,然后检查application.yml里有没有自定义spring.mvc.static-path-pattern。登录拦截一般用HandlerInterceptor实现,在preHandle里判断 session 里有没有用户,没有就重定向到登录页。这里有个坑:拦截器如果配了excludePathPatterns但没排除静态资源,登录页的 CSS 也会被拦,导致登录页裸奔。常见做法是把/css/**、/js/**、/images/**全排除掉。

4.3 表单提交与参数绑定

购书系统里添加图书、修改库存、提交订单都是表单 POST。Thymeleaf 的th:object和th:field可以自动绑定实体字段,减少手写name属性。下面是一个添加图书的表单片段。

<form th:action="@{/admin/book/save}" th:object="${book}" method="post"> <input type="text" th:field="*{name}" placeholder="书名"> <input type="text" th:field="*{author}" placeholder="作者"> <input type="number" th:field="*{price}" step="0.01" placeholder="价格"> <input type="number" th:field="*{stock}" placeholder="库存"> <button type="submit">保存</button> </form>

逻辑说明:th:object="${book}"绑定后端传来的空实体,th:field="*{name}"会自动生成name="name"和id="name",提交时 SpringMVC 按字段名注入。参数上,step="0.01"是价格输入框的精度,别小看这个,少了它浏览器可能只让输整数。如果提交后报400 Bad Request,先看实体字段类型和表单输入是否匹配,比如价格用Double接收但输入了非数字,绑定就失败。

5. 启动排错与常见问题:从 404 到 500 的排查路径

5.1 启动报错:数据源和驱动类

现象:启动时控制台报Failed to configure a DataSource: 'url' attribute is not specified。原因:application.yml里数据源配置没写全,或者spring.datasource.url拼写错了。解决:检查url、username、password、driver-class-name四项,MySQL 8.0 的驱动类名是com.mysql.cj.jdbc.Driver,5.7 可以用com.mysql.jdbc.Driver,但 8.0 用旧驱动会警告。连接串里 8.0 必须加serverTimezone=Asia/Shanghai,否则时间字段差 8 小时。

5.2 页面 404:模板路径和控制器映射

现象:启动成功,访问首页报 404。原因:Thymeleaf 模板没放在templates下,或者 Controller 的@GetMapping路径跟浏览器访问的不一致。解决:先看src/main/resources/templates下有没有index.html,再看 Controller 类上的@RequestMapping和方法上的@GetMapping拼起来是什么路径。如果用了@Controller但方法返回String却没配thymeleaf前缀后缀,也会 404。默认配置是classpath:/templates/和.html,一般不用改。

5.3 中文乱码:数据库、连接串和页面编码

现象:书名显示成问号或乱码。原因:数据库字符集不是utf8mb4,或者连接串没加characterEncoding=utf8,或者 Thymeleaf 页面没声明UTF-8。解决:三步一起查——建库时用utf8mb4,连接串加useUnicode=true&characterEncoding=utf8,HTML 头部加<meta charset="UTF-8">。如果还乱,检查 MySQL 的my.ini或my.cnf里character-set-server是不是utf8mb4。

5.4 库存扣成负数:并发下的更新顺序

现象:下单后库存变成负数。原因:先查库存再扣减,两个请求同时查到库存为 1,都扣了 1。解决:把扣减写成条件更新UPDATE book SET stock = stock - #{qty} WHERE id = #{id} AND stock >= #{qty},根据返回的影响行数判断是否成功。如果源码里用的是updateByPrimaryKey直接覆盖,那就要改成自定义 SQL。这个坑在课设答辩时被问到概率很高,提前改掉能省不少解释。

5.5 MyBatis 映射失败:字段名和 resultMap

现象:查询返回的对象字段全是 null。原因:数据库字段是category_id,实体是categoryId,但没开驼峰映射,也没写resultMap。解决:在application.yml里加mybatis.configuration.map-underscore-to-camel-case: true,或者手动写<resultMap>把列和属性对应上。如果只有个别字段 null,检查 SQL 里有没有AS别名,别名要和实体属性名一致。

6. 二次开发与验证:把购书商城改成你自己的业务原型

拿到一套能跑的源码只是起点,真正有价值的是知道怎么改。我一般会先跑通一条完整链路:注册 → 登录 → 浏览图书 → 加入购物车 → 下单 → 后台改库存 → 前台看到库存变化。这条链路走通,说明数据源、事务、模板渲染、拦截器都没大问题。然后按下面几个方向做二次开发验证。

改造方向改动点验证方法
换业务主题把book表改成product,字段名同步改全局搜索替换后启动,看列表页和详情页是否正常
加分类筛选在BookMapper.xml加category_id条件,Controller 加参数传不同分类 ID,看返回列表是否变化
接支付模拟在订单状态里加paid枚举,下单后直接改状态查数据库orders.status是否按预期流转
换前端模板把 Thymeleaf 页面改成 Bootstrap 或 Layui看静态资源路径和th:属性是否冲突

验证时我习惯用curl或 Postman 直接打接口,绕过页面看 JSON 返回,这样能快速区分是后端逻辑问题还是模板渲染问题。比如查图书列表接口,如果返回的 JSON 里字段齐全,但页面不显示,那就是 Thymeleaf 表达式写错了;如果 JSON 里就是 null,那就是 MyBatis 映射或 SQL 的问题。

还有一个我踩过的坑:有些源码的pom.xml里引了spring-boot-devtools,热部署会缓存模板和静态资源,改了 HTML 不生效。这时候要么在application.yml里关掉spring.thymeleaf.cache,要么直接重启。从那以后我每次改 Thymeleaf 页面都强制先关缓存再调,省得对着没变化的页面怀疑人生。希望这套拆解能帮你把这份购书商城源码真正跑起来、改下去。

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