news 2026/10/7 23:24:49

Java Web超市管理系统:Servlet+JSP+MySQL三层架构课设全解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Java Web超市管理系统:Servlet+JSP+MySQL三层架构课设全解析

简介:基于Java Web的超市管理系统是一份面向计算机相关专业在校学生、教师及企业开发者的数据库课程设计完整资料包,尤其适合用作课程设计、毕业设计或项目初期立项演示,也适合零基础学生作为入门进阶的学习项目。资源以实际超市管理业务为核心场景,覆盖商品、供应商、进货、销售与库存等主要功能模块,能够完整展示Java Web应用与数据库设计的开发流程。压缩包内共125个文件,整体大小约3.05MB,其中Java源码与JSP页面承载核心业务逻辑,JavaScript与CSS负责前端交互与样式,XML与properties等包含配置信息,另有项目说明文档与相关资源,文件结构清晰,便于按需对照学习。目前已有40人学习下载。项目源码经运行测试稳定,附带详细文档和答辩材料,曾在评审中获得95分,可直接用于课程设计、毕业设计或其他作业提交,也能在现有基础上扩展功能,是高性价比的参考项目。

1. Java Web 超市管理系统:从课设选题到数据库高分的完整落地资料

每年数据库课程设计答辩季,最难受的不是代码跑不起来,而是代码能跑却说不清“这张表为什么这么建、这个请求到底经过了哪些类”。这份基于 Java Web 的超市管理系统,是典型的 JSP + Servlet + MySQL 三层架构项目,压缩包里源码、数据库脚本、详细文档都齐,覆盖登录会话、供应商管理、商品信息维护这些数据库课设高频考点。它解决的问题很具体:让答辩者从页面、控制层、业务层到数据层都能讲清楚。适合计算机相关专业学生拿来做课设基线或毕设演示原型,也适合基础一般但想补 Java 基础的同学照着改。拿到手先按第 3 章建库、第 4 章对代码,半天时间就能摸透整套运行链路。

2. 项目底层的技术骨架:Servlet + JSP + MySQL 的请求链路与模块对应关系

2.1 为什么数据库课设反而应该选 Servlet 而不是 Spring Boot

现在网上一搜 Java web 课设,跳出来的全是 Spring Boot 全家桶。但数据库课程设计这类课的评分标准,往往把“是否理解 Servlet 生命周期”“请求参数怎么封装”“JDBC 事务边界怎么控制”列在细目里。评审老师更愿意听一个能把 HttpServletRequest 讲明白的学生,而不是背了一堆注解、问两句就卡壳的。这个项目的控制层就是最朴素的 Servlet:每个功能模块一个 Servlet,doGet/doPost 里按 action 参数分发,业务层把表单数据封装成实体对象,数据层用 JDBC 操作 MySQL。这种写法跟 Java 基础课、数据库原理课的知识点一一对应,临时改也快。

运行环境上,常见组合是 Tomcat 9 + MySQL 5.7 或 8.0 + JDK 8,代码兼容 javax.servlet 命名空间,导入 IDEA 或 Eclipse 后配置一个 Tomcat 就能跑。如果你电脑上装的是 Tomcat 10,先别急着导,第 5 章有专门的说明。这里多说一句:选环境时越接近教材越好,课设不是生产环境,稳定大于新潮。

2.2 一次登录请求的完整流转:表单、Servlet、Service、DAO 各层干了什么

以登录为例。用户在 login.jsp 输入账号和密码,点提交后表单 POST 到配置好的 LoginServlet。Servlet 第一件事是设置请求编码,然后从 request 里取 username 和 password 两个参数,封装后交给 UserService 做校验。Service 层不直接写 SQL,它管业务规则,比如“两个参数不能为空”“密码校验不通过时返回什么提示”;DAO 层才真正拿 Connection 执行查询。结果逐层返回后,Servlet 把 User 对象放进 session,用 sendRedirect 跳到 main.jsp;校验失败则把错误信息塞进 request,forward 回 login.jsp 显示。

