news 2026/11/2 5:55:47

SSM网上花店系统毕设源码深度解析:从架构分层到部署避坑指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
SSM网上花店系统毕设源码深度解析:从架构分层到部署避坑指南

简介:基于SSM框架与JSP技术的网上花店系统毕业设计源码,适合Java学习者、毕业设计学生及需要快速搭建电商项目的人员。系统分用户端与管理端,覆盖商品展示管理、购物车订单、个人中心、支付配送、促销优惠券、评价反馈、库存预警、销售统计等电商核心功能,可直接运行或二次开发。压缩包共1458个文件,约29.38MB,以JSP页面、Java类、JS脚本、CSS样式为主,另含SQL数据库脚本、说明文档和配套设计文稿,目录结构清晰便于检索。已有92人学习下载。其中包含完整项目源码及文档,前端整合了Bootstrap、Layui、ElementUI等常见UI框架,适合课程设计、毕业设计答辩;通过学习可掌握SSM整合、JSP页面交互、MySQL数据库设计及订单流程实现等关键技能,也可作为电商项目开发起步模板。

1. 为什么这套SSM花店系统值得你下载复现

很多来找我做 Java 毕业设计的人,诉求其实高度一致:要一个功能完整、能演示、能答辩,并且自己还能改得动的项目。这套基于 SSM 框架的网上花店系统正是这种典型货——Spring 管对象、SpringMVC 管请求分发、MyBatis 管数据库访问,前端用 JSP 配合 Bootstrap、Layui 做渲染。功能上把商品展示、购物车、订单流转、支付模拟、优惠券、评价反馈、库存预警和销售统计全串起来了,相当于一个迷你电商平台。对要交毕业设计的人来说,这类 Java 源码附说明文档,能直接落地演示;对想练手的人,SSM 比 SpringBoot 更能讲清楚分层是怎么回事。花一个下午把它跑通,比翻一百篇框架教程都实在。

这套系统的业务边界也卡得刚好:既有管理员端的商品上下架、库存预警、订单配送管理,又有用户端的注册登录、加购下单、优惠券使用和个人中心。数据库层面至少会拆出用户、分类、商品、购物车、订单、订单明细、优惠券、评价这几张核心表,每一张表都能对应到页面上的一个操作,讲起来不空洞。如果你正缺一个能撑场面的 Java 毕设源码,我建议你先把它下载下来,按我下面的步骤跑一遍,再决定怎么往自己的题目上靠。

2. SSM 架构怎么看:三层结构、业务模块与数据表设计

2.1 表现层与业务层数据层的分工

SSM 这套组合之所以成为 Java 毕设的常青树,核心原因是它把职责切得非常干净。Controller 层只做参数接收、调用服务和结果转发;Service 层写业务规则和事务边界;Mapper 层负责跟数据库打交道。以用户登录为例,JSP 页面把表单提交到 LoginController,Controller 调用 UserService.login(),UserService 内部调用 UserMapper.selectByUsername(),最后把结果封装成 ModelAndView 返回。你拿到源码后第一件事,不是急着改页面,而是先把这种调用链捋出来,后面改任何功能都顺手。

@Controller @RequestMapping("/user") public class UserController { @Autowired private UserService userService; @RequestMapping("/login") public String login(String username, String password, HttpSession session, Model model) { User user = userService.login(username, password); if (user != null) { session.setAttribute("loginUser", user); return "redirect:/product/list"; } model.addAttribute("msg", "用户名或密码错误"); return "user/login"; } }

这段代码要点在于 @Autowired 依赖注入:UserController 不需要自己 new UserService,Spring 容器在启动时会把实现类自动塞进来。你以后如果要把 Service 换成另一个实现,比如加一层 Redis 缓存,只要改实现类或者改注入类型,Controller 一行都不用动。另外注意登录成功用的是 redirect 而不是 forward,这样刷新页面时不会重复提交表单,是 JSP 项目里很常见的写法。

