ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

基于Spring Boot的Java二手交易平台课设源码实战解析

2026/10/4 1:22:36 拓冰建站 浏览量
基于Spring Boot的Java二手交易平台课设源码实战解析 简介一套基于Java和Spring Boot框架的二手交易平台完整项目面向正在进行毕业设计或课程设计的计算机相关专业学生也适合作为Java Web开发课程实训参考可帮助学习者从零搭建一个可运行的前后端分离系统覆盖用户注册登录、商品发布与搜索、订单管理等核心业务模块。压缩包内共809个文件整体体积16.84MB主要文件类型包括130个Java源文件、48个Vue组件、44个HTML页面、153个JS脚本、44个CSS样式同时附带SQL数据库初始化脚本、Spring Boot配置文件及一键启动脚本前后端代码结构完整目录清晰便于本地部署。运行环境采用JDK1.8数据库使用MySQL5.7或8开发工具可选择Eclipse或IDEA项目已经过测试可正常启动运行。目前已有41人学习对于毕业设计答辩、课程设计演示以及理解Spring Boot与Vue前后端协同开发的流程都有直接的参考价值。资源内还包含运行说明、数据库导入文件与项目目录文档使用者可在现有功能基础上便捷修改和扩展快速形成可交付的课程项目。1. Java 二手交易平台课设源码能跑通、能答辩、能改造成自己的项目做 Java 课设或者毕设最难熬的不是写代码而是你从零开始写了一个星期最后发现登录接口 500、数据库连接超时、前端页面乱成一锅粥。这份基于 Spring Boot 的二手交易平台源码我第一次拆完的印象是它不像那些故意加密、故意缺文件的“残缺资源”目录结构完整连 index.html 和几个 Vue 组件的 .bak 备份文件都躺在里面说明作者是真实开发过一段时间的。项目本身是标准的 Java 单体应用JDK 1.8 Spring Boot MySQL 5.7/8适合拿来直接跑通再按自己的需求改造成“像是自己写的”课设作品。如果你是那种“只想找个能跑的项目交差”的或者“想抄一套完整模块再动手改”的这份源码都够用。文章后面我会把它拆成项目结构、环境搭建、核心模块实现、踩坑记录和二次开发技巧。2. 项目结构和关键技术选型先看懂 .bak 文件和 Maven 依赖再动手2.1 Spring Boot 骨架和那些 .bak 文件是怎么回事打开压缩包你会看到一批文件我按“开箱即用”的优先级帮你排个序1-install.bat和2-run.bat是 Windows 环境下的安装和启动脚本.classpath是 Eclipse 的项目配置文件用 IDEA 导入时会自动忽略它mvnw.cmd是 Maven 的包装器保证你机器上没有装全局 Maven 也能拉依赖。最显眼的是几个 .bak 文件index.html.bak、update-password.vue.bak、IndexAsideStatic.vue.bak、BreadCrumbs.vue.bak、IndexHeader.vue.bak。.bak 文件本质上是开发者的“后悔药”。比如update-password.vue.bak大概率是作者在重写修改密码功能之前把旧版本备份了一份。我的判断是主项目结构是 Spring Boot 后端 Vue 前端页面混合部署——Spring Boot 的src/main/resources/static目录下放着编译后的静态资源比如那个app.348460df.css而 .vue 源文件是开发期的前端源码。这种结构在这个项目里很常见后端用 Spring MVC 提供接口前端用 Vue 写页面最后mvn package打成 jar 包前端资源被复制进target/classes/static一个 jar 就能启动。你在答辩的时候如果能讲明白“为什么项目里有 .bak 文件”反而是加分项——说明你重视版本管理懂得在改动前留备份。但如果你有洁癖也可以直接把 .bak 文件删掉不影响运行。2.2 Maven 依赖和数据持久化方案Spring Data JPA 还是 MyBatis从项目文件推测这个项目用 Maven 管理依赖核心依赖应该包括spring-boot-starter-web、spring-boot-starter-data-jpa、mysql-connector-java、spring-boot-starter-test这类常规组合。Spring Boot 2.x 自动配置了 Spring MVC 和 Spring Data JPA这意味着你不用写一堆 XML 配置文件一个application.yml就能搞定数据源和 JPA 配置。这里我必须说一下选型问题现在很多培训机构和网上的课设资源默认用 MyBatis因为 SQL 可控、易于调试。但这份资源既然走了 Spring Data JPA 路线你在答辩时就要能说清楚 JPA 的优点——它把数据库表映射成 Java 实体类CRUD 操作通过接口方法直接完成不需要手写 SQL。比如你定义一个GoodsRepository extends JpaRepositoryGoods, Long继承的方法是findAll()查全部、findByNameContaining(String keyword)做模糊搜索、deleteById(Long id)删除。如果导师问为什么不用 MyBatis你就回答“JPA 更适合快速开发实体关系映射清晰而且 Spring Boot 对它有自动配置支持减少了大量样板代码。”这个回答在答辩时很安全。我用一张表把这个项目的技术栈和常见替代方案列清楚方便你答辩时应对不同提问角度技术点本项目方案常见替代方案场景选择理由开发语言Java 8Java 11 / 17JDK 1.8 最稳定课设环境兼容性最好后端框架Spring Boot 2.xSSM / Spring MVC自动配置、内嵌 Tomcat、无需部署 war 包持久层Spring Data JPAMyBatis / MyBatis-Plus实体映射、CRUD 快答辩好解释数据库MySQL 5.7 / 8.0H2 / SQLite开箱即用Navicat 可视化好操作前端Vue 静态资源Thymeleaf / JSP前后端分离思路接口联调清晰构建工具MavenGradle依赖管理简单mvnw 保证环境一致当你打开pom.xml看依赖时重点关注三个地方spring-boot-starter-parent锁定了 Spring Boot 版本spring-boot-starter-web包含了内嵌 Tomcatmysql-connector-java的版本需要和 MySQL 版本匹配否则容易出现时区报错这个后面避坑章节会详细说。2.3 启动脚本拆解1-install.bat 和 2-run.bat 到底做了什么这两个批处理文件是这份资源最贴心的设计也是本地最快的路径。1-install.bat的内容我推测是echo off echo 正在安装依赖并打包项目... call mvnw.cmd clean install -DskipTests echo 安装完成 pause2-run.bat大概是echo off echo 正在启动二手交易平台... call mvnw.cmd spring-boot:run pause逻辑很简单先执行clean install把项目打包成 jar 并安装到本地 Maven 仓库再通过spring-boot:run启动。关键参数是-DskipTests意思是跳过单元测试直接打包——课设阶段测试代码往往不完善如果src/test目录下有测试类会因为环境问题跑挂跳过可以节省时间。这里注意一个细节mvnw.cmd是 Maven 包装脚本它会在当前目录下找.mvn/wrapper/maven-wrapper.properties文件读取其中指定的 Maven 版本并自动下载到本地用户目录这样即使你的电脑没装 Maven 也能跑。但现实中我建议你打开1-install.bat确认一下里面的内容因为有些资源的install脚本写的是mvn install依赖全局 Maven而不是mvnw.cmd install。如果你本机已经装了 Maven用哪个都一样如果没装靠mvnw.cmd会更稳妥。双击脚本后如果闪退不要慌多半是命令执行失败——在命令行窗口里手动执行一次看看报错信息是 Java 版本不对还是依赖下载超时。另一个想到的点是这个项目既然是前后端混合部署index.html应该放在src/main/resources/static下。如果你要用 IDEEclipse 或 IDEA启动可以直接运行src/main/java下带SpringBootApplication注解的主类它的main方法一启动就内嵌 Tomcat监听 8080 端口。这就是为什么很多课设项目都选 Spring Boot——省去配置 Tomcat 外置容器的成本。3. 环境准备与本地复现安装、配置、启动把项目真正跑起来3.1 JDK、MySQL、Navicat 和 IDE 的版本匹配细节要把这份源码跑起来第一关是环境变量这个步骤踩坑率极高尤其是 Windows 11 用户。我按实操顺序说一遍你照着做基本一次过。先装 JDK 1.8。注意你从 Oracle 官网下载的 jdk-8uXXX-windows-x64.exe 装完后需要手动配置三个环境变量JAVA_HOME C:\Program Files\Java\jdk1.8.0_202 按你实际安装路径填 Path %JAVA_HOME%\bin 追加到系统变量 Path 最前面 CLASSPATH .;%JAVA_HOME%\lib\dt.jar;%JAVA_HOME%\lib\tools.jar配置完成后打开 cmd 验证java -version javac -version如果你看到了java version 1.8.0_202和javac 1.8.0_202说明环境变量配好了。很多人的翻车点在于 Path 里没配bin目录或者配了但 cmd 没重启——所以验证前记得新开一个窗口别用之前开的旧窗口。数据库方面我推荐 MySQL 5.7原因很简单5.7 的密码加密规则是mysql_native_password和 JDBC 驱动兼容性最好。如果你装了 MySQL 8连接 jdbc 时大概率会报Public Key Retrieval is not allowed或Unable to load authentication plugin caching_sha2_password解决方式是在 JDBC URL 后面加上allowPublicKeyRetrievaltrueuseSSLfalse。这个后面避坑章节专门说。Navicat 连接 MySQL 时注意root 用户的密码尽量设成root或123456因为项目的application.yml或者application.properties里写的默认密码通常是这两个之一。连接成功后用 Navicat 执行项目里附带的.sql文件如果有或者手动建库——库名一般叫secondhand或second_hand。如果资源里没有 SQL 文件那就说明 JPA 的ddl-auto配置是update启动时会自动建表。3.2 从零启动全流程数据库导入到浏览器访问我写一个标准流程适合从来没见过这个项目的人照着走一遍。前提是你已经装好了 JDK 8 MySQL Navicat IDEA。第一步用 IDEA 打开项目根目录。IDEA 会自动读取pom.xml并开始下载 Maven 依赖。如果你的网络环境不好依赖下载很慢可以在 IDEA 的设置里把Maven → Importing → VM options for importer加一行-Dmaven.wagon.http.retryHandler.count3减少重试等待。第二步等项目左下角进度条走完打开src/main/resources找到配置文件。我推测是application.yml或application.properties内容类似spring: datasource: url: jdbc:mysql://localhost:3306/second_hand?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/ShanghaiuseSSLfalse username: root password: root driver-class-name: com.mysql.cj.jdbc.Driver jpa: hibernate: ddl-auto: update show-sql: true几个关键参数解释一下serverTimezoneAsia/Shanghai是解决 MySQL 8 的时区报错问题不加它你会看到The server time zone value й׼ʱ is unrecognized这种乱码报错useSSLfalse是告诉客户端不要用 SSL 连接本地开发没必要ddl-auto: update意味着 Hibernate 启动时检查实体类和数据库表的映射关系缺少表就自动建表字段变了就自动加列。这个配置对课设来说很好用你新加实体类后重启项目表就自动建好了不用手写 SQL。但它也有个坑——如果删掉实体类的某个字段JPA 不会自动删列需要手动去数据库里删否则报错说 Unknown column。第三步运行主类。IDEA 里右键Application.java类名可能是 SecondHandApplication、MainApplication 之类的选择Run控制台滚动一段日志后你看到类似Tomcat started on port(s): 8080就说明启动成功。打开浏览器输入http://localhost:8080如果能看到首页可能是登录页或者商品列表页就说明项目跑通了。如果端口被占用常见于你开过其它 Java 项目有两条路可选一是改配置文件里的server.port为 8081二是找到占用进程杀掉。我一般直接用第二种因为课设答辩时端口写 8080 更稳妥netstat -ano | findstr :8080 taskkill /PID 占用的PID /Fnetstat列出所有占用 8080 的进程 PIDtaskkill强制结束对应进程。注意这个操作在 Windows 的 cmd 里跑不要在 IDE 的 Terminal 里跑有的 IDE 终端是 bash 环境命令会报错。3.3 前端资源缺失的应急处理dist 目录和 npm build 的关系有个情况我要提前预警如果你发现启动后浏览器页面空白或者 F12 控制台报Failed to load resource: 404然后错误指向某个.js或.css文件那大概率是static目录下的前端资源不完整。这个问题我会放在后面的避坑章节深入讲。但这里你可以先做一个动作——去src/main/resources/static目录下看index.html引用的资源文件名和static目录下实际存在的文件做对比缺什么文件就用项目里的 .vue 源码重新构建。构建前端需要 Node.js 环境。你在项目根目录找找有没有package.json有的话说明前端源码和依赖在根目录运行npm install npm run buildnpm install会根据package.json里的dependencies拉取 Vue、Element UI、vue-router 等依赖npm run build会执行打包脚本把 .vue 源文件编译成浏览器能识别的 js/css并输出到dist目录。打包完把dist目录里的文件复制到src/main/resources/static下重启 Spring Boot 项目前端就正常了。这里有个细节如果你用的 Node 版本太高比如 18有些老项目的node-sass会安装失败我建议装 Node 14 LTS 版本兼容性最好。如果你的项目里用的是sassdart-sass那 Node 16 以上也没问题。4. 核心功能模块的实现套路从注册登录到订单状态机4.1 用户注册登录BCrypt 加密和 Session 管理二手交易平台最外层的功能模块是用户注册登录。这里扯一点安全性的问题答辩时老师最爱问的就是“密码怎么存的”。劣质的课设会明文存密码稍微好一点的是 MD5但正确的做法是用 BCrypt。如果你在项目的pom.xml里看到spring-security-crypto或者代码里出现BCryptPasswordEncoder那恭喜你作者做了件正确的事。常见写法是PostMapping(/register) public R register(RequestBody User user) { BCryptPasswordEncoder encoder new BCryptPasswordEncoder(); String encodedPassword encoder.encode(user.getPassword()); user.setPassword(encodedPassword); userRepository.save(user); return R.success(注册成功); }当你调用encoder.encode(user.getPassword())时BCrypt 会随机生成一个盐值salt把盐值和密码一起做哈希生成一个长度为 60 的字符串格式类似$2a$10$7QnXjWNqU2lUuBxR1m7Z0eY9CYxnKxXG6lfYM3nU1oF8yRqZwsnWm。这个字符串本身包含了盐值和算法参数所以校验时直接用encoder.matches(明文密码, 数据库中的密文)就能判断。这比 MD5 安全在哪MD5 没有盐同一个密码永远是同一个哈希值黑客拿彩虹表一对比就出来了BCrypt 每次加密的盐值随机同一个密码两次加密结果不同彩虹表直接失效。登录后的会话管理这个项目大概率用的是 HttpSession。遇到.vue备份文件说明前端页面有 Vue RouterAPI 调用时会在请求头带 token 或者用 Cookie 保持会话。我的经验是课程设计这个复杂度用 HttpSession 就够了——后端登录成功后session.setAttribute(user, user)需要鉴权的接口在 Controller 里取 session 校验代码简单答辩也说得明白。不建议上 Spring Security 全家桶因为那东西的配置复杂度够你再写一个课设。4.2 商品发布和浏览文件上传与图片存储策略商品发布模块是这个平台的业务核心。关键点有两个一是发布表单的数据绑定二是商品图片上传。Spring Boot 处理文件上传用MultipartFile接口常见代码PostMapping(/goods/publish) public R publish(RequestParam(title) String title, RequestParam(price) BigDecimal price, RequestParam(description) String description, RequestParam(image) MultipartFile image, HttpSession session) { // 1. 保存图片到服务器本地 String originalFilename image.getOriginalFilename(); String ext originalFilename.substring(originalFilename.lastIndexOf(.)); String fileName UUID.randomUUID().toString() ext; String uploadDir D:/upload/; File dest new File(uploadDir fileName); image.transferTo(dest); // 2. 把商品信息写入数据库 Goods goods new Goods(); goods.setTitle(title); goods.setPrice(price); goods.setDescription(description); goods.setImage(/images/ fileName); goods.setSellerId(((User) session.getAttribute(user)).getId()); goodsRepository.save(goods); return R.success(发布成功); }这里解释了为什么用UUID.randomUUID()生成文件名——因为如果不重命名两个用户同时上传同名的a.jpg后上传的会覆盖先上传的导致图片错乱。加上 UUID 后文件名唯一永远不会冲突。图片路径可以硬编码为D:/upload/但这只是课设做法实际开发会配置成可配置项用application.yml的custom.upload-dir属性来注入。浏览功能Spring Data JPA 的分页查询是标准姿势GetMapping(/goods/list) public PageGoods list(RequestParam(defaultValue 1) Integer page, RequestParam(defaultValue 12) Integer size) { PageRequest pageRequest PageRequest.of(page - 1, size, Sort.by(createTime).descending()); return goodsRepository.findAll(pageRequest); }PageRequest.of(page - 1, size, Sort.by(createTime).descending())三个参数的含义分别是页码从 0 开始前端传 1 这里减一、每页条数、排序规则——按创建时间倒序新发布的商品排前面。如果你前端能做分页器这个接口返回的total、pages、records字段够用了。4.3 订单管理从“已下单”到“已完成”的状态流转设计订单模块最能体现你的“设计感”答辩时讲清楚能拿不少分。二手平台的订单状态一般是0待付款 → 1已付款 → 2已发货 → 3已完成外加一个4已取消作为退出路径。任何状态的流转方向都是单向的不能跳转比如不能从0待付款直接跳到3已完成。代码层面我用一个简单的状态机来实现而不是散落一堆 if-elsepublic enum OrderStatus { PENDING_PAYMENT(0, 待付款), PAID(1, 已付款), DELIVERED(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 boolean canTransitionTo(OrderStatus target) { switch (this) { case PENDING_PAYMENT: return target PAID || target CANCELLED; case PAID: return target DELIVERED; case DELIVERED: return target COMPLETED; default: return false; } } }为什么用枚举而不是用整数直接比较因为整数魔法值容易写错且不可读比如你在 Service 层看到if (order.getStatus() 1)根本不知道这个1是什么意思。而用枚举配合canTransitionTo方法所有状态流转规则集中在一个文件里改流转规则只动一处前端要显示状态描述也直接拿枚举的desc字段不用再写一个 switch 映射表。在 Service 层付款操作的逻辑大概是Transactional public void pay(Long orderId) { Order order orderRepository.findById(orderId).orElseThrow(); if (!order.getStatus().canTransitionTo(OrderStatus.PAID)) { throw new BusinessException(当前状态不可支付); } order.setStatus(OrderStatus.PAID); order.setPayTime(LocalDateTime.now()); orderRepository.save(order); }Transactional注解保证整个方法要么全做要么全不做如果orderRepository.save()失败回滚前面修改内存里实体的操作也不会落库避免了“支付成功但订单状态没改”的数据不一致问题。这里值得关注的是我在校验状态时直接抛了BusinessException说明这个项目有全局异常处理器。如果你要自己写可以加一个RestControllerAdvice类捕获业务异常和系统异常统一返回{code: 500, msg: 当前状态不可支付}给前端。4.4 搜索模块关键字匹配的两种实现方案和取舍商品搜索功能课设项目一般有两种做法。第一种是 JPA 方法命名查询简单但搜索能力有限ListGoods findByTitleContainingOrDescriptionContaining(String keyword1, String keyword2);Spring Data JPA 解析这个方法名——findBy固定开头TitleContaining表示 title 字段 like 匹配Or表示或的关系DescriptionContaining表示描述字段也 like 匹配。最终生成的 SQL 是WHERE title LIKE %keyword% OR description LIKE %keyword%。这种方法适合关键字精确到单词的场景中文分词不支持搜索“苹果手机”匹配不到“iPhone”这种同义词。第二种是全文本搜索。MySQL 的MATCH...AGAINST是官方方案但需要建全文索引而且中文分词效果一般。如果在项目里看到了FullTextField之类的注解那应该是用了 Hibernate Search但这种重量级方案在课设里其实是杀鸡用牛刀还会增加答辩时讲不清楚的风险。我的建议是课设阶段用第一种就够关键词匹配商品标题和描述面试时能说出“用 JPA 的Containing关键字实现模糊搜索缺点是只做字面匹配、不做语义分析”就已经合格了。如果你的项目时间充裕想加个亮点可以用 Redis 做搜索热词的缓存——把用户搜索的关键词 push 到 Redis 的 ZSet按搜索次数排序前台展示热门搜索。这个进价不需要改数据库表结构复杂度可控但前提是你的环境里有 Redis没有的话别硬上。5. 避坑与常见问题排查我拆过 30 个课设项目的血泪经验课设项目翻车几率最高的不是写业务代码而是环境、依赖、资源缺失这三座大山。我把这些年拆项目常见的坑集中写一下每条都是“现象 → 原因 → 解决”的结构你照着排查。5.1 启动报错java.lang.IllegalStateException: Failed to execute CommandLineRunner现象控制台出现大段红色报错关键信息是Failed to execute CommandLineRunner后面跟一段 Caused by。原因项目里有一个或多个实现了CommandLineRunner接口的类它在应用启动后自动执行——常见用途是初始化管理员账号或者往数据库写入测试数据。它执行失败的原因一般是依赖没准备好比如数据库表还没建立、或者 Redis 连接不上、或者某个配置项读取到了 null。解决找到报错中 Caused by 后面的第一行那才是根因。如果是数据库连接相关的检查application.yml里的 URL 和用户名密码是否正确MySQL 服务有没有启动如果是 NPE说明初始化逻辑里某个数据缺失。然后确认ddl-auto是update而不是none或validate——如果是validate启动时 Hibernate 校验实体和表结构不匹配就会直接报错根本走不到 Runner。快速验证的办法把 CommandLineRunner 的具体业务代码注释掉重启看是否能过能过就说明是 Runner 的逻辑问题。5.2 MySQL 8 驱动的身份验证插件不兼容现象项目连接 MySQL 8启动时报Unable to load authentication plugin caching_sha2_password或者Public Key Retrieval is not allowed。原因MySQL 8 默认的认证插件是caching_sha2_password而老版本 JDBC 驱动5.x 系列不认识这个插件另外 JDBC 默认不主动获取公钥需要显式开启。解决最省事的是换 MySQL 5.7。如果你一定要用 MySQL 8有两个动作缺一不可。第一确认pom.xml里的mysql-connector-java版本是 8.0.x比如 8.0.335.x 的驱动在 8.版数据库上就是各种兼容问题。第二JDBC URL 加参数url: jdbc:mysql://localhost:3306/second_hand?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/ShanghaiuseSSLfalseallowPublicKeyRetrievaltrueallowPublicKeyRetrievaltrue允许客户端从服务器获取公钥useSSLfalse关闭 SSL 加密这两个参数必须搭配出现否则仍然报错。如果你用的是 Navicat 连接 MySQL 8也要把连接设置里的“使用 SSL”关掉。5.3 前端页面样式丢失或接口 404现象浏览器能打开index.html但页面没有样式、控制台报.css或.js文件 404或者前端能显示但所有请求接口都返回 404。原因这个项目的前端资源是从 Vue 打包出来的静态文件。如果打包后没有复制到src/main/resources/static或者运行时访问的路径和实际放的路径不一致就会 404。接口 404 大概率是路由前缀写错——前端请求/api/goods/list但后端的RequestMapping(/goods/list)没有/api前缀或者反过来。解决先按我之前说的对比index.html里引用的文件名和static目录实际文件名。如果缺的是编译产物用npm run build重新构建再复制。如果是接口路径不一致解决方式加一个全局路径前缀在启动类里注入 WebMvcConfigurerConfiguration public class WebConfig implements WebMvcConfigurer { Override public void configurePathMatch(PathMatchConfigurer configurer) { configurer.addPathPrefix(/api, c - true); } }addPathPrefix(/api, c - true)表示给所有 Controller 的路径统一加上/api前缀c - true是一个断言predicate意思是所有 Controller 都加。这样你 Controller 里的RequestMapping(/goods/list)实际对外暴露的路径变成/api/goods/list前端如果本来就请求/api开头的地址就对齐了。如果你想只给部分类加前缀可以把c - true改成c - c.isAnnotationPresent(MyApi.class)之类的条件。5.4 JPA 实体加字段后报 Unknown column现象你在实体类里加了个private String phone;重启项目后查询报错Unknown column phone in field list。原因Hibernate 的ddl-auto是update时只负责自动建表和加列但它监听的入口是启动时实体扫描。如果你修改了实体类而重启时 JPA 没成功触发SchemaUpdate或者项目里配置的是ddl-auto: validate只校验不更新数据库表结构就和实体对不上。解决确认application.yml里ddl-auto的值是update然后重启项目看控制台有没有Hibernate: alter table的日志。如果没有说明ddl-auto不是update或者 Hibernate 扫描不到这个实体类。保险的做法是直接用 Navicat 手动手工加列ALTER TABLE goods ADD COLUMN phone VARCHAR(20) NULL;还有一个容易被忽略的点JPA 的字段名映射默认采用驼峰转下划线——实体类sellerId映射成数据库列seller_id。如果你手写 SQL 时写成了sellerId就会报错。这个问题我也遇到过排查到怀疑人生最后发现是命名策略在作怪。Hibernate 的SpringPhysicalNamingStrategy会把驼峰自动转下划线所有 JPQL 中写sellerId没问题但原生 SQL 里必须写seller_id。5.5 端口被占用导致启动失败现象启动报Port 8080 was already in use或者Web server failed to start。原因很简单的直接原因上一个 Java 进程没关干净或者你开了多个项目。解决Windows 下最快的方式是找到进程杀掉netstat -ano | findstr :8080 taskkill /PID 1234 /Fnetstat -ano列出所有端口和对应的 PIDfindstr :8080过滤出 8080 端口的行第二列最后一列是 PID。taskkill /PID 1234 /F强杀进程。如果这个进程是 IDEA 的 Spring Boot 进程杀完在 IDEA 里重新运行就行不会出问题。如果你不想每条命令都敲也可以直接把配置文件里的端口换掉server: port: 8081但我要提醒一句如果前端页面里写死了请求地址http://localhost:8080/api/...你改后端端口后前端全连不上需要同步改前端所以本地调试还是推荐杀进程而不是改端口。6. 把课设改造成“自己的项目”的三个技巧加密、防刷、数据回滚项目跑通只是第一步你真正要交的课设是“你自己做出来的作品”。我的建议是不要通篇重写而是在现有骨架上加三个亮点这三个点都是面试官和答辩老师喜欢看到的且改动量可控。第一个技巧是给管理后台的接口加一个简单的登录拦截器。这个项目里大概率已经有用户登录但管理员的接口可能没做受控访问。用一个拦截器统一拦截Component public class AdminInterceptor implements HandlerInterceptor { Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { HttpSession session request.getSession(); User user (User) session.getAttribute(user); if (user null || !ADMIN.equals(user.getRole())) { response.setStatus(401); response.getWriter().write({\code\:401,\msg\:\无权访问\}); return false; } return true; } }然后在配置类里注册拦截器registry.addInterceptor(adminInterceptor) .addPathPatterns(/admin/**) .excludePathPatterns(/admin/login);addPathPatterns(/admin/**)表示拦截所有以/admin/开头的 URLexcludePathPatterns(/admin/login)放行登录接口。注册之后未登录管理员访问管理接口时后端返回 401 的 JSON 而不是页面前端拿这个状态码跳转登录页。这个拦截器看起来简单但能体现你对“权限控制”的理解而且代码量不大答辩完全讲得清。第二个技巧是给用户密码字段加 AES 加密。我知道数据库存 BCrypt 哈希是安全做法但如果你想让项目更完善一点可以在前端把密码 AES 加密后再传输后端解密再 BCrypt。这个设计在真实项目里很常见——防止密码在 HTTP 明文传输中被抓包。实现方式是在pom.xml里加hutool-all工具包然后用工具包封装加密解密public class AesUtil { private static final String KEY your-secret-key; public static String encrypt(String content) { AES aes SecureUtil.aes(KEY.getBytes(StandardCharsets.UTF_8)); return aes.encryptHex(content); } public static String decrypt(String content) { AES aes SecureUtil.aes(KEY.getBytes(StandardCharsets.UTF_8)); return aes.decryptStr(content); } }注意一个容易翻车的点SecureUtil.aes(KEY.getBytes())的 KEY 必须是 16、24 或 32 字节不能随便填一个短于 16 字节的字符串否则会报InvalidKeyException。我一般直接写死一个 16 字节的固定值比如abc123def456gh78。这个 AesUtil 放在src/main/java/com/xxx/utils包下存储密码时先decrypt得到明文再 BCrypt 加密。多一个中间的 AES 层这在答辩时能多讲一分钟的“传输安全设计”。第三个技巧是商品发布和订单支付的事务回滚验证。这是最常见的“项目质量加分”场景当订单支付成功后发通知发送失败、更新库存发生异常不应该出现“扣了款但订单状态没变”的情况。做法是在 Service 层方法上加Transactional然后主动抛异常验证回滚Transactional public void buyGoods(Long goodsId, Long buyerId) { Goods goods goodsRepository.findById(goodsId).orElseThrow(); if (goods.getStatus() ! 0) { throw new BusinessException(商品已下架); } // 扣库存 goods.setStock(goods.getStock() - 1); goodsRepository.save(goods); // 创建订单 Order order new Order(); order.setGoodsId(goodsId); order.setBuyerId(buyerId); orderRepository.save(order); // 模拟异常验证回滚 if (goods.getStock() 0) { throw new BusinessException(库存不足); } }Transactional保证当方法抛出任何 RuntimeException包括 BusinessException时之前执行的所有save()操作全部回滚数据库回到方法执行前的状态。你可以故意把goods.setStock()改成goods.setStock(goods.getStock() - 10)来制造负库存触发异常后去数据库确认goods表那条记录怎么变——如果库存没变负、订单记录没新增说明回滚生效了。这个验证实验在答辩时可以当场演示给老师看非常加分。从那以后我每次拿到课设资源都会强制走一遍这套流程先验环境再跑通主功能然后加一个权限拦截器和事务回滚验证最后才是换皮改文案。你在这个项目上花两个下午把上面这些代码加进去这份二手交易平台就不再是别人的作业而是你的作品了。希望帮到你——动手跑一遍比看十篇文章都管用。本文还有配套的精品资源点击获取