这条链路在资源包里逐层有对应:JSP 在 webapp 或 WebContent 目录,Servlet 是一堆 *Servlet 类,业务层是接口加 Impl 实现,DAO 集中在 dao 包。压缩包里的 ProviderServlet.class 和 ProviderServiceImpl.class 就是这条链路上供应商模块的控制器和业务实现。答辩时候老师问“登录这行代码走通了哪些类”,按这个顺序讲,基本不会翻车。

<servlet> <servlet-name>LoginServlet</servlet-name> <servlet-class>com.supermarket.controller.LoginServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>LoginServlet</servlet-name> <url-pattern>/login</url-pattern> </servlet-mapping>

映射的规则是:浏览器访问 /login 这个 URL 时,请求交给 com.supermarket.controller.LoginServlet 处理。如果资料里用的是 @WebServlet("/login") 注解,效果一样,只是省了 web.xml 这两段配置。要注意 url-pattern 必须以斜杠开头,同一路径不要在注解和 web.xml 里同时配,否则容器启动时会出现两个 Servlet 实例的冲突。

2.3 压缩包里的资源文件与模块功能对应表:先认文件再动手

压缩包拿到手,第一步不是导入 IDE,而是先认文件。以这份资源为例,我按控制层、业务层、静态资源三类整理过一张对应表:

文件(片段)分类对应职责
ProviderServlet.class控制层供应商模块请求入口,负责 list / add / update / delete 动作分发
ProviderServiceImpl.class业务层供应商增删改查的业务封装,事务边界从这里控制
style.css、public.css静态资源页面公共样式,登录页与列表页共用
datepicker.css、WdatePicker.css静态资源日期选择控件样式,销售单、进货单的时间字段用

静态资源的三个 css 文件要放在一起解释:style.css 和 public.css 是公共样式;datepicker.css 和 WdatePicker.css 是日期选择控件 My97DatePicker 的样式文件。看到它们基本能判断系统里有带时间筛选的页面,比如销售单或进货单。这类控件文件不能单独拎出来,必须和 js 脚本放在同一目录结构下,否则就会出第 5 章里说的日期控件点不开的问题。

WebContent/ ├── css/ # style.css、public.css 公共样式 ├── js/ # 页面脚本与工具库 ├── My97DatePicker/ # datepicker.css、WdatePicker.css 及 js 控件资源 ├── login.jsp ├── main.jsp ├── provider_list.jsp └── WEB-INF/ ├── web.xml └── lib/ # mysql-connector-java-*.jar src/ └── com/supermarket/ ├── controller/ # LoginServlet、ProviderServlet 等控制器 ├── service/ # ProviderService 接口与 ProviderServiceImpl └── dao/ # 数据库访问类

对照这个结构检查资源包,能快速发现缺什么:只有 .class 没有 .java,说明源码被编译过一版,改业务逻辑会很痛苦;没有 lib 目录说明数据库驱动要自己补。资源包描述里写了“全部资料+详细文档”,导入前确认这几个关键文件在,基本上不会踩大坑。

3. 数据库设计是答辩重头戏:四张核心表的 DDL 与表关系

3.1 为什么拆成四张表而非一张大表:范式与数据流向

超市管理系统如果只做增删改查,很多人会图省事把用户、供应商、商品全塞进一张表。这样的表第三范式肯定过不了,写起来简单,答辩时老师一句“这表怎么拆”就能问住。合理的划分是四张核心表:t_user 存登录账号和操作员,t_provider 存供应商档案,t_goods 存商品和库存,t_sale_order 存销售单。商品通过 provider_id 关联供应商,销售单通过 user_id 关联操作员。

表名主键外键说明
t_userid无登录用户
t_providerid无供应商档案
t_goodsidprovider_id → t_provider.id商品信息与库存
t_sale_orderiduser_id → t_user.id销售订单

从数据流向上看,这四张表天然形成两条线:一条是“供应商 → 商品”的供货关系,一条是“操作员 → 销售单”的业务流水。写报表或统计时,join 这两条链就能出数。从课设评分角度,“表拆得合理”是加分项,它比页面好看更能说明数据库功底。