public interface UserService { User login(String username, String password); boolean register(User user); int updateInfo(User user); } @Service public class UserServiceImpl implements UserService { @Autowired private UserMapper userMapper; @Override public User login(String username, String password) { return userMapper.selectByUsernameAndPwd(username, password); } }

Service 层在这套系统里承担的是事务边界和业务规则。比如注册功能里要同时写 user 表和 user_coupon 表(新用户送一张券),那这两次插入必须在同一个事务里,否则会出现用户有了但券没发的情况。SSM 里给 Service 方法加 @Transactional 注解就能做到,这也是面试时会被追问的点——事务是加在 Controller 还是 Service?标准答案永远是 Service,因为一个 Controller 方法可能调多个 Service,事务粒度放在 Service 才最准。

Mapper 层更简单直接,一个接口配一个 XML。接口里声明方法,XML 里写 SQL,MyBatis 通过动态代理把二者绑定。注意这套系统中用户表和商品表之间的关联查询不会写在 Mapper 层以外,因为 MyBatis 的 resultMap 支持一对一、一对多映射,比如查询订单时可以顺便把订单明细列表也带出来,避免在 Java 里写双层 for 循环去拼数据。

2.2 八大业务模块与一条订单的数据流

这套花店系统的模块划分在毕设里属于比较齐的,我整理成一张表,方便你对照源码目录找代码位置。

模块核心功能涉及数据表
用户模块注册、登录、个人信息维护、收货地址user, address
商品模块分类浏览、详情查看、关键词搜索category, product
购物车模块加入购物车、修改数量、删除条目cart
订单模块提交订单、生成订单号、状态跟踪order, order_item
促销与优惠券活动发布、优惠券领取与抵扣coupon, user_coupon
评价模块对已完成订单的商品打分评论comment
库存预警低于阈值时后台提醒补货product
数据统计销量排行、销售额汇总pro/order_item 聚合查询

看完表你就能理解这系统的信息流为什么称为“闭环”:用户注册登录后浏览商品,把花加入购物车,下单时生成主订单和订单明细两条记录,支付后管理员在后台看到待发货订单,发货后用户在个人中心看到物流状态,确认收货后还能对商品评价。每一步都会改订单状态字段,所以 order 表里必然有一个 status 字段,从 0 到 4 或者用字符串枚举表示“待支付、已支付、待发货、已发货、已完成”。你想在答辩时展示业务逻辑,抓住这条链路讲就行。

以“用户下单”这个动作为例,完整的调用顺序是:购物车页面点击结算,请求跑到 OrderController.submit(),Controller 先校验用户登录状态,再调 OrderService.createOrder()。Service 里第一步查购物车商品,第二步计算总价并判断是否满足优惠券门槛,第三步扣减库存,第四步生成 order 和 order_item 记录,第五步清空购物车。这一串操作如果不用事务包裹,扣库存成功但订单生成失败,就会出现“货已经扣了但用户没下单成功”的超卖问题。所以你翻源码时看到 createOrder() 上面挂着 @Transactional,属于正常且必要的设计,不是代码冗余。

2.3 数据库表结构与关键字段分析

拿到源码后先看数据库脚本,这是最快理解系统的方式。这类 SSM 毕设一般会附一个 flower_shop.sql,里面包含建库、建表和初始化数据。核心表是下面这几张,字段命名基本是下划线风格,和 Java 实体类的驼峰命名对应。

CREATE TABLE `product` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(100) NOT NULL, `category_id` int(11) DEFAULT NULL, `price` decimal(10,2) DEFAULT NULL, `stock` int(11) DEFAULT '0', `sales` int(11) DEFAULT '0', `picture` varchar(255) DEFAULT NULL, `description` text, `status` tinyint(1) DEFAULT '1', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

