简介:这是一套面向计算机专业本科生毕业设计与Java实战学习的校园二手交易平台完整工程,基于SpringBoot+MySQL构建,解决学生毕设选题难、项目落地难、系统功能不全等实际问题,亦适用于课程设计与期末大作业。压缩包共232个文件,含53个核心Java业务类、26个HTML前端页面、24个JS交互脚本、10个CSS样式文件及29个JPG/GIF图片资源,涵盖前后端全部源码、SQL建库建表脚本、LayUI前端框架样式(如layui.css、goods_main.css等)、Docker部署配置及详细项目说明文档,整体大小仅5.58MB,轻量易部署。已有2824人下载学习,所有模块均经严格调试,可直接运行:前端支持商品分类浏览、用户注册登录、在线支付与个人中心管理;后台提供用户、商品、订单、钱包及系统设置等六大管理功能,结构清晰、注释规范、扩展性强,是兼具教学价值与工程实践参考意义的高质量毕设范例。
1. 项目概述与核心价值
最近在整理硬盘时,翻出了几年前带学生做的一个毕业设计项目——“基于SpringBoot的校园二手交易平台”。这个项目在当时反响不错,很多同学都基于它顺利完成了答辩,甚至有些学弟学妹毕业后创业做校园服务,其核心思路也来源于此。今天,我就把这个项目的完整源码和数据库文件拿出来,结合我这些年带项目的经验,从头到尾拆解一遍。这不仅仅是一个“毕业设计.zip”压缩包,我更想分享的是,如何从一个简单的想法出发,构建一个结构清晰、可扩展、真正能跑起来的Web应用。无论你是正在为毕设发愁的计算机专业学生,还是想快速上手SpringBoot全栈开发的入门开发者,这篇文章都能给你提供一条清晰的路径和一堆可以直接“抄作业”的代码。
这个平台的核心目标很明确:为校内学生提供一个安全、便捷的二手物品交易环境。想象一下,毕业生离校时带不走的专业书籍、几乎全新的台灯、只用过一学期的健身器材,以及新生入学时需要采购的二手自行车、课本、生活用品,这里就是一个理想的对接平台。它要解决的不是淘宝、闲鱼那样的泛化需求,而是聚焦于“校园”这个封闭、高信任度的场景,因此功能设计上会特别强调身份认证(如学号验证)、校内配送、线下自提、论坛讨论区等特色模块。技术上,我们选择了SpringBoot作为后端框架,搭配MyBatis-Plus和MySQL,前端则使用主流的Vue.js或Thymeleaf(根据项目版本不同),整体架构是经典的前后端分离(或服务端渲染),确保项目既易于学习理解,又具备良好的工程实践。
2. 项目整体架构与技术选型解析
2.1 为什么是SpringBoot?
在开始看代码之前,我们必须先理解技术选型背后的逻辑。为什么这个项目,乃至如今绝大多数Java系的毕业设计和中小型项目,都首选SpringBoot?答案在于“约定大于配置”的理念和强大的生态。
SpringBoot本质上是对传统Spring框架的再封装和自动化。回想一下十年前做Java Web项目,光是一个SSH(Spring+Struts+Hibernate)的环境搭建,就需要配置无数的XML文件,处理各种jar包冲突,部署到Tomcat还要折腾半天。SpringBoot通过内嵌Tomcat/Jetty服务器、提供一系列“Starter”依赖包、以及自动配置机制,将开发者从繁琐的配置中解放出来。对于毕业设计而言,这意味着你可以把宝贵的时间集中在业务逻辑的实现上,而不是和环境搏斗。在这个二手平台项目中,我们只需要在pom.xml里引入spring-boot-starter-web,spring-boot-starter-data-jpa(或mybatis-spring-boot-starter),spring-boot-starter-thymeleaf等几个依赖,一个具备Web MVC、数据持久化、模板渲染能力的基础框架就准备好了。
注意:SpringBoot版本的选择至关重要。我见过很多同学直接使用最新的3.x版本,结果在整合一些老旧的第三方库(如某些版本的Shiro或Redis客户端)时遇到兼容性问题。对于毕业设计,我建议选择一个成熟稳定的版本,比如SpringBoot 2.7.x。它资料丰富,社区问题解答多,能避免很多不必要的麻烦。本源码包通常基于2.x版本构建。
2.2 前后端分离 vs. 服务端渲染
这是一个架构上的关键决策,直接影响到你的开发模式、团队协作和最终部署。本项目的源码可能提供了两种范式,我们需要理解各自的适用场景。
1. 服务端渲染(使用Thymeleaf模板):这是早期版本可能采用的方式。后端Controller处理请求后,直接将数据模型(Model)传递给Thymeleaf模板引擎,由服务器生成完整的HTML页面返回给浏览器。它的优点是开发简单、速度快、SEO友好(因为返回的是完整的HTML),非常适合内容型、页面交互不复杂的应用。对于个人开发的毕设,这是一个高效的选择。你只需要关注Java后端和简单的HTML/CSS/JS,无需额外学习前端框架。
2. 前后端分离(使用Vue.js + RESTful API):这是当前更主流、也更体现工程能力的架构。后端SpringBoot项目纯粹提供RESTful API接口,返回JSON数据。前端则是一个独立的Vue.js项目,通过Axios等库调用后端接口,在浏览器端动态渲染页面。这种架构的优势在于前后端职责清晰、可以并行开发、前端用户体验更流畅(单页面应用),且后端API可以被多种客户端(如小程序、App)复用。如果你的毕设要求体现技术广度,或者你想深入学习现代Web开发流程,强烈推荐此模式。
在本项目源码中,你可能会看到src/main/resources/templates目录下的.html文件(Thymeleaf),也可能看到一个独立的frontend或vue-project目录。理解你拿到的是哪一种,是正确运行项目的第一步。
2.3 数据库设计与核心表结构
任何交易平台的核心都是数据。一个糟糕的数据库设计会让后续开发举步维艰。这里,我结合经验,提炼出校园二手平台最核心的几张表及其设计要点。
用户表 (user):这是所有业务的起点。除了常规的id、username、password(务必加密存储)、phone、email外,必须包含校园特色字段,如student_id(学号,可用于验证校内身份)、college(学院)、dormitory(宿舍楼)。密码加密推荐使用Spring Security的BCryptPasswordEncoder,这是目前最安全便捷的方式之一。
商品表 (goods):这是平台的血液。关键字段包括:
title:商品标题,需要设置合理的长度限制和索引。description:商品详情,建议使用TEXT类型。price:价格,使用DECIMAL(10,2)类型确保精度。category_id:关联分类表,实现商品分类浏览。user_id:关联发布者。status:商品状态(0-待审核,1-出售中,2-已售出,3-已下架)。这是一个非常重要的状态机设计,所有业务流程都围绕状态流转。cover_image:封面图URL。这里涉及文件上传功能,建议将图片上传到对象存储(如七牛云、阿里云OSS)或项目静态目录,数据库中只存路径。
订单表 (order):注意,order是SQL关键字,建表时通常用反引号包裹或使用order_info等别名。核心字段:
order_sn:唯一订单号,通常由时间戳+随机数生成,用于对外展示。goods_id:购买的商品。buyer_id/seller_id:买卖双方ID。total_price:订单总价。status:订单状态(0-待付款,1-待发货,2-待收货,3-已完成,4-已取消,5-退款中)。订单状态的设计比商品状态更复杂,它直接对应着支付、物流、售后等完整流程。address:收货地址。在校园场景下,可以简化为“XX宿舍楼XX号”或选择“校内快递点自提”。
分类表 (category)、聊天/留言表 (message)、收藏表 (favorite)等也是常见组成部分。一个良好的设计是在项目初期就用PowerDesigner或在线工具画出ER图,理清实体关系。
3. 核心功能模块实现细节与避坑指南
3.1 用户认证与权限控制
安全是交易平台的基石。我们不能让任何人随意发布商品或下单。本项目通常会采用Session或JWT(JSON Web Token)方案。
Session方案:这是最传统的方式。用户登录后,服务器创建一个Session,将用户信息存入其中,并生成一个Session ID通过Cookie返回给浏览器。后续请求,浏览器自动携带此Cookie,服务器通过Session ID找到对应用户信息。SpringBoot中整合非常方便,但需要注意分布式场景下的Session共享问题(毕业设计单机部署可忽略)。常见坑点:未设置Session过期时间,导致安全风险;或是在前后端分离项目中,需要手动处理跨域时的Cookie传递。
JWT方案:这是当前前后端分离架构下的主流。用户登录成功后,服务器用密钥生成一个包含用户ID等信息的Token字符串返回给前端。前端后续请求在HTTP Header(通常是Authorization: Bearer <token>)中携带此Token。服务器验证Token签名有效性后即可识别用户。它的优点是无状态、适合分布式。关键实现步骤:
- 引入依赖:
jjwt(Java JWT库)。 - 编写一个
JwtUtil工具类,用于生成和解析Token。 - 创建一个
JwtAuthenticationFilter过滤器,在每次请求前,从Header中取出Token并解析,将用户信息存入SecurityContext或Request属性中。 - 使用Spring Security或自定义注解(如
@RequireLogin)来保护需要登录的接口。
实操心得:对于校园平台,我强烈建议在用户注册/登录环节,增加一个“学号验证”的步骤。这未必是真正的对接学校教务系统(那需要接口权限),可以简化为:注册时填写学号,系统向该学号对应的学校邮箱(假设格式为
学号@学校域名.edu.cn)发送一封验证邮件,用户点击邮件中的链接完成验证。这能极大增强平台的“校园属性”和可信度,也是毕设答辩中的一个亮点。
3.2 商品发布与图片上传
这是用户接触最多的功能之一,体验必须流畅。核心是表单处理和文件上传。
后端Controller设计:
@PostMapping("/goods/publish") @ResponseBody // 如果前后端分离,返回JSON public Result publishGoods(@RequestParam("title") String title, @RequestParam("price") BigDecimal price, @RequestParam("categoryId") Integer categoryId, @RequestParam("description") String description, @RequestParam("coverImage") MultipartFile coverImage, HttpSession session) { // 或用@RequestAttribute从过滤器中获取用户ID // 1. 参数校验(标题非空、价格大于0等) // 2. 处理图片上传 String imagePath = fileUploadService.uploadImage(coverImage); // 3. 构建Goods对象,设置状态为“待审核”(0) Goods goods = new Goods(); goods.setTitle(title); goods.setPrice(price); goods.setCoverImage(imagePath); goods.setStatus(0); goods.setUserId(getCurrentUserId(session)); // 4. 调用Service层保存到数据库 goodsService.save(goods); return Result.success("商品发布成功,等待审核"); }文件上传服务 (FileUploadService):这里有两个关键选择。
- 本地存储:最简单。使用
MultipartFile.transferTo()方法将文件保存到服务器磁盘的某个目录(如/upload),并在数据库中存储相对路径(如/upload/20230520/abc.jpg)。然后需要配置静态资源映射,让外部能访问到这个目录。致命坑点:项目打包成Jar后,transferTo到Jar包内的路径会失败!必须使用绝对路径,或者将上传目录设置在Jar包外部(如System.getProperty("user.dir"))。// 示例:获取项目运行目录下的upload文件夹 String uploadDir = System.getProperty("user.dir") + "/upload/"; File dir = new File(uploadDir); if (!dir.exists()) dir.mkdirs(); String fileName = UUID.randomUUID() + "_" + file.getOriginalFilename(); File dest = new File(uploadDir + fileName); file.transferTo(dest); return "/upload/" + fileName; // 返回访问路径 - 云对象存储:更专业、更推荐。使用阿里云OSS、腾讯云COS或七牛云等服务的SDK。优点是扩容方便、访问速度快、不占用应用服务器资源。你需要先在云平台创建Bucket,获取AccessKey和SecretKey,然后在项目中配置。虽然多了一步,但这绝对是毕设的加分项,体现了你对生产环境部署的考量。
3.3 商品搜索与列表展示
平台首页和分类页的核心是商品列表,并且需要支持搜索。这里涉及数据库查询优化。
基础列表查询:使用MyBatis-Plus的QueryWrapper可以轻松构建。
// GoodsService.java public Page<GoodsVO> getGoodsList(Integer pageNum, Integer pageSize, Integer categoryId, String keyword) { Page<Goods> page = new Page<>(pageNum, pageSize); QueryWrapper<Goods> wrapper = new QueryWrapper<>(); wrapper.eq("status", 1); // 只查询出售中的商品 if (categoryId != null && categoryId > 0) { wrapper.eq("category_id", categoryId); } if (StringUtils.isNotBlank(keyword)) { wrapper.like("title", keyword).or().like("description", keyword); } wrapper.orderByDesc("create_time"); // 按发布时间倒序 Page<Goods> goodsPage = goodsMapper.selectPage(page, wrapper); // 将Page<Goods> 转换为 Page<GoodsVO>,VO中可能包含发布者昵称等信息 return convertToGoodsVOPage(goodsPage); }搜索优化:当商品数量增多时,like '%keyword%'会导致全表扫描,性能极差。解决方案:
- 数据库层面:对
title和description字段建立全文索引(FULLTEXT INDEX,MySQL需使用MyISAM或InnoDB并版本支持),然后使用MATCH ... AGAINST语法进行全文检索,效率高很多。 - 引入搜索引擎:对于真正的大型应用,会引入Elasticsearch或Solr。但对于毕业设计,如果数据量不大,使用数据库全文索引或简单的
like查询(并做好索引)即可。你可以在答辩时提及更优方案的思路,展示你的知识广度。
分页技巧:MyBatis-Plus的Page对象非常好用。但要注意,前端传递的pageNum(页码)通常从1开始,而Page构造函数内部会自动处理。确保你的pageSize(每页条数)有一个合理的最大值限制,比如100,防止恶意请求拖垮数据库。
3.4 交易流程与状态机实现
这是业务逻辑最复杂的部分,核心是商品状态和订单状态的流转。我们必须保证状态变更的原子性和一致性,避免出现“超卖”(同一商品被多人下单)等经典问题。
下单流程:
- 校验:用户点击购买,后端接口首先校验商品是否存在且状态为“出售中”(1)。
- 库存/状态锁:这是防止超卖的关键。不能先查询状态为“出售中”,然后直接创建订单。因为在这两个操作之间,可能有其他请求也通过了校验。标准做法是使用乐观锁或悲观锁。
- 乐观锁:在商品表中增加一个
version字段。更新商品状态时,带上查询时的version值:update goods set status=2, version=version+1 where id=#{id} and version=#{oldVersion}。如果更新影响行数为0,说明期间已被别人修改,则下单失败。 - 悲观锁(SELECT ... FOR UPDATE):在查询商品信息时直接加行锁,但性能开销较大,在简单场景下,对于毕业设计,更简洁的方式是:
- 乐观锁:在商品表中增加一个
- 直接更新:最实用的方法是,在下单事务中,直接执行一条更新语句:
update goods set status=2 where id=#{goodsId} and status=1。如果这条SQL执行后影响的行数为1,说明成功锁定了该商品,可以继续创建订单;如果为0,说明商品状态已不是“出售中”,则下单失败。这种方式在MySQL的RR(可重复读)隔离级别下是安全的。 - 创建订单:商品锁定成功后,生成唯一订单号,创建订单记录,状态为“待付款”(0)。
支付与状态流转: 校园二手平台为了简化,常常模拟支付流程。可以设计一个“模拟支付”接口,调用后直接将订单状态改为“待发货”(1)。更完整的流程应包括:
- 待付款(0) -> (用户支付) -> 待发货(1)
- 待发货(1) -> (卖家点击发货) -> 待收货(2)
- 待收货(2) -> (买家确认收货) -> 已完成(3)
- 任何适当阶段 -> (用户取消) -> 已取消(4)
每一个状态变更,都应该记录操作日志,并在必要时通知对方(通过站内信或WebSocket)。
4. 数据库文件解析与项目初始化
拿到源码包里的SQL文件(通常是schema.sql和data.sql),不要直接导入就完事。理解其结构,是你掌握项目的第一步。
建表语句 (schema.sql):打开这个文件,你会看到一系列CREATE TABLE语句。重点关注:
- 字符集和排序规则:通常会是
utf8mb4和utf8mb4_general_ci。utf8mb4才是真正的UTF-8,支持存储emoji表情,一定要用这个。 - 引擎:
ENGINE=InnoDB。InnoDB支持事务、行级锁,是MySQL的默认和推荐引擎。 - 字段类型选择:
id:主键,BIGINT自增。- 价格相关:
DECIMAL(10,2),精确表示10位数,其中2位小数。 - 文本:短文本用
VARCHAR(255),长文本用TEXT。 - 时间戳:
create_time,update_time,类型为datetime或timestamp。建议使用timestamp,它可以自动跟踪时间变化。
- 索引:查看除了主键外,还在哪些字段上建立了索引(
KEY或INDEX)。通常在外键字段(user_id,goods_id)、经常用于查询和排序的字段(status,create_time)上建索引,能极大提升查询速度。
初始数据 (data.sql):这个文件可能包含一些初始数据,如管理员账号、商品分类等。务必修改默认管理员密码!这些密码往往是明文或简单加密,导入后第一件事就是登录后台修改成一个强密码。
项目配置与启动:
- 数据库连接:找到
src/main/resources/application.yml或application.properties文件,修改其中的数据库连接信息(url,username,password)为你本地的MySQL配置。 - 文件上传路径:如果使用本地存储,检查配置中指定的上传目录是否存在,或根据代码逻辑创建。
- 启动类:找到标注了
@SpringBootApplication的主类,直接运行它的main方法。SpringBoot会启动内嵌的Tomcat服务器。 - 访问:控制台无报错后,在浏览器访问
http://localhost:8080(默认端口)。
5. 毕业设计深度优化与扩展思路
如果你希望你的毕设不止于“能运行”,而是脱颖而出,获得高分,可以考虑以下优化和扩展方向。这些也是面试中常被问到的项目亮点。
5.1 引入缓存提升性能
随着访问量和商品数据增加,频繁查询数据库会成为瓶颈。引入Redis作为缓存层是标准的性能优化方案。
应用场景:
- 首页热门商品列表:将计算好的热门商品列表(按浏览量、收藏量排序)存入Redis,设置过期时间(如10分钟)。用户访问首页时,直接从Redis获取,极大减轻数据库压力。
- 商品详情页:商品信息相对稳定,可以将
Goods对象序列化成JSON字符串存入Redis,Key设计为goods:{id}。查询时先查缓存,未命中再查数据库并回填缓存。 - 会话存储:如果使用Spring Session,可以将会话数据存储到Redis,实现分布式Session共享。
整合步骤:
- 引入依赖:
spring-boot-starter-data-redis。 - 配置
application.yml中的Redis连接信息。 - 使用
RedisTemplate或更便捷的StringRedisTemplate进行操作。
注意事项:缓存数据与数据库数据的一致性问题。当商品信息被修改或下架时,必须同时删除或更新Redis中对应的缓存。这可以通过在更新数据库的Service方法中添加@CacheEvict注解来实现。
5.2 实现站内信与实时通信
平台内的买卖双方需要沟通。简单的做法是使用“留言”功能,每条留言关联商品和用户。更高级的体验是实现类聊天工具的实时通信。
方案一:轮询(简单但低效)前端定时(如每5秒)向后端发送请求,查询是否有新消息。实现简单,但实时性差,服务器压力大。
方案二:WebSocket(推荐)建立长连接,实现服务器主动向客户端推送消息。SpringBoot提供了对WebSocket的很好支持,可以使用STOMP子协议来简化开发。
- 引入依赖:
spring-boot-starter-websocket。 - 配置WebSocket端点。
- 前端使用SockJS和Stomp.js库建立连接并订阅频道(如
/user/{userId}/queue/messages)。 - 当买家向卖家发送消息时,后端将消息存入数据库,并通过WebSocket实时推送到卖家前端。
这个功能能显著提升项目的技术含量和用户体验。
5.3 增加后台管理功能
一个完整的平台必须有后台管理系统,用于审核商品、管理用户、处理投诉等。这通常是一个独立的模块或一套页面。
技术实现:可以复用现有的SpringBoot后端,新增一套以/admin开头的API接口。前端可以:
- 使用一套新的Thymeleaf模板(与服务端渲染架构结合)。
- 或者新建一个Vue.js后台项目(与前后端分离架构结合)。
核心功能:
- 仪表盘:展示平台关键数据,如总用户数、今日交易额、待审核商品数。
- 商品管理:列表展示所有商品,支持按状态筛选,提供“审核通过”、“审核驳回”、“强制下架”等操作。
- 用户管理:查看用户列表,禁用违规账号。
- 订单管理:查看所有订单,处理纠纷。
- 系统设置:管理商品分类、公告等。
权限控制:后台所有接口必须进行严格的权限校验。可以使用Spring Security,定义角色(如ROLE_ADMIN),并通过注解@PreAuthorize("hasRole('ADMIN')")来保护接口。
5.4 部署上线与持续集成
将项目部署到公网,让同学老师都能访问,是毕设答辩的终极展示。这涉及到服务器、域名、部署流程等一系列知识。
简易部署(传统方式):
- 购买一台云服务器(如腾讯云、阿里云的学生机,很便宜)。
- 在服务器上安装JDK、MySQL、Nginx。
- 将项目打包成可执行的Jar文件:
mvn clean package。 - 将Jar文件上传到服务器,使用
nohup java -jar your-project.jar &命令在后台运行。 - 配置Nginx作为反向代理,将80端口的HTTP请求转发到SpringBoot应用的8080端口。Nginx还可以用来托管前端静态文件(如果是分离架构)和配置SSL证书实现HTTPS。
进阶部署(Docker容器化): 使用Docker可以将应用及其依赖环境打包成一个镜像,实现一次构建,处处运行。你需要编写Dockerfile和docker-compose.yml文件。这能让你在简历上写下“具备容器化部署经验”,是一个很大的加分项。
持续集成/持续部署(CI/CD):虽然对毕设来说可能有点“杀鸡用牛刀”,但了解其概念很有价值。你可以使用GitHub Actions或Jenkins,配置当代码推送到Git仓库的main分支时,自动执行测试、打包、并部署到服务器。这体现了现代化的软件工程实践。
6. 常见问题排查与调试技巧
在运行和开发这个项目的过程中,你几乎一定会遇到下面这些问题。这里我把我踩过的坑和解决方案总结给你。
6.1 数据库连接失败
这是最常见的问题,没有之一。
- 现象:启动时报错
Communications link failure或Access denied for user。 - 排查:
- 检查
application.yml中的url,username,password是否正确。特别注意url中的serverTimezone参数,在中国通常设置为Asia/Shanghai,否则可能因时区问题导致时间字段错误。 - 检查MySQL服务是否启动。
- 检查该用户是否有从本地(
localhost)或远程(%)连接的权限。对于本地开发,确保用户有localhost的权限。
- 检查
- 解决:登录MySQL命令行,执行:
GRANT ALL PRIVILEGES ON your_database.* TO 'your_username'@'localhost' IDENTIFIED BY 'your_password'; FLUSH PRIVILEGES;
6.2 端口被占用
- 现象:启动时报错
Web server failed to start. Port 8080 was already in use。 - 解决:
- 找到占用端口的进程并杀死:
lsof -i:8080找到PID,然后kill -9 PID(Linux/Mac)。Windows下使用netstat -ano | findstr :8080,然后到任务管理器中结束对应PID的进程。 - 或者,直接在
application.yml中修改SpringBoot的启动端口:server.port: 8081。
- 找到占用端口的进程并杀死:
6.3 前端页面访问不到或样式丢失
- 现象:能访问
localhost:8080,但页面是白的,或者没有CSS样式。 - 排查:
- 服务端渲染项目:检查
Controller的返回值是否正确指向了模板文件(如return "index";对应templates/index.html)。检查静态资源(CSS, JS, 图片)是否放在了src/main/resources/static/目录下。 - 前后端分离项目:确保前端项目已独立启动(如
npm run serve),并运行在另一个端口(如8081)。然后检查后端接口地址配置是否正确(前端项目中axios的baseURL是否指向了localhost:8080)。最关键的是跨域问题!SpringBoot后端需要配置CORS。// 添加一个配置类 @Configuration public class CorsConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedOrigins("http://localhost:8081") // 你的前端地址 .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") .allowCredentials(true) .maxAge(3600); } }
- 服务端渲染项目:检查
6.4 文件上传失败或找不到
- 现象:发布商品时,图片上传失败,或上传后前端显示不出来。
- 排查:
- 路径问题:绝对路径还是相对路径?在开发环境(IDE中运行)和生产环境(打包成Jar运行)下,获取当前路径的行为可能不同。务必使用绝对路径或外部化配置。建议在
application.yml中定义一个配置项:upload.path: /path/to/upload,然后在代码中读取。 - 权限问题:运行Java程序的用户是否有在目标上传目录的写入权限?
- 静态资源映射:如果文件保存在本地,SpringBoot需要配置静态资源映射才能被外部访问。
这样,存储在spring: web: resources: static-locations: classpath:/static/, file:${upload.path} # 将上传目录加入静态资源路径/path/to/upload/abc.jpg的文件,就可以通过http://localhost:8080/upload/abc.jpg访问。
- 路径问题:绝对路径还是相对路径?在开发环境(IDE中运行)和生产环境(打包成Jar运行)下,获取当前路径的行为可能不同。务必使用绝对路径或外部化配置。建议在
6.5 事务不生效
- 现象:在下单流程中,扣减商品库存和创建订单两个操作,一个成功一个失败,数据不一致。
- 原因:没有正确使用Spring的事务管理。
- 解决:确保在
Service层的方法上添加了@Transactional注解。并且,该方法必须是public的,且是从类外部调用(通过代理),在类内部调用(this.method())会导致事务失效。同时,检查数据库引擎是否为InnoDB(只有它支持事务)。
最后,我想说的是,这个“校园二手交易平台”的源码,是一个非常好的学习和实践模板。但千万不要止步于让它跑起来。尝试去修改它的UI,增加一个“求购”功能,或者实现我上面提到的WebSocket聊天。在调试bug、阅读日志、查阅文档的过程中,你的收获会远远超过代码本身。编程的本质是解决问题,而这个项目,给了你一个绝佳的、贴近真实的问题场景。祝你开发顺利,答辩成功。
本文还有配套的精品资源,点击获取