3.2 建表 SQL 与字段含义:从 t_user 到 t_sale_order 的完整 DDL

CREATE DATABASE IF NOT EXISTS supermarket DEFAULT CHARSET utf8mb4; USE supermarket; CREATE TABLE t_user ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE COMMENT '登录名', password VARCHAR(64) NOT NULL COMMENT '密码,建议存MD5摘要,不要存明文', real_name VARCHAR(50) COMMENT '姓名', role VARCHAR(20) DEFAULT 'staff' COMMENT '角色:admin/staff' ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE t_provider ( id INT PRIMARY KEY AUTO_INCREMENT, provider_code VARCHAR(20) NOT NULL COMMENT '供应商编号,如 GYS001', provider_name VARCHAR(100) NOT NULL COMMENT '供应商名称', contact VARCHAR(30) COMMENT '联系人', phone VARCHAR(20) COMMENT '联系电话', address VARCHAR(200) COMMENT '地址', create_date DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '建档时间' ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE t_goods ( id INT PRIMARY KEY AUTO_INCREMENT, goods_code VARCHAR(20) NOT NULL COMMENT '商品编码', goods_name VARCHAR(100) NOT NULL COMMENT '商品名称', provider_id INT COMMENT '供应商外键', spec VARCHAR(50) COMMENT '规格,如 500ml/瓶', unit VARCHAR(10) COMMENT '单位', price DECIMAL(10,2) COMMENT '销售单价', stock INT DEFAULT 0 COMMENT '当前库存', stock_min INT DEFAULT 10 COMMENT '库存预警下限', CONSTRAINT fk_goods_provider FOREIGN KEY (provider_id) REFERENCES t_provider(id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE t_sale_order ( id INT PRIMARY KEY AUTO_INCREMENT, order_no VARCHAR(30) NOT NULL UNIQUE COMMENT '销售单号', user_id INT COMMENT '操作员', total_amount DECIMAL(10,2) COMMENT '订单总金额', sale_date DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '下单时间', CONSTRAINT fk_order_user FOREIGN KEY (user_id) REFERENCES t_user(id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

几个字段的设计细节值得多说。金额字段用 DECIMAL(10,2) 而不是 FLOAT,因为浮点类型在累加和比较时会有精度误差,超市这种对钱敏感的场景用 DECIMAL 是标准做法。t_goods 里的 stock_min 字段是库存预警下限,第 6 章的扩展功能就是基于它做的,现在先留好。外键约束保证不会出现“商品挂在不存在供应商下”的脏数据,引擎统一 InnoDB 才能让外键真正生效。

参数说明:DECIMAL(10,2) 的 10 是总位数,2 是小数位,最大能存 99999999.99,如果预期单笔金额很大,把 10 调成 12;外键字段 provider_id 和主键 id 的类型、长度必须一致,否则建表时直接报错;create_date 用 DATETIME 而不是 TIMESTAMP,是因为课设要演示的“某段时间内的销售单”更依赖 DATETIME 的显示习惯。

3.3 初始化测试数据与两个常见误区

INSERT INTO t_user (username, password, real_name, role) VALUES ('admin', MD5('123456'), '系统管理员', 'admin'); INSERT INTO t_provider (provider_code, provider_name, contact, phone, address) VALUES ('GYS001', '上海某食品贸易公司', '王经理', '13800000000', '上海市浦东新区'); INSERT INTO t_goods (goods_code, goods_name, provider_id, spec, unit, price, stock, stock_min) VALUES ('SP0001', '可乐500ml', 1, '500ml/瓶', '瓶', 3.50, 120, 20);

第一条插入语句里有 MD5('123456')。用 MySQL 的 MD5 函数在课设场景里足够,真项目里要换成 bcrypt 加盐,这点心里有数就行。第二条插入把一个供应商和商品搭上关系,后续测试登录、列表、删除时都有现成数据可用。

初始化数据有两个常见误区。一个是密码明文存储,登录 SQL 直接写成 SELECT * FROM t_user WHERE username='传值' AND password='传值',这种写法在答辩时被问数据库安全会很难圆场,至少把存储改成 MD5 摘要。另一个是测试数据太“干净”,全是张三李四,缺少能触发边界条件的数据,比如库存为 0 的商品、合作方已停用的供应商,这类数据反而最能帮你验证代码的边界判断。

4. 把核心功能跑起来:登录会话、供应商分页查询与增删改的代码实现

4.1 登录与 Session 保持:doPost 里的关键分支

@Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding("UTF-8"); String username = request.getParameter("username"); String password = request.getParameter("password"); if (username == null || username.trim().isEmpty() || password == null || password.trim().isEmpty()) { request.setAttribute("errorMsg", "用户名和密码不能为空"); request.getRequestDispatcher("/login.jsp").forward(request, response); return; } UserService userService = new UserServiceImpl(); User user = userService.login(username, password); if (user != null) { HttpSession session = request.getSession(); session.setAttribute("loginUser", user); response.sendRedirect(request.getContextPath() + "/main.jsp"); } else { request.setAttribute("errorMsg", "用户名或密码错误"); request.getRequestDispatcher("/login.jsp").forward(request, response); } }

这段代码有两个容易被答辩老师抓住的点。第一是登录成功后用的是 sendRedirect 而不是 forward,因为 forward 是服务端跳转,刷新页面会重复提交表单,重定向相当于让浏览器重新请求一次 main.jsp,天然避免这个问题。第二是错误提示只写“用户名或密码错误”,不告诉用户具体哪个错,这是 web 项目登录模块的基本安全习惯,防试探。

setCharacterEncoding("UTF-8") 必须放在读取参数的代码之前,放后面就是无效操作,这是 Java Web 里最容易踩的顺序坑。UserService.login 返回 null 就代表校验失败,Service 内部是这么处理密码的:

public User login(String username, String password) { String encrypted = MD5Util.md5(password); String sql = "SELECT * FROM t_user WHERE username = ? AND password = ?"; // 使用 PreparedStatement 执行,参数依次是 username、encrypted }

注意这里的 MD5 是先在 Java 里算好摘要再传进 SQL,而不是在 SQL 里调用 MD5() 函数。两种写法结果一样,但前者的逻辑更清晰:加密属于业务层职责,数据库只负责存储和匹配。如果下载的项目里看到的是后者,跑起来没问题,答辩时能解释清楚原理就不虚。

4.2 供应商列表:action 分发、分页参数与模糊查询

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String action = request.getParameter("action"); if (action == null || "list".equals(action)) { list(request, response); } else if ("toEdit".equals(action)) { toEdit(request, response); } else if ("delete".equals(action)) { delete(request, response); } } private void list(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { int pageNum = 1; int pageSize = 10; String pageNumStr = request.getParameter("pageNum"); if (pageNumStr != null && !pageNumStr.trim().isEmpty()) { pageNum = Integer.parseInt(pageNumStr); if (pageNum < 1) pageNum = 1; } String providerName = request.getParameter("providerName"); ProviderService providerService = new ProviderServiceImpl(); PageResult<Provider> pageResult = providerService.queryByPage(providerName, pageNum, pageSize); request.setAttribute("pageResult", pageResult); request.setAttribute("providerName", providerName == null ? "" : providerName); request.getRequestDispatcher("/provider_list.jsp").forward(request, response); }

action 参数是这个 Servlet 的分发核心。list、toEdit、delete、save 分别对应列表、进编辑页、删除、保存四类操作;URL 上没有 action 时默认走列表,保证直接访问 /provider 不会白屏。pageNum 的解析做了防御处理:没传、传空、传小于 1 的值都回退到 1,这是分页参数最常见的坑——前端某次没把页码带上来,后端直接 Integer.parseInt 抛 NumberFormatException。

providerName 模糊查询的关键在后端 SQL:

public List<Provider> queryByPage(String name, int pageNum, int pageSize) { StringBuilder sql = new StringBuilder( "SELECT * FROM t_provider WHERE 1=1"); List<Object> params = new ArrayList<>(); if (name != null && !name.trim().isEmpty()) { sql.append(" AND provider_name LIKE ?"); params.add("%" + name.trim() + "%"); } sql.append(" LIMIT ?, ?"); params.add((pageNum - 1) * pageSize); params.add(pageSize); // 执行查询,组装 Provider 实体列表返回 }

LIMIT 的起点是 (pageNum-1)*pageSize,比如第 3 页每页 10 条,就从第 20 条开始取。这个计算用整数做,别用字符串拼接。LIKE 的模糊词也是通过 ? 参数传入,不要拼成 "LIKE '%"+name+"%'",否则又是 SQL 注入窗口。这两点属于“数据库增删改查”的基本要求,也是 Java 基础面试里常考的点。

列表页的翻页组件是按这组参数设计的:pageNum 用来高亮当前页,providerName 在翻页时与表单联动。刚才 Servlet 里把 providerName 再存回 request,就是为了让第二页开始搜索条件不丢,这个细节很多课设项目都忘了。

4.3 新增、修改与删除:参数校验和外键引用检查

private void save(Provider provider) { String sql = "INSERT INTO t_provider (provider_code, provider_name, contact, phone, address, create_date) " + "VALUES (?, ?, ?, ?, ?, NOW())"; // 使用 PreparedStatement 按顺序绑定参数后执行 }

新增和修改共用同一套校验逻辑:provider_code 必填且不能重复,phone 可空但要校验长度,所有字符串先 trim 再入库。校验放在 Servlet 里用 if 判断即可,课设场景不需要引入校验框架。修改时的 SQL 用 UPDATE,注意 where 条件必须带上 id,否则会把整表都改掉,这是新手写 update 最常见的翻车点。

删除前先做外键引用检查:

public boolean isProviderReferenced(int providerId) { String sql = "SELECT COUNT(*) FROM t_goods WHERE provider_id = ?"; // 查出的数量 > 0 时返回 true,表示该供应商下还有商品在售,不能直接删 }

删除前先查 t_goods 是否有外键引用,比直接执行 DELETE 抛出外键约束异常要友好得多。有的课设项目把删除写成孤立 DELETE,一旦商品表里还有该供应商的记录,页面直接报 500,答辩演示时当场翻车。这个检查逻辑也是给老师看的:你考虑了数据完整性而不只是把功能跑通。

5. 部署与运行排查:数据库时区、中文乱码、日期控件失效的踩坑记录

5.1 MySQL 8 驱动加载失败与时区报错

现象:Tomcat 启动正常,登录页也能打开,但输入账号密码后抛 java.lang.ClassNotFoundException: com.mysql.jdbc.Driver,或者报 The server time zone value 'Öйú±ê׼ʱ¼ä' is unrecognized 这种看起来像乱码的时区错误。

原因:mysql-connector-java 的 5.x 和 8.x 驱动类名不一样。5.x 是 com.mysql.jdbc.Driver,8.x 改成了 com.mysql.cj.jdbc.Driver;同时 8.x 的 JDBC 连接 URL 必须显式声明 serverTimezone,否则它去解析操作系统时区时碰到中文描述就抛异常。这个“乱码”其实是时区名编码问题,不是数据库数据乱码。

解决:先看 WEB-INF/lib 里的 jar 是哪个版本,再决定连接串。8.x 的标准写法是:

jdbc:mysql://localhost:3306/supermarket?useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=utf8

5.x 的写法保持 com.mysql.jdbc.Driver,URL 里不写 serverTimezone 也能跑。排查顺序永远是:先看 jar 版本,再改驱动类名和连接串,两条都对了,数据库连接基本不会再有幺蛾子。

5.2 中文乱码:页面、请求、数据库三级编码

现象:新增一条供应商记录,列表页显示“???上海??”这种问号串,保存到数据库里的中文也变成问号;另一种情况是登录成功后整个页面中文乱码。

原因:中文乱码是典型的链路问题,四个环节只要有任何一个没统一就会花式出乱码:JSP 页面编码、请求参数编码、数据库表字符集、JDBC 连接串的 characterEncoding。最常见的组合是 JSP 用了默认的 ISO-8859-1 提交表单,Servlet 又没调用 setCharacterEncoding,数据一到数据库就全乱了。

解决:按三级统一。页面这级,JSP 头部写 <%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>;请求这级,Servlet 的 doPost 第一行调用 request.setCharacterEncoding("UTF-8"),Tomcat 8 以前的 doGet 中文参数需要在 server.xml 配 URIEncoding;存储这级,建表用 utf8mb4,连接串加 characterEncoding=utf8。我处理过的大部分乱码项目,按这个顺序查一遍都能收干净。

5.3 日期控件点不开:My97DatePicker 的资源路径问题

现象:页面上其他组件都正常,只有日期输入框点了没反应,浏览器控制台报 WdatePicker.js 404,或者日期控件的样式明显缺失——输入框还在,但弹不出日历面板。

原因:My97DatePicker 这个控件是按当前页面相对路径找资源的。JSP 在子目录下比如 /sale/sale_add.jsp 时,如果引入写的是相对路径 scripts/WdatePicker.js,浏览器会在 /sale/scripts/ 找,自然 404。另一个容易被忽略的是登录过滤器把静态资源也拦了,即使会话没过期,静态文件也会被拦截导致控件失效。

解决:引入路径全部改用 EL 绝对路径:

<script src="${pageContext.request.contextPath}/My97DatePicker/WdatePicker.js"></script>

同时在过滤器里放行静态目录:/My97DatePicker/、/css/、/js/ 不参与登录校验。我拿到课设资源的第一件事就是把所有静态资源的引入改成绝对路径,能消掉一半页面样式问题,包括这个日期控件。

5.4 Tomcat 10 与 javax.servlet 包冲突

现象:项目导入 IntelliJ IDEA 后所有 Servlet 类红标,提示 The import javax.servlet cannot be resolved,把 Tomcat 10 加进运行环境仍然报错。

原因:Tomcat 10 把 Java EE 规范换成了 Jakarta EE,Servlet 的包名从 javax.servlet 改成了 jakarta.servlet。这份资源是典型的老 Java Web 项目,所有 import 都基于 javax,在 Tomcat 10 上直接编译不过。这是环境与代码的兼容性问题,不是代码本身的 Bug。

解决:把运行环境换成 Tomcat 9 或 8.5,源码和配置都不用动。如果机器上只有 Tomcat 10 又不想装新版,只能把全项目 import javax.servlet 批量替换成 jakarta.servlet,替换后还要检查 JSP 里隐含的 servlet 调用。我的习惯是课设环境固定 Tomcat 9,血泪经验:别跟 Servlet 容器版本较劲。

6. 答辩加分技巧:自己动手加一个库存预警模块

加一个功能,比改十个 Bug 更能在答辩时拉分。这里说一个任何超市系统都适用、代码量又小的扩展:库存预警。它和数据库课设的契合点在于——需要改表、加查询、加页面,完整走一遍“数据库 → 业务 → 展示”的流程,老师想看不到你都难。

先给 t_goods 加预警下限字段。如果建表时还没有这个字段,就补一条:

ALTER TABLE t_goods ADD COLUMN stock_min INT DEFAULT 10 COMMENT '库存预警下限';

然后 DAO 层加一个查询方法:

public List<Goods> findLowStock() { String sql = "SELECT * FROM t_goods WHERE stock <= stock_min ORDER BY stock ASC"; // 执行查询,逐行封装 Goods 对象返回 }

逻辑说明:把筛选条件写在 SQL 而不是 JSP 的遍历里,是这条扩展里最关键的决策。数据库在引擎层完成过滤,返回的行数就是“真问题”的条数,页面拿到数据直接展示即可。如果放在 JSP 里用 JSTL 遍历全表再逐行判断,库存 10 万条时页面会明显变慢,而且代码难读。

Servlet 里加一个 action 分支:

} else if ("lowStock".equals(action)) { GoodsDao goodsDao = new GoodsDao(); request.setAttribute("lowStockList", goodsDao.findLowStock()); request.getRequestDispatcher("/low_stock.jsp").forward(request, response); }

low_stock.jsp 里用表格列商品编码、名称、当前库存、预警下限四列,库存数量用醒目的红色标注。菜单栏再加一个入口链接,指向 lowStock 动作,完整闭环就出来了。

验证方法:往 t_goods 插两条对照数据,一条 stock=5、stock_min=10(应触发预警),一条 stock=80、stock_min=10(不触发)。打开预警页面,只出现第一条,说明筛选逻辑正确;再手动把第一条库存改成 12,刷新后它消失,证明查询是实时读库的。

我在做这类扩展时也走过弯路。第一次给项目加“销售统计 Top10”时图快,把统计逻辑写进 JSP 的遍历里,商品数据一多页面明显变慢,答辩演示差点翻车。从那以后,我每加一个统计或筛选类功能都强制先问自己一句:这条筛选该不该在数据库里完成?答案要是“该”,就写进 SQL,JSP 只做展示。这个习惯帮我省了不少事后优化的时间。这份资料里的建库脚本和源码是打包好的,按第 3 章建库、第 5 章查一遍环境,基本就能跑通,希望帮到你。

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

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

游戏UGC多智能体AI圆桌协作系统:Token控制与本地推理实践

1. 从“Token 焦虑”说起&#xff1a;为什么游戏 UGC 场景需要一场 AI 圆桌 做游戏 UGC 内容的人&#xff0c;最近一两年大概都有一种共同的体感&#xff1a;AI 能帮上忙&#xff0c;但帮得不够“顺”。你想让 AI 帮你写一段 Minecraft 的建筑设定、生成一段 NPC 对话、再顺手把…

作者头像 李华
网站建设 2026/10/7 23:23:58

Agent Skills 技能体系:重构智能体架构,告别提示词膨胀

大概半年前&#xff0c;我接手维护一个智能体项目&#xff0c;系统提示词堆到了 6000 字。每次请求光把这坨规则塞进模型就要吃掉大量上下文&#xff0c;日常请求稳定在 1.2 万 token 左右&#xff0c;延迟三秒起步&#xff0c;回答还经常自相矛盾——旧的规则被新的规则覆盖&a…

作者头像 李华
网站建设 2026/10/7 23:22:31

反激电源CCM与DCM模式判别与设计要点

1. 为什么反激电源的CCM/DCM模式切换不是“选哪个更好”&#xff0c;而是“必须算清楚再动手”反激式开关电源&#xff0c;这个在小功率适配器、LED驱动、辅助电源里几乎无处不在的拓扑&#xff0c;表面上看就是个变压器加几个MOSFET和二极管&#xff0c;但真正把它调稳、调高效…

作者头像 李华
网站建设 2026/10/7 23:22:25

RAG失效后如何微调大模型?完整LoRA实战与踩坑记录

先说结论&#xff1a;RAG不是万能的&#xff0c;当你发现检索增强生成&#xff08;RAG&#xff09;的答案开始“一本正经地胡说八道”&#xff0c;或者召回的片段怎么也拼不成一句人话时&#xff0c;那就该考虑走模型微调这条路了。我这次微调自己模型的起因很直接——在做垂直…

作者头像 李华
网站建设 2026/10/7 23:20:10

Deeplab-ResNet建筑物变化检测实战:从训练到GIS部署

简介&#xff1a;本资源是一套基于Deeplab-ResNet算法的建筑物变化检测完整实现源码&#xff0c;面向遥感图像分析、GIS应用开发及深度学习图像分割方向的研究者与工程实践者&#xff0c;解决高分辨率遥感影像中建筑物新建、拆除或损毁等动态变化的精准识别问题&#xff0c;适用…

作者头像 李华
网站建设 2026/10/7 23:18:55

Replay 8.7汉化终版实测:AI翻唱与音轨分离的完整指南

打开软件的那一刻&#xff0c;我差点以为自己下错了版本。Replay 8.7汉化终版&#xff0c;界面干干净净&#xff0c;全中文显示&#xff0c;AI翻唱、音轨分离、变调变速这些核心功能一眼就能找到&#xff0c;不用再对着英文菜单反复查词典。用了一阵子之后&#xff0c;我想把这…

作者头像 李华