product 表里有两个字段特别容易在演示时拿来当亮点:一个是 stock 库存字段,库存预警功能就是靠它实现的——后台定时或每次下单后查一把 stock 是否小于阈值,小于就标记为“需补货”;另一个是 sales 销量字段,每次创建订单成功时执行 sales = sales + 数量,销售统计页面直接 SELECT SUM(sales) FROM product 就能出榜,不需要去 order_item 里做复杂聚合。这种冗余字段设计在电商系统里很常见,牺牲一点存储换查询速度。

CREATE TABLE `order` ( `id` int(11) NOT NULL AUTO_INCREMENT, `order_no` varchar(32) NOT NULL, `user_id` int(11) DEFAULT NULL, `total_price` decimal(10,2) DEFAULT NULL, `status` tinyint(1) DEFAULT '0', `create_time` datetime DEFAULT NULL, `pay_time` datetime DEFAULT NULL, `consignee` varchar(50) DEFAULT NULL, `phone` varchar(20) DEFAULT NULL, `address` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`), UNIQUE KEY `uk_order_no` (`order_no`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

order 表的设计有两个值得在论文里写一笔的细节。第一是 order_no 订单号加了唯一索引,生成规则一般是用时间戳加随机数,避免并发下单时主键冲突;第二是收货人信息直接冗余在订单表里,而没有去关联 address 表。原因是用户下单后地址可能改了,如果关联查询历史订单就会串号,快照式存储才是电商的标准做法。你在部署时如果发现某张表字段看不懂,去对应实体类看注释基本能明白。

3. 跑通到能点:环境版本、导入步骤与数据库初始化

3.1 环境版本清单与选型理由

我帮人排错排得最多的,不是代码问题,是版本不匹配。这套 SSM + JSP 的老组合对 JDK 和 Tomcat 版本非常敏感,建议你按下面的清单装,省得踩无谓的坑。

组件推荐版本说明
JDK1.8(8u202 或更高)不要用 17/21,老项目依赖库不一定兼容
Tomcat8.5 或 9.0不要用 Tomcat 10,Jakarta 命名空间会直接炸
MySQL5.7 或 8.0两个版本对应 jdbc 驱动配置有差别
IDEA2020.3 及以上社区版即可,不需要旗舰版
Maven3.6+如果项目带 pom.xml 就用,否则忽略

为什么坚决建议 JDK 1.8?因为很多 SSM 毕设用的库还是 CGLIB 代理和旧版 javassist,在高版本 JDK 上会出现反射调用失败,报错信息里写的 “源发行版 17 需要目标发行版 17” 就是编译期版本不匹配的典型症状。Tomcat 选 8.5 而不是 10 的原因更直接:Tomcat 10 把 javax.servlet 换成了 jakarta.servlet,老代码里所有 import javax.servlet.* 全部报错,这种兼容性问题是加一行配置救不回来的。

MySQL 这边要注意驱动类名。MySQL 5.7 用 com.mysql.jdbc.Driver 没问题,但 MySQL 8.0 必须换成 com.mysql.cj.jdbc.Driver,而且 url 里要加 serverTimezone=Asia/Shanghai。我见过太多人把 5.7 时代的配置文件原封不动搬到 8.0,结果启动直接报 Public Key Retrieval is not allowed。后面第 5 章我会细讲这条,这里先记住结论。

3.2 IDEA 导入与 Tomcat 部署步骤

拿到源码包后先解压看目录结构。传统 SSM 项目通常有两种组织方式:一种是 Maven 工程,顶层有 pom.xml;另一种是普通 Web 工程,依赖 jar 包直接堆在 WEB-INF/lib 目录下。两种我都遇到过,导入方式略有不同。

# 典型目录结构(Maven 工程) flower_shop/ ├── pom.xml ├── src/main/java/com/example/controller ├── src/main/java/com/example/service ├── src/main/java/com/example/mapper ├── src/main/resources/spring ├── src/main/webapp/WEB-INF/jsp └── sql/flower_shop.sql # 典型目录结构(非 Maven 工程) flower_shop/ ├── src/com/example/controller ├── WebRoot/WEB-INF/lib/*.jar ├── WebRoot/WEB-INF/jsp └── sql/flower_shop.sql

Maven 工程的话,IDEA 里直接 File -> Open 选择 pom.xml,等待依赖下载,然后 File -> Project Structure 里确认 Artifacts 有 war exploded。非 Maven 工程更简单,Open 整个目录后,把 WebRoot 或 web 目录标记为 Web 资源目录,再把 lib 加入库即可。我个人习惯先看有没有 pom.xml,有就按 Maven 走,没有就手动配置,省得 IDEA 自作主张。

# 配置 Tomcat 的步骤(IDEA 界面操作对应关系) # 1. Run -> Edit Configurations -> "+" -> Tomcat Server -> Local # 2. Deployment 页签 -> "+" -> Artifact -> 选择 xxx:war exploded # 3. Server 页签 -> HTTP port: 8080 # 4. Application context: / (建议设为根路径)

这里 Application context 我强烈建议直接设成 /。很多毕设源码里写死了跳转路径,比如 controller 里 return "redirect:/product/list",如果 context 是 /flower_shop,那实际访问地址变成 /flower_shop/product/list,这些相对路径全要跟着改。设成根路径少动一处源码,演示时也更自然。端口如果被占用,8080 改成 8081 也行,但记得浏览器访问时带上对应端口。

3.3 数据库初始化与账号配置

数据库这步是翻车重灾区,但规则很简单:先建库,再导数据,最后改连接配置。顺序反了,连接永远报 access denied。

# 命令行导入(Windows / Mac 通用) mysql -u root -p < sql/flower_shop.sql # 或者进入 MySQL 后执行 mysql> source /path/to/flower_shop.sql;

导入完成后,打开数据库看是否有 user、product、order 等表。有些网上下载的脚本只有表结构没有初始数据,你会发现管理员账号不存在、商品列表为空,演示时尬住。这时需要手动往 user 表插入一条管理员记录,密码字段如果是 MD5 加密,就插入 MD5 后的值,比如 e10adc3949ba59abbe56e057f20f883e 对应 123456。

# src/main/resources/jdbc.properties jdbc.driver=com.mysql.cj.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/flower_shop?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai jdbc.username=root jdbc.password=你的数据库密码

jdbc.properties 是整个系统连接数据库的唯一入口。这里四个参数要跟你本机实际环境对齐:url 里的 flower_shop 是库名,如果你的脚本建的是别的库名,这里必须改;username 和 password 填你 MySQL 的账号,注意默认 root 的密码就是你安装时设的那个,别和前端页面的登录密码搞混。改完这个文件,然后启动 Tomcat,能在控制台看到 “Initializing Spring root WebApplicationContext” 说明 Spring 容器起来了,接着访问 http://localhost:8080 就能看到登录页。

4. 读懂 SSM 整合的三个配置:Spring、SpringMVC 与 MyBatis

4.1 applicationContext.xml:数据源、包扫描与事务

SSM 项目里会有多个 Spring 配置文件,最核心的是 applicationContext.xml,它负责容器启动、数据源、包扫描和事务管理。这个文件你部署时一般不用改,但答辩时老师十有八九会指着它问“Spring 和 SpringMVC 分别扫描哪些包”,所以必须读懂。

<!-- applicationContext.xml 关键片段 --> <context:component-scan base-package="com.flower.shop"> <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/> <context:exclude-filter type="annotation" expression="org.springframework.web.bind.annotation.ControllerAdvice"/> </context:component-scan> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> <property name="initialSize" value="5"/> <property name="maxActive" value="20"/> </bean> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource"/> <property name="mapperLocations" value="classpath:mapper/*.xml"/> <property name="configLocation" value="classpath:mybatis-config.xml"/> </bean> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="com.flower.shop.mapper"/> </bean> <tx:annotation-driven transaction-manager="transactionManager"/>

component-scan 里的 exclude-filter 是很多新手看不懂的地方。根容器只扫描 @Service、@Repository 这些注解,把 @Controller 排除掉;@Controller 单独交给 spring-mvc.xml 里的子容器扫描。这样做的原因涉及 Spring 容器双亲机制——父子容器如果不做拆分,Controller 里注入 Service 会发生重复代理或者事务不生效。你可以不深究原理,但删掉 exclude-filter 一定会出问题,这是血泪经验。

数据源我推荐用 Druid 而不是 DBCP,因为 Druid 自带连接池监控,访问 /druid 能看到活跃连接数,演示时给老师看效果很加分。initialSize 是启动时预创建的连接数,maxActive 是最大连接数。毕设项目并发量不大,initialSize=5 足够,改太大会拖慢启动速度。

SqlSessionFactoryBean 和 MapperScannerConfigurer 是 MyBatis 接入 Spring 的两个胶水。前者把 mapper 目录下所有 XML 注册进 SqlSessionFactory,后者扫描 com.flower.shop.mapper 包,把 Mapper 接口自动代理成 Spring Bean。注意 mapperLocations 的路径要和你实际放 XML 的目录一致,路径写错启动时会报 Invalid bound statement (not found)。

4.2 spring-mvc.xml:请求映射、视图解析与静态资源放行

spring-mvc.xml 比 applicationContext.xml 小得多,但它决定了页面能不能正常显示、样式会不会丢。这个文件里最要命的配置是视图解析器和静态资源处理。

<!-- spring-mvc.xml 关键片段 --> <mvc:annotation-driven/> <mvc:default-servlet-handler/> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/jsp/"/> <property name="suffix" value=".jsp"/> </bean> <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <property name="maxUploadSize" value="5242880"/> </bean>

InternalResourceViewResolver 里 prefix 和 suffix 的组合意味着 Controller 里 return "product/list",实际加载的是 /WEB-INF/jsp/product/list.jsp。WEB-INF 目录下的 JSP 不能通过浏览器直接输入地址访问,只能由 Controller 转发跳转,这样设计是为了安全——用户没法绕过登录直接访问后台页面。你如果想把 JSP 放到根目录下,就必须改 prefix,但我不建议这么干。

mvc:default-servlet-handler/ 这行看起来不起眼,实际上几乎所有样式丢失问题都是少了它。因为 DispatcherServlet 的 url-pattern 配的是 /,所有请求都会先经过它,如果不放行静态资源,Bootstrap、Layui 的 CSS 和 JS 全部会被当成 Controller 请求处理,结果就是 404。加了这行后,Tomcat 的默认 Servlet 会接管没有映射的静态资源请求,/css/style.css 才能正常加载。

multipartResolver 是文件上传用的,前缀 CommonsMultipartResolver 意味着项目里用到了 commons-fileupload 这个库。maxUploadSize 是单文件大小上限,5MB 对应 5242880 字节。这套花店系统的管理员上传商品图片走的就是它,如果你传大图报 MaxUploadSizeExceededException,把数字调大即可。

4.3 ProductMapper.xml:resultMap 与动态 SQL

MyBatis 的 XML 是 SQL 和 Java 方法之间的桥梁。我看过太多人在这个文件的 resultMap 上翻车:要么字段名对不上导致查出来全是 null,要么动态 SQL 没写 where 条件导致全表更新。下面这段是商品管理模块里最常见的分页加条件查询。

<!-- ProductMapper.xml 关键片段 --> <resultMap id="ProductMap" type="com.flower.shop.entity.Product"> <id column="id" property="id"/> <result column="category_id" property="categoryId"/> <result column="name" property="name"/> <result column="price" property="price"/> <result column="stock" property="stock"/> <result column="sales" property="sales"/> <result column="picture" property="picture"/> <result column="description" property="description"/> </resultMap> <select id="selectByCondition" parameterType="map" resultMap="ProductMap"> SELECT * FROM product <where> <if test="name != null and name != ''"> AND name LIKE CONCAT('%', #{name}, '%') </if> <if test="categoryId != null"> AND category_id = #{categoryId} </if> </where> ORDER BY id DESC LIMIT #{offset}, #{limit} </select>

resultMap 的 role 是把数据库下划线字段映射到 Java 驼峰属性。如果这里漏掉 category_id 到 categoryId 的映射,查询结果里这个字段就是 null。你可能会问:既然字段命名规则统一,能不能让 MyBatis 自动映射?可以,在 mybatis-config.xml 里设置 mapUnderscoreToCamelCase=true 就能省掉大部分 resultMap 手工配置。但注意 resultType 自动映射时,嵌套对象还是处理不了,一对多还是要手写 resultMap。

动态 SQL 里的 标签是我最推荐新手照抄的写法。它有两个好处:第一,如果所有 if 都不成立,where 关键字不会出现,SQL 变成 SELECT * FROM product ORDER BY id DESC,语法正确;第二,如果第一个 if 成立,它会自动去掉开头的 AND,不用你自己在条件前面拼 WHERE 1=1。你看到的很多老项目里写 WHERE 1=1,那是在 MyBatis 动态 SQL 流行之前的土办法,现在不应该再这么写。

LIMIT #{offset}, #{limit} 是手写分页,参数从 Service 层传进来。offset 是跳过多少条,limit 是每页多少条。这套系统如果还用了 PageHelper 插件,分页会变成在 Mapper 方法前后自动拦截改写 SQL,但原理都一样:最终数据库执行的是 LIMIT 语句。你改分页时只需要改 mapper 接口传参,不用动 XML。

5. 避坑指南:运行这套毕设最容易翻车的六个位置

5.1 依赖缺失、版本冲突与启动崩溃

现象:Tomcat 启动到一半直接报 ClassNotFoundException,最常见的是 org.springframework.web.context.ContextLoaderListener,或者 java.lang.NoClassDefFoundError。

原因:Spring 的 jar 包没有进到 WEB-INF/lib。IDEA 里如果你用的是 Maven 工程但依赖 scope 写成了 provided,或者导入的是非 Maven 工程但没把 lib 目录加入 Artifact,部署时这些 jar 不会复制到 Tomcat。

解决:Maven 工程在 Project Structure -> Artifacts 里确认 lib 目录被包含;非 Maven 工程去检查 web/WEB-INF/lib 是否存在完整的 jar 包。最简单的方式是重新点一下 Build -> Rebuild Project,然后看生成的 target 目录里有没有对应 jar。我一般还会打开 Tomcat 的发布目录看一眼,确认发布出去的不是空壳。

5.2 MySQL 8.0 连接报 Public Key Retrieval

现象:项目启动时控制台抛出 “Public Key Retrieval is not allowed”,数据库连接不上。

原因:MySQL 8.0 默认的用户认证插件是 caching_sha2_password,客户端第一次连接时需要向服务器请求公钥加密密码,但 JDBC 驱动默认关闭了这个行为。

解决:在 jdbc.properties 的 url 后面追加 allowPublicKeyRetrieval=true 和 useSSL=false,再把驱动类名改成 com.mysql.cj.jdbc.Driver。这两个参数一个都不能少,如果装了 MySQL 8.0 就把这段直接复制进去,这是最省事的办法。

5.3 页面中文乱码与数据库中文变问号

现象:浏览器里页面中文显示成一堆乱码,或者从后台添加的商品名称存进数据库后变成 ???。

原因:三层编码不一致。JSP 页面用了 GBK,数据库建表用了 utf8,Tomcat 连接器默认 ISO-8859-1,两个环节不一致就会乱。

解决:把三处统一成 UTF-8。第一处,JSP 第一行确认 contentType="text/html; charset=UTF-8";第二处,数据库建库语句统一用 utf8mb4,ALTER DATABASE flower_shop DEFAULT CHARACTER SET utf8mb4;第三处,给表单提交加 CharacterEncodingFilter,放到 web.xml 过滤器链的最前面。这三步做完还乱码,就查一下 MySQL 连接 url 里有没有 characterEncoding=utf8。

5.4 页面样式全丢静态资源 404

现象:页面能打开,但所有 CSS、JS、图片全部加载失败,浏览器控制台一堆 404,页面像回到十年前。

原因:DispatcherServlet 的 url-pattern 配置成了 /,把所有请求都拦了,静态资源没有放行。这是 SSM 项目最常见的低级事故。

解决:在 spring-mvc.xml 里加 mvc:default-servlet-handler/ ,或者用 <mvc:resources mapping="/static/**" location="/static/"/> 明确指定静态资源目录。加完之后清理浏览器缓存重新访问,样式就回来了。以后每部署一套新项目,我都习惯先看控制台有没有静态资源 404,再点页面。

5.5 JSP 页面 EL 表达式原样输出

现象:页面上直接显示 ${product.name} 这种字符串,不显示实际值。

原因:web.xml 里的 Servlet 版本声明太低,或者 JSP 页面里被设置了 isELIgnored="true"。Tomcat 高版本(9.x)对应隐式 EL 开关是默认开启的,但有些老模板会显式关闭。

解决:在 JSP 页面顶部加 <%@ page isELIgnored="false" %>,同时检查 web.xml 的 web-app 头版本,建议用 4.0 或至少 3.1。改完重新部署,强制刷新页面。如果还不生效,看一下是不是用了 JSTL 但没引入 jstl.jar,这也会导致标签库里的变量取不到值。

5.6 实体类字段映射不上导致查询结果为空

现象:登录后商品列表能显示但价格、库存全是 null,或者后台编辑商品时部分字段丢失。

原因:数据库字段是下划线命名(如 create_time),Java 属性是驼峰命名(如 createTime),要么 MyBatis 没开驼峰映射,要么 resultMap 漏写字段。另一个隐藏原因是表别名和 resultMap 里的 column 不一致。

解决:最保险的是在 mybatis-config.xml 里加 ,这能覆盖绝大多数自动映射场景。如果有嵌套对象和一对多关系,再针对性地补 resultMap。查不到字段时先写段测试 SQL 在数据库客户端跑一遍,看是不是 SQL 本身别名没对上,别一上来就改 Java 代码。

6. 把花店系统改造成自己的毕设:加一个销售趋势图并验证边界

6.1 给后台统计页加一个销售趋势折线图

先别急着把整个系统推倒重写,挑一个小口子做增量开发,既能体现工作量,又不容易翻车。我建议你在现有统计模块上加一个“近 7 天销售趋势”接口,用 ECharts 画折线图。这个功能老师一看就懂,且能展示前端图表能力。

// OrderMapper.java 新增方法 List<Map<String, Object>> selectSalesTrend(@Param("days") int days);
<!-- OrderMapper.xml 新增 SQL --> <select id="selectSalesTrend" resultType="map"> SELECT DATE_FORMAT(create_time, '%Y-%m-%d') AS date, COUNT(*) AS order_count, SUM(total_price) AS total_amount FROM `order` WHERE create_time >= DATE_SUB(CURDATE(), INTERVAL #{days} DAY) AND status >= 1 GROUP BY DATE_FORMAT(create_time, '%Y-%m-%d') ORDER BY date </select>

Service 层调 Mapper 后把 List 直接传给 Controller,Controller 在 JSP 里把它转成 JSON 字符串塞给 ECharts。这个改造的关键是 SQL 里的 DATE_FORMAT 和 INTERVAL,前者把时间按天截断分组,后者做日期偏移。你如果想把周期改成 30 天,只需把调用处的参数从 7 改成 30,SQL 不用动。图表加载用最简单的 line 系列即可,配置项里把 xAxis 设为日期数组,series 里放 order_count 或 total_amount 都行。

6.2 三个必做的边界验证

改完功能别急着截图交差,先把边界情况测一遍。第一是库存为零时商品能不能正常下单——正确行为是提示“库存不足”且不让提交订单,而不是继续下单然后库存变负数。第二是优惠券门槛有没有生效——设置一张满 100 减 20 的券,分别用 99 元和 100.50 元的订单测,只有满足门槛才抵扣。第三是订单状态流转是否完整——从待支付到已支付,从已发货到已完成,每一步操作后刷新页面看状态是否同步更新。这三个用例跑完,你的系统在答辩演示时基本不会被问住。

前两条属于业务逻辑测试,第三条是状态机一致性验证。我在改造这套花店系统时,把这三个用例写成了一个简单的冒烟脚本,每次改完代码就自动跑一遍,比手动点页面快得多。从那以后,我每拿到一套新的毕设源码,都强制先跑一遍冒烟用例再改业务——少熬夜,少在答辩前夜翻车。希望这套花店系统的源码也能帮你少踩几个坑。

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/11/2 5:55:46

D4147 GFI控制器设计:漏电检测、零地故障与抗误动作

1. 先搞清楚这颗芯片到底在防什么接地故障断路器控制器&#xff0c;圈内更多叫它 GFI 控制器、GFCI 控制芯片&#xff0c;或者通俗点就是"漏电保护的大脑"。手上这颗 D4147 定位很清楚&#xff1a;低功率、专门用来检测危险的到接地电流路径&#xff0c;同时还要能识…

作者头像 李华
网站建设 2026/11/2 5:55:09

一站式语音工作站VoiceStudio:AI配音与声音克隆实战指南

1. VoiceStudio 整体设计与定位拆解做语音内容创作这行快十年了&#xff0c;从最早的录音笔加电脑端Audacity&#xff0c;到后来扛着声卡麦克风到处跑&#xff0c;再到这两年AI配音工具爆发&#xff0c;说实话工具换了一茬又一茬&#xff0c;但真正让我觉得“可以稳定出活”的一…

作者头像 李华
网站建设 2026/11/2 5:54:46

Angular单页应用SEO优化实战指南

1. 单页应用SEO的挑战与机遇单页应用&#xff08;SPA&#xff09;在现代Web开发中越来越流行&#xff0c;Angular作为主流SPA框架之一&#xff0c;其SEO问题一直是开发者关注的焦点。传统多页应用&#xff08;MPA&#xff09;的SEO处理相对简单&#xff0c;而SPA由于内容动态加…

作者头像 李华
网站建设 2026/11/2 5:54:43

LLM爬虫五类项目怎么选:架构差异、成本控制与避坑指南

做采集这行久了会发现一个怪现象&#xff1a;写请求、处理编码、管理请求头这些活儿&#xff0c;早就有成熟轮子&#xff0c;一两个小时就能搭起来&#xff1b;真正把项目拖垮的&#xff0c;是后面那件事——目标站点改了版&#xff0c;选择器全废&#xff0c;几万条任务跑出来…

作者头像 李华
网站建设 2026/11/2 5:53:52

从录音到发布:搭建高效语音内容生产工作流

1. 语音创作这件事&#xff0c;值得一套自己的“工坊”先说个真实感受&#xff1a;这两年是语音内容最火热的时候&#xff0c;播客、有声书、短视频口播、课程配音、企业宣传片旁白&#xff0c;全都在抢优质的声音内容。我自己从几年前开始做播客&#xff0c;中间帮团队搭过配音…

作者头像 李华
网站建设 2026/11/2 5:52:19

MyBatis行级数据权限实战:拦截器与SQL改写方案

做过后台管理系统的同行大多碰过这类需求&#xff1a;销售只能看自己跟进的客户&#xff0c;部门主管能看整个部门的&#xff0c;区域负责人能看下面所有团队的。第一次接到这种需求&#xff0c;很多人的做法是在每个 Mapper 的 SQL 后面手写and create_by #{userId}&#xff…

作者头像 李华