ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

SpringBoot网上蛋糕店管理系统设计与实现:从数据库到订单事务

SpringBoot网上蛋糕店管理系统设计与实现:从数据库到订单事务

简介:这是一份基于SpringBoot的网上蛋糕售卖店管理系统毕业设计资源,适合计算机相关专业学生、准备毕业设计的开发者以及想学习前后端分离项目的人群。系统完整划分管理员、店员、用户三种角色,覆盖商品上架、购物车结算、论坛互动、公告发布、轮播图配置等核心业务,能帮助读者理解电商系统常见的权限控制与模块拆分。资源包共5个文件,包含可运行的源码压缩包、项目设计论文doc文档、数据库db.sql脚本、说明文档txt以及docx版表结构说明,压缩包整体约37.34MB,目录结构清晰,便于按需查阅。目前已有37人学习下载。随包附带万字设计报告和初始化数据库脚本,拿到后导入MySQL即可启动项目,既可用于毕业设计参考,也可复现SpringBoot+Vue全栈开发流程,对学习商品管理、订单处理和角色权限设计均有直接帮助。

1. 基于SpringBoot的网上蛋糕售卖店管理系统:值不值得动手做

看到这个标题,我第一反应是“又是课设三件套”。但说句公道话,像网上蛋糕售卖店这种管理系统,恰恰是SpringBoot入门最完整的练习载体:它不算大,却能覆盖登录鉴权、商品展示、购物车、下单、后台订单管理、销量统计这些高频业务。相比那些只做单表增删改查的示例,这套业务能把前后端联调、事务、状态流转、SQL聚合一次练全,配套的数据库脚本和万字文档也适合拿去改造成毕业设计或课设。适合谁?正在学SpringBoot想做个小项目写进简历的人,或者被课设题目卡住需要一份能跑通的完整工程的人。这篇我会把系统设计、核心代码、配置参数到踩坑记录都拆开讲。

2. 先看懂设计再动手:角色权限与数据库表怎么拆

2.1 管理员端和用户端各管哪些事:权限模型怎么划

这种网上蛋糕售卖店系统,业务上天然分成两端。用户端做浏览、加入购物车、下单、查订单;管理员端做商品管理、分类管理、订单发货、看统计数据。如果是课程设计,很多同学会做成一个账号表加role字段,登录后靠判断role来跳转。但我更推荐把管理员和用户拆成两张表——表结构干净,查询逻辑更简单,后端也不需要到处判断角色。

用户表 user:负责前台登录 管理员表 admin:负责后台登录

两边的功能边界大概是这样的:

角色可用功能落库表
用户注册/登录、浏览蛋糕、加入购物车、下单、查看自己的订单user、cart、orders、order_item
管理员登录、管理分类、管理商品、处理订单状态、查看销售统计admin、category、cake、orders

权限拦截不要写在Controller里到处if判断,用Spring Boot的拦截器(HandlerInterceptor)统一拦截,只放行登录接口和注册接口,其他都要校验Session。这个在第4章会给出完整实现。先记住一点:前端页面隐藏按钮不等于安全,真正的权限控制要在后端拦截器上做。

2.2 蛋糕商品、订单、会员三张核心表:字段设计为什么这样定

数据库是整个系统的地基,我一般先把表关系画明白再写代码。网上蛋糕售卖店的核心表至少包含分类表、蛋糕商品表、用户表、购物车表、订单主表、订单明细表。分类和商品是1对多,订单主表和订单明细是1对多,用户和订单是1对多。下面这几张表是这套系统里最重要的,字段设计直接影响后面写SQL的难度。

先看蛋糕商品表cake:

CREATE TABLE cake ( id INT AUTO_INCREMENT PRIMARY KEY COMMENT '商品ID', category_id INT NOT NULL COMMENT '所属分类ID', name VARCHAR(100) NOT NULL COMMENT '蛋糕名称', description TEXT COMMENT '商品描述', price DECIMAL(10,2) NOT NULL COMMENT '售价', stock INT NOT NULL DEFAULT 0 COMMENT '库存', image VARCHAR(255) COMMENT '图片路径', sales INT DEFAULT 0 COMMENT '销量', status TINYINT DEFAULT 1 COMMENT '1上架 0下架', create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间' ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='蛋糕商品表';

价格用DECIMAL(10,2)而不是FLOAT或DOUBLE,因为二进制浮点数做金额运算是出了名的不可靠,0.1+0.2这种精度问题不要再踩第二次。库存字段stock在下单时要做扣减判断,这个字段会参与事务操作,不能允许为NULL,否则容易出现NULL+1导致库存错乱。status字段做上下架,商品多的时候后端可以用这个字段过滤,不用担心删掉历史商品把订单关联的数据弄丢。

再看订单主表orders和订单明细表order_item:

CREATE TABLE orders ( id INT AUTO_INCREMENT PRIMARY KEY COMMENT '订单ID', order_no VARCHAR(32) NOT NULL COMMENT '订单编号', user_id INT NOT NULL COMMENT '下单用户ID', total_price DECIMAL(10,2) NOT NULL COMMENT '订单总价', status TINYINT NOT NULL DEFAULT 0 COMMENT '0待支付 1已支付 2已发货 3已完成 4已取消', receiver_name VARCHAR(50) COMMENT '收货人', receiver_phone VARCHAR(20) COMMENT '联系电话', receiver_address VARCHAR(255) COMMENT '收货地址', create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '下单时间' ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单主表'; CREATE TABLE order_item ( id INT AUTO_INCREMENT PRIMARY KEY, order_id INT NOT NULL COMMENT '关联订单ID', cake_id INT NOT NULL COMMENT '商品ID', cake_name VARCHAR(100) COMMENT '商品快照名称', price DECIMAL(10,2) COMMENT '购买时单价', quantity INT NOT NULL COMMENT '购买数量' ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单明细表';

订单明细里必须存cake_name和price这两个冗余字段。为什么?因为蛋糕商品表里的价格和名称随时会改,订单明细存的是“下单那一刻的商品快照”。如果不冗余,用户三个月前买的订单,管理员再去看会发现商品名字和价格全变了,这就是典型的“黑匣子”数据问题,到时候查账都查不清。

2.3 初始化数据与SQL脚本:跑通前必须先做的一件事

源码包里配套的数据库脚本,导入顺序有讲究。有些同学拿到脚本直接Navicat全选执行,结果建表失败,十有八九是表之间有外键依赖,导入顺序不对。我一般会先在管理工具里按分类表、蛋糕表、用户表、订单表、订单明细表的顺序单独运行建表语句,确认无误后,再执行初始化数据。

初始化数据里必须包含三个东西才能保证登录和展示不报错:

  • 一个管理员账号:默认admin/123456(密码记得是MD5加密后的值)
  • 几个测试用户账号:方便测试下单流程
  • 每个分类下至少3-5个蛋糕商品:否则首页空空荡荡,分页、详情、购物车逻辑都测不了

MySQL 5.7和8.0的建库语句都建议显式指定字符集:

CREATE DATABASE IF NOT EXISTS cake_shop DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;

utf8mb4才能完整存中文和表情符号,如果只写utf8,某些生僻字和emoji会变成问号。这也为后面第5章的中文乱码问题提前埋了个伏笔。

3. 把SpringBoot项目搭起来:POM依赖、YAML配置与启动类

3.1 Maven依赖怎么选:版本组合与数据库连接池参数

SpringBoot项目第一关就是maven依赖。很多新手在这里翻车,不是因为不会写,而是版本没配对。网上蛋糕售卖店管理系统本质是个CRUD项目,不涉及复杂中间件,依赖选择原则是“越稳越好,版本别追新”。我推荐用SpringBoot 2.7.18配合JDK 8,这是目前课设和实际项目里最稳的组合。SpringBoot 3.x要求JDK 17起步,很多同学的电脑上装的是JDK 8,一导入就报编译错,不如直接锁2.x。

核心依赖给一个可以照着用的pom.xml片段:

<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.3.2</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.33</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper-spring-boot-starter</artifactId> <version>1.4.7</version> </dependency>

这里直接写版本号是为了避免SpringBoot父工程管理不到的那个坑,特别是mysql-connector-java在SpringBoot 2.7里被改成com.mysql:mysql-connector-j了,有些同学复制老教程的mysql:mysql-connector-java导致依赖下载失败。另外,PageHelper这个分页插件选1.4.7,这个版本对SpringBoot 2.x兼容性很稳定,分页查询会省很多手写limit的工作。

连接池不用额外引druid,SpringBoot 2.x默认自带HikariCP,性能足够这个项目用。数据库连接池的几个关键参数我在3.2节配置里说。

3.2 Application.yml配置:端口、数据源、MyBatis映射与日志

一个规范的yml配置不仅决定系统能不能启动,还决定排查问题时有没有日志可看。下面这份配置我做过很多次,基本可以照抄:

server: port: 8080 servlet: context-path: / spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/cake_shop?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai username: root password: 123456 hikari: minimum-idle: 5 maximum-pool-size: 20 connection-timeout: 30000 mybatis: mapper-locations: classpath:mapper/*.xml type-aliases-package: com.cake.entity configuration: map-underscore-to-camel-case: true log-impl: org.apache.ibatis.logging.stdout.StdOutImpl logging: level: com.cake.mapper: debug

注意url里的几个参数:characterEncoding=utf8解决中文参数乱码;serverTimezone=Asia/Shanghai解决MySQL 8.0默认时区导致的连接报错,这个坑极其常见,第5章会细说。spring.datasource.hikari这个配置里minimum-idle控制在5个空闲连接,maximum-pool-size控制最大20个连接,对课设项目足够。如果数据库是MySQL 8.0,驱动类名必须写com.mysql.cj.jdbc.Driver,老教程里的com.mysql.jdbc.Driver在8.0下会直接报ClassNotFoundException。

mybatis.configuration里的map-underscore-to-camel-case这个配置特别关键,它能把数据库里的create_time自动映射成Java属性createTime,少写一堆resultMap。然后log-impl配StdOutImpl,在控制台直接打印SQL语句,调试增删改查时能看清楚参数到底传进来没有。

3.3 启动类和分层结构:为什么Controller-Service-Mapper这三层不能省

SpringBoot的启动类本身没啥花头,就是一个带@SpringBootApplication注解的类,默认会扫描启动类所在包及子包。这个“同包或子包”约束很多人不知道,把Controller放在com.cake.controller没问题,但如果放到com.cake.controller.admin里也没事,只要都在com.cake下面就行。一旦放错包,启动时会报找不到Controller或Mapper扫描不到。

常见的基础包结构是这样的:

com.cake ├── CakeApplication.java // 启动类 ├── controller/ // 控制层:接收请求、返回视图或JSON ├── service/ // 业务层:处理业务逻辑、事务 ├── mapper/ // 数据访问层:定义Mapper接口 ├── entity/ // 数据库实体类 ├── config/ // 拦截器、Web配置等 └── resources/ ├── mapper/ // 存放Mapper XML文件 ├── static/ // JS/CSS/图片 └── templates/ // Thymeleaf页面模板

为什么Controller-Service-Mapper这三层不能省?这个项目规模小,很多人偷懒直接在Controller里写业务和SQL调用,结果就是后续加需求时越来越难改。Controller只负责接收参数和返回结果,Service层放业务逻辑,Mapper层只做数据库交互。事务注解也必须加在Service层,这一点在第4章下单功能里会体现出来。

4. 从登录到下单:四个核心功能的代码实现

4.1 登录与拦截器:用Session实现角色鉴权的常见做法

用户登录先看Mapper的查询方法。这里我用一个注解SQL来演示,不单独贴XML:

@Mapper public interface UserMapper { @Select("SELECT * FROM user WHERE username = #{username} AND password = #{password}") User findByUsernameAndPassword(@Param("username") String username, @Param("password") String password); }

密码这里用MD5编码存储。注意:这只是课设级别的做法,生产环境必须用BCrypt。不过得说清楚MD5的局限,拿到源码的同学如果将来去做真实项目,要改成BCrypt,否则用户密码表泄露等于全库密码泄露。

Controller登录逻辑:

@Controller @RequestMapping("/user") public class UserController { @Autowired private UserMapper userMapper; @PostMapping("/login") public String login(String username, String password, HttpSession session) { User user = userMapper.findByUsernameAndPassword(username, password); if (user != null) { session.setAttribute("loginUser", user); return "redirect:/index"; } return "login"; } }

登录成功后把整个User对象存进Session,页面端通过thymeleaf直接取session里的loginUser.name显示用户名。管理员登录同理,只是存的对象换成Admin。

拦截器是这套系统鉴权的关键。不写拦截器的话,用户不登录也能直接访问后台接口,这就等于系统裸奔。自定义拦截器:

public class LoginInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { Object user = request.getSession().getAttribute("loginUser"); if (user == null) { response.sendRedirect("/login"); return false; } return true; } }

然后在配置类里注册这个拦截器,并且放行静态资源和登录页面:

@Configuration public class WebConfig implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new LoginInterceptor()) .addPathPatterns("/**") .excludePathPatterns("/login", "/user/login", "/register", "/static/**", "/css/**", "/js/**", "/images/**"); } }

注意顺序:addPathPatterns先拦截所有路径,再用excludePathPatterns逐个放行。这里最容易翻车的两个点:一是忘记放行静态资源,导致登录页面样式全丢;二是把放行写成了/*,只放行了第一级路径,/css/style.css这种二级路径还是会被拦,乍一看玄学,实际是路径通配符没写对。

4.2 商品列表与分页:查得又快又稳的写法

用户端首页要展示蛋糕列表,后台管理也要看商品列表,分页是这套系统躲不开的功能。用PageHelper插件比手写LIMIT更省事,但用法有讲究。先看Service层:

public PageInfo<Cake> getCakeList(int pageNum, int pageSize) { PageHelper.startPage(pageNum, pageSize); List<Cake> list = cakeMapper.selectAll(); return new PageInfo<>(list); }

PageHelper.startPage有个重要原则:它只对紧接着的下一条SQL查询生效。所以startPage和调用mapper查询之间不能有任何其他查询操作夹在中间,否则分页会莫名失效,把不该分页的SQL也给分页了。Controller里接收页码和每页条数:

@GetMapping("/index") public String index(@RequestParam(defaultValue = "1") int pageNum, @RequestParam(defaultValue = "12") int pageSize, Model model) { PageInfo<Cake> pageInfo = cakeService.getCakeList(pageNum, pageSize); model.addAttribute("pageInfo", pageInfo); return "index"; }

前端thymeleaf里用pageInfo.list遍历商品,用pageInfo.pageNum取当前页码,pageInfo.pages取总页数。pageSize给12是因为蛋糕列表页一般做3列4行或者4列3行的网格布局,一屏刚好装满。如果用户想看更多,后台就改pageSize参数,这个值没必要在代码里写死。

4.3 购物车与下单:事务注解为什么必须加在Service层

购物车我见过的课设实现有两种:一种建cart表存数据库,一种用Session临时存。数据库方案的好处是用户下次登录购物车还在,坏处是每次操作都要读写表;Session方案简单但购物车会丢失。这套系统的源码大多用数据库方案,因为要写入数据库增删改查的说明和代码,文档和答辩都需要数据库操作作为亮点。既然用数据库,那购物车表就是用户ID+商品ID+购买数量三件套。

下单流程是整个系统里最重要的一段业务代码。它至少要完成四件事:从购物车取商品、校验库存、扣减库存、生成订单和明细。任何一步失败都不能留下半截数据,所以必须加事务。正确写法是Service方法加@Transactional:

@Service public class OrderService { @Autowired private OrderMapper orderMapper; @Autowired private CakeMapper cakeMapper; @Transactional(rollbackFor = Exception.class) public boolean createOrder(Order order, List<CartItem> cartItems) { // 1. 生成订单主表记录 order.setCreateTime(new Date()); orderMapper.insert(order); // 2. 扣库存并写入订单明细 for (CartItem item : cartItems) { Cake cake = cakeMapper.selectById(item.getCakeId()); if (cake.getStock() < item.getQuantity()) { throw new RuntimeException("商品库存不足"); } cakeMapper.reduceStock(item.getCakeId(), item.getQuantity()); orderMapper.insertOrderItem(order.getId(), item.getCakeId(), cake.getName(), cake.getPrice(), item.getQuantity()); } // 3. 清空该用户的购物车 orderMapper.clearCart(order.getUserId()); return true; } }

@Transactional(rollbackFor = Exception.class)这行必须写全。Spring的事务默认只在遇到RuntimeException时回滚,如果业务里抛出的是受检异常,比如Exception,默认是不会回滚的。很多“下单扣了库存但订单没生成”就是因为这里没写rollbackFor。另外事务失效的大坑之一是同一个类里的方法自调用——Controller调save方法,save里this.createOrder,这时候createOrder上的@Transactional是不生效的,因为绕过了Spring代理。正确做法是把下单逻辑单独放在一个Service类里,让外部来调用。

4.4 后台订单管理与销量统计:简单SQL也能撑起看板

后台管理最核心的是订单列表和状态流转。订单状态用数字存储,页面显示的时候再映射成文字。管理员点发货就是更新status从1改成2,看板上的统计则靠聚合SQL完成。

按订单状态统计数量的SQL:

@Select("SELECT status, COUNT(*) AS count FROM orders GROUP BY status") List<OrderStatusCount> countByStatus();

这条SQL会把0到4每个状态的数量一次性查出来,后端用Map<Integer, Integer>接住,放到Model里返回给前端。维度从SQL入手比用Java循环统计高效,也比在Service里写一堆if判断干净。

按天统计最近7天销售额的SQL:

@Select("SELECT DATE_FORMAT(create_time, '%Y-%m-%d') AS day, " + "SUM(total_price) AS amount FROM orders " + "WHERE create_time >= DATE_SUB(CURDATE(), INTERVAL 6 DAY) " + "GROUP BY DATE_FORMAT(create_time, '%Y-%m-%d')") List<DailySale> selectDailySaleLast7Days();

DATE_FORMAT和DATE_SUB是MySQL的两个常用函数,前者格式化时间,后者做日期偏移。几天没订单的天会不出现在结果集里,前端图表会断,这是正常现象,做柱状图时用Java把它补0就行。

5. 避坑排查:这个系统最常见的5个翻车现场

5.1 数据库连不上:时区、驱动和连接池参数三板斧

现象:启动项目时控制台报Communications link failure,或者The server time zone value is unrecognized。

原因:90%是MySQL 8.0的时区问题。MySQL 8.0默认时区不是UTC,而驱动要求明确指定时区,不指定就报错。另外一个常见原因是用老驱动类com.mysql.jdbc.Driver连8.0数据库。

解决:URL里加serverTimezone=Asia/Shanghai,驱动类名改成com.mysql.cj.jdbc.Driver。如果还不行,检查用户名密码是不是写错,或者MySQL服务根本没启动,用mysql -uroot -p在命令行试连一次,先排除数据库自身问题再怀疑SpringBoot配置。

5.2 前端页面拿不到数据:字段名映射与JSON序列化

现象:接口返回的数据里createTime是null,或者thymeleaf页面显示不出创建时间。

原因:数据库字段create_time,Java属性是createTime,MyBatis默认不会自动映射下划线到驼峰。要么在yml里开map-underscore-to-camel-case,要么在SQL里写别名。还有一种情况是Controller方法漏加@ResponseBody,返回对象被当成视图名去找模板,页面就变成了白板。

解决:yml里加mybatis.configuration.map-underscore-to-camel-case: true。排查顺序是先看后端日志里SQL有没有查出数据,再看页面上的字段名是不是和Java属性名一致。SQL能查出数但页面是null,基本就是映射问题。

5.3 部署后白屏或404:静态资源路径与拦截器放行

现象:本地开发点得通,打过jar包部署后页面能打开但没有CSS样式,或者直接404。

原因:开发环境有IDE帮忙处理静态资源,部署后如果拦截器没放行/static/***路径,浏览器请求css和js会被拦截器拦掉跳转登录,页面就只剩裸HTML。另一个原因是打成jar包后templates页面资源路径写的是绝对路径/static/css/style.css,但项目context-path不是根路径。

解决:拦截器配置里excludePathPatterns把/css/, /js/, /images/**, /static/**全放行。页面里资源引用写相对路径或者加th:href="@{/css/style.css}",让thymeleaf自动拼上context-path。

5.4 中文乱码问题:从请求到数据库全链路排查

现象:数据库里中文变成问号,或者页面提交中文数据后存进去是??。

原因:建库时没指定utf8mb4,数据库默认是latin1;或者JDBC连接串里没写characterEncoding=utf8;还有可能是Tomcat对POST表单的解码字符集不对。

解决:三个地方一次全改。建库语句用DEFAULT CHARSET=utf8mb4;JDBC URL加characterEncoding=utf8;如果页面是form表单POST提交,在yml里配置server.servlet.encoding.force=true。改完重启后重新插入数据,之前已变成问号的旧数据只能手动修复。

5.5 事务不生效:自调用与异常被吞掉的两个典型

现象:测试下单时发现库存扣了但订单表没记录,或者订单建了但库存没扣。

原因:事务不生效最常见的原因有两个。第一,同类的某个方法里this调用另一个加@Transactional的方法,绕过Spring代理使注解失效;第二,方法内部catch了Exception后没往外抛,事务“认为”方法执行成功,于是正常提交。

解决:事务方法放在独立的Service类里,不要同类自调用。catch里如果要让事务回滚,必须重新抛出,或者用TransactionAspectSupport.currentTransactionStatus().setRollbackOnly()手动标记回滚。排查时看控制台日志里有没有TransactionInterceptor字样就知道事务有没有被代理类拦截。

6. 从跑通到能答辩、能上线:这三个进阶点让你的系统更值钱

6.1 用一个缓存注解给首页和热门商品提速

首页每次请求都查一遍数据库不划算。Spring Boot官方自带的Spring Cache搭配一个简单缓存就能解决。分类列表和商品列表这种低频变更数据,直接加@Cacheable:

@Cacheable(value = "cakes", key = "#pageNum") public PageInfo<Cake> getCakeList(int pageNum, int pageSize) { PageHelper.startPage(pageNum, pageSize); return new PageInfo<>(cakeMapper.selectAll()); }

首次调用后会缓存结果,后续请求直接走缓存。后台修改商品的时候再用@CacheEvict清掉对应缓存,避免看到旧数据。加这个注解能让你的文档和答辩多一个“缓存优化”的亮点,成本极低。

6.2 订单号的生成规则:并发下不重复的简单做法

订单号设计是答辩时容易被追问的细节。简单做法是用时间戳加随机数:yyyyMMddHHmmss加4位随机数。但并发高时理论上有碰撞风险,更稳妥的写法是结合用户ID和自增主键:

String orderNo = "CK" + System.currentTimeMillis() + String.format("%03d", userId % 1000);

这样同一个用户在同一毫秒内重复下单的概率极低。如果有更强的唯一要求,数据库层给order_no加唯一索引,插入失败时重试生成。保持一个原则:订单号不要用UUID,太长且分段无规律,不适合做表格展示,也不适合作为搜索关键词。

6.3 部署到服务器:打jar包换环境的完整配置清单

最后是部署。本地跑通不算完,能打包部署才说明环境配置真正理解了。先确认pom里打包方式是jar,然后执行:

mvn clean package -DskipTests

打完的jar包在target目录下,传到服务器后运行:

nohup java -jar cake-shop-0.0.1.jar > app.log 2>&1 &

改成生产环境时,最多改三处:数据库连接串、账号密码、日志级别。建议把application.yml外置到jar包同级目录的config文件夹下,SpringBoot会自动优先读取外部配置,这样升级jar包时不用重新改配置。上线前顺手把PageHelper的reasonable参数打开,防止用户传pageNum=999直接把数据库翻穿。这个参数在yml里配:

pagehelper: reasonable: true

我自己的习惯是,每接到一套这种管理系统,先把数据库脚本导一遍、把项目跑起来,再往回看代码结构。跑不起来就调环境,跑起来了再读逻辑。很多同学一上来就扎进代码里,最后卡在配置上大半天,顺序反了。这套蛋糕店项目虽然业务简单,但把登录、权限、事务、分页、聚合统计这几个SpringBoot核心点串明白了,后面的路会顺很多。希望帮到你。

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

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进