news 2026/10/9 12:52:25

JavaWeb超市管理系统毕业设计:Servlet+JSP+JDBC分层实现与部署避坑指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
JavaWeb超市管理系统毕业设计:Servlet+JSP+JDBC分层实现与部署避坑指南

简介:一套基于 JavaWeb 的超市管理系统毕业设计项目,包含可运行源码与数据库脚本,适合计算机、通信、人工智能、自动化等相关专业学生、教师或从业者,作为毕业设计、课程设计或期末大作业参考,也可作为 Java 入门者的进阶练手项目。系统围绕商品管理、会员管理、供应商管理、结账与用户登录等模块展开,采用 JSP 与 Servlet 的分层设计,代码经调试测试可正常运行,整体结构清晰,便于理解 JavaWeb 项目从数据访问到页面渲染的完整流程。压缩包为 zip 格式,共 386 个文件,作品中以 49 个 Java 源文件、56 个 JSP 页面和 98 个 class 文件为核心,同时提供 SQL 数据库脚本及 CSS、SCSS、JS 等前端资源,包体大小约 10.36MB。这套系统曾作为个人毕业设计获得 98 分评价,目前已有 156 人浏览学习。读者可在部署运行后对照源码二次开发,如扩展库存预警、销售统计、供应商对账等模块。

1. 中式超市管理系统该有的样子:先搞清楚这份 javaweb 毕业设计值不值得花时间

拿到这份 javaweb 超市管理系统时,我第一反应是“又一个三层架构大作业”,但拆完代码发现它其实是那种答辩特别稳的项目:Servlet 管请求、JSP 渲染页面、DAO 封装 JDBC,再加一套可直接导入的数据库脚本。见过太多毕设死在“框架堆得华丽但跑不起来”,这个反过来——代码朴素但逻辑闭环完整,商品、会员、供应商、收银、用户五条业务线全部打通,用应付“你这系统怎么设计的”这类答辩提问,比花架子 Spring Boot 全家桶好使。尤其它的收银模块涉及事务处理,这是多数课程设计里最容易偷懒、也最容易被老师追问的地方。如果你是计算机、通信、自动化相关专业的学生,需要一份能讲清楚来龙去脉的 JavaWeb 项目当毕设底稿,或者正在补 Servlet/JSP 底层链路的知识,这个资源值得拆开看一看。

2. 从五个 DaoImpl 反向拆解项目:看懂 Servlet + DAO + JSP 的老三层是怎么协作的

2.1 为什么这套老技术栈反而是毕设的加分项

现在培训机构推的都是 Spring Boot + MyBatis-Plus,但如果你拿这种框架交毕设,最容易翻车的场景恰恰是答辩:老师一句“你这个接口是怎么被路由到 Controller 的”,很多同学就卡住了。而 Servlet + JSP + JDBC 这套组合虽然“老”,但它把一次 HTTP 请求的完整路径摆在了明面上——浏览器发请求,Tomcat 根据web.xml或注解找到 Servlet 的doGet/doPost,Servlet 调 DAO 接口,DAO 实现类用 JDBC 拼 SQL 查 MySQL 数据库,再把结果setAttribute后forward给 JSP 渲染。

这个链路每一步都能展开讲,也最容易用“自动挡换成手动挡”的类比跟老师解释清楚。再看这套系统的类结构,CheckoutServlet.class、UserDaoImpl.class、ProductDaoImpl.class、VipDaoImpl.class、ProviderDaoImpl.class直接摆在那里说明它没有引入额外框架,就是最原始的 JavaWeb 分层。这类项目还有另一个隐性福利:代码量可控。一个有正常课业压力的学生,两周内把它读透并改造出自己的功能模块,比从零写一个 Spring Boot 项目要现实得多。所以选这套技术栈不是落后,是策略。

2.2 通过类名反推数据库表结构:文件清单里的信息量

拿到资源先别急着跑,先看那五个类名,项目的数据模型基本就出来了。每个DaoImpl背后至少对应一张主表和一组增删改查方法,这是我拆这类项目时最爱用的“逆向读项目”方式:

类名对应的业务表(常见设计)核心职责
UserDaoImplt_user用户登录、密码校验、权限区分
ProductDaoImplt_product商品增删改查、库存更新、按名称模糊搜索
VipDaoImplt_vip会员开卡、充值、折扣查询
ProviderDaoImplt_provider供应商信息维护、进货记录
CheckoutServlett_sale_record/t_product收银结算、减库存、生成销售记录

那数据库脚本里常见的字段就是:t_user(id, username, password, role),t_product(id, name, price, stock, provider_id, is_delete),t_vip(id, name, phone, score, discount),t_provider(id, name, contact, phone)。其中is_delete是我特别留意的字段——正规一点的超市管理系统做删除商品时不会物理删行,而是软删除,把is_delete置 1,查询时统一带WHERE is_delete = 0过滤。如果你的资源里数据库脚本有这一列,说明作者写代码时有真实业务意识,项目质量就有底了。

搞清楚表结构之后,整个项目的运转逻辑就清晰了:营业员登录系统,先维护好供应商和商品,顾客来结账时输入会员号享受折扣,CheckoutServlet在当前线程的事务里完成减库存和写销售流水。这套逻辑放在课程设计里已经能覆盖“超市管理系统”这个题目的绝大多数功能点。

2.3 把项目导入 IDEA 并按序启动:本文的操作步骤

我拿到这类源码包的习惯是不急着双击web.xml,先按下面五个步骤把运行环境固定下来,避免后面踩环境坑时到处猜。第一步装 JDK 8,注意是 JDK 8 而不是 11 或 17,老项目对高版本 JDK 的兼容性常出问题;第二步装 Tomcat 8.5 或 9.0,装完后在 IDEA 里 Setting 配置 Application Servers;第三步装 MySQL 5.7 或 8.0,如果本机已经装了 8.0 也没关系,驱动和连接串注意一下即可;第四步把数据库脚本导入,命令行方式最直接:

mysql -u root -p supermarket < /path/to/supermarket.sql

用-p后回车再输密码,比直接写在命令行里安全。<后面是 SQL 脚本的绝对路径,Windows 下注意路径不要带中文,否则可能出现读取乱码或找不到文件的诡异报错。导入完成后用show tables;验证一下表是否存在,别跳过这步,至少能确认脚本执行没有静默失败。

第五步打开项目里的数据库连接配置,最常见的是一个db.properties文件或DBUtil.java,改成本机能连的参数。这里给出一个典型的DBUtil.java长这样:

public class DBUtil { private static final String URL = "jdbc:mysql://localhost:3306/supermarket" + "?useSSL=false&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai"; private static final String USER = "root"; private static final String PASSWORD = "123456"; static { try { Class.forName("com.mysql.cj.jdbc.Driver"); } catch (ClassNotFoundException e) { e.printStackTrace(); } } public static Connection getConnection() throws SQLException { return DriverManager.getConnection(URL, USER, PASSWORD); } }

如果你的 MySQL 是 5.7,需要把驱动类名改回com.mysql.jdbc.Driver;如果是 8.0,保持上面这个就行,但serverTimezone=Asia/Shanghai必须加,否则数据库连接会直接报时区错误。URL 里的characterEncoding=UTF-8同样不能删,不然 JSP 页面显示商品名称时就是一片问号。

配好后在 IDEA 里添加 Tomcat 运行配置,Deployment 那一栏选war exploded,Application context 填/supermarket或项目里实际配置的路径,点击运行。这里有个容易漏的操作:Tomcat 配置里要把项目的依赖包加进去,否则启动日志会报ClassNotFoundException,因为 Tomcat 没有加载项目的类。启动后浏览器访问http://localhost:8080/supermarket/login.jsp,看到登录页说明环境通了。

3. 把五条业务线逐条拆开:登录、商品、收银台、会员和供应商的实现逻辑

3.1 登录与权限:UserDaoImpl是入口也是最容易被忽视的安全短板

很多课程设计的登录模块就一句SELECT * FROM user WHERE username = ? AND password = ?,能通就万事大吉。但拆这份项目时你会发现,关键问题往往不在功能通不通,而在这么写能不能禁得起提问。从安全角度讲,用PreparedStatement做参数化查询是底线,直接拿字符串拼 SQL 的写法会在答辩上被老师一问一个准。下面是一个典型的UserDaoImpl.login()方法写法:

public User login(String username, String password) { String sql = "SELECT * FROM t_user WHERE username = ? AND password = ?"; try (Connection conn = DBUtil.getConnection(); PreparedStatement ps = conn.prepareStatement(sql)) { ps.setString(1, username); ps.setString(2, password); try (ResultSet rs = ps.executeQuery()) { if (rs.next()) { User user = new User(); user.setId(rs.getInt("id")); user.setUsername(rs.getString("username")); user.setRole(rs.getString("role")); return user; } } } catch (SQLException e) { e.printStackTrace(); } return null; }

这里?占位符配合ps.setString()能防 SQL 注入,这是底线。登录成功后session.setAttribute("user", user)存的是User对象而不是散装用户名,后面 JSP 页面判断权限时直接从 session 取对象的role字段,比反复查库高效也不容易出错。角色字段通常分admin和cashier两种,管理员能进商品维护页面,收银员只能进收银台,不想加 Spring Security 这类框架时,在 JSP 顶部加一个<c:if test="${sessionScope.user.role == 'admin'}">做菜单显隐就够课程设计用了。

我一般会在登录代码里特别留意一点:校验失败时不要提示“用户名不存在”或“密码错误”这种区分性信息,统一返回“用户名或密码错误”,防止被拿去探测账号有效性。这个细节写进答辩讲稿里,往往会让老师觉得你有安全意识。

3.2 商品管理:软删除和分页查询是两个高频提问点

ProductDaoImpl是业务量最大的一个 DAO,商品新增、编辑、下架、查询四件事都要在这里完成。新增商品的 SQL 相对直白,注意字段别漏,provider_id关联供应商表,这是一个外键逻辑但很多课程设计会漏掉。真正值得讲的是商品列表的展示,一旦商品数据超过几十条,全量SELECT *就不现实,分页几乎是超市系统的刚需。分页的核心就是 LIMIT 加两个参数:

SELECT * FROM t_product WHERE is_delete = 0 LIMIT ?, ?;

第一个?是偏移量(pageNum - 1) * pageSize,第二个?是每页条数pageSize。前端 JSP 页面用一个<c:forEach>循环遍历传给页面的列表数据,底部是两个按钮“上一页”和“下一页”,Servlet 从 request 参数里拿pageNum做边界判断。这里的坑在于第一页时pageNum - 1会变负数,所以进方法第一步就该检查pageNum < 1时强行置为 1。我见过不少项目在分页上翻车,全是因为没有对页码做下限校验,最后一页翻过头时列表直接空白。

再就是删除操作。物理删除DELETE FROM t_product WHERE id = ?写起来简单,但意味着销售记录表里关联的商品名和价格会失去源头,将来查历史流水时数据对不上,这是实际业务里非常忌讳的事。很多课程设计为了图省事就这么写了,答辩时老师问“你这个超市的销售历史和商品档案怎么追溯”,当场就接不上话。所以作者在t_product里设计is_delete字段、查询统一带条件,这一手是值得你写进讲稿里的。如果拿到的源码不是软删除,那我建议动手改一版:把delete方法改成UPDATE t_product SET is_delete = 1 WHERE id = ?,改动量不大但能体现真实业务思考。

3.3 收银台事务:CheckoutServlet是整套系统的含金量所在

再来谈这个项目最有份量的一个模块。超市收银不是简单的“算个总价”,它牵一发动全身——顾客买了三件商品,你要同时完成三件事:计算折扣后的总金额、扣减对应商品的库存、向销售记录表插入一条流水。这三件事必须一起成功或一起失败,这就是数据库事务的经典场景。而很多课程设计在收银这块其实就是做个“购物车求和”,完全没考虑一致性。这份项目里单独拆出一个CheckoutServlet而不是塞在别的 Servlet 里,说明作者在架构上是有意识的。

收银核心逻辑我把它写成伪代码加注释的框架,方便你对照着读源码:

protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String vipId = req.getParameter("vipId"); String[] productIds = req.getParameterValues("productId"); String[] quantities = req.getParameterValues("quantity"); Connection conn = null; try { conn = DBUtil.getConnection(); conn.setAutoCommit(false); // 开启事务 // 1. 查会员折扣率 double discount = 1.0; if (vipId != null && !vipId.isEmpty()) { discount = getVipDiscount(conn, vipId); } // 2. 循环扣减库存并计算总价 double total = 0; for (int i = 0; i < productIds.length; i++) { int pid = Integer.parseInt(productIds[i]); int qty = Integer.parseInt(quantities[i]); double price = getProductPrice(conn, pid); // 减库存前先校验库存够不够 if (!reduceStock(conn, pid, qty)) { throw new RuntimeException("库存不足"); } total += price * qty; } total = total * discount; // 3. 写销售流水 insertSaleRecord(conn, total); conn.commit(); // 所有操作成功,提交事务 resp.sendRedirect("checkout_success.jsp"); } catch (Exception e) { if (conn != null) { try { conn.rollback(); } catch (SQLException ex) { ex.printStackTrace(); } } req.setAttribute("errorMsg", e.getMessage()); req.getRequestDispatcher("checkout.jsp").forward(req, resp); } finally { if (conn != null) { try { conn.setAutoCommit(true); conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } }

这段代码里最关键的是conn.setAutoCommit(false)和conn.commit()之间的代码块——中间任何一步抛异常,rollback()就会把前面已经执行的扣库存操作全部撤销。逻辑上我先校验库存再扣减,避免出现卖超的情况;productId和quantity用两个数组接收,是因为页面上多个相同 name 的隐藏字段提交过来时,getParameterValues拿到的是字符串数组,必须一一对应取。最后用resp.sendRedirect而不是 forward 跳转到成功页,这个是防止刷新页面重复提交的关键——F5 刷新一个 POST 请求页面会重新执行一次结账,重复扣库存,这是老项目最常见的生产事故。如果你在自己项目里看到结账后没做重定向,这个坑必须补上,补法就是上面这种“PRG 模式”(POST-Redirect-GET)。

3.4 会员与供应商两条副线:分数差距都在细节里

VipDaoImpl和ProviderDaoImpl看起来是配角,但毕设分数差往往就差在这里。会员模块最常见的功能是开卡和充值,开卡时设置初始积分,充值时按金额累计积分,积分反过来可以兑换小礼品或抵扣现金。这里有个交互细节:收银员结账时输入会员手机号就能查到折扣率,而不是每次都要手动选会员 ID,这个查询通常用SELECT discount FROM t_vip WHERE phone = ?。很多做得敷衍的项目会在结账页面放一个下拉框列出所有会员,几十个还可以,上百个就会卡到没法用,这个对比你可以写进论文的“系统优化”部分。

供应商模块的核心价值在于进货。新增一个供应商时,联系电话、地址等字段要加非空校验;商品表的provider_id下拉框数据就是从t_provider查出来的,如果页面显示的是供应商 ID 而不是名字,JSP 里value="${product.providerId}"显示时需要做一个 ID 到名称的映射,一般是在查商品时直接JOIN t_provider查出名字,避免在页面里循环比对。我在看这类代码时会特别留意 DAO 层有没有做结果的 map 映射,很多同学干脆把结果集直接塞给 JSP 用,字段定义乱七八糟,这是需要调整的地方:

public List<Provider> findAll() { String sql = "SELECT id, name, contact, phone FROM t_provider WHERE is_delete = 0"; List<Provider> list = new ArrayList<>(); try (Connection conn = DBUtil.getConnection(); PreparedStatement ps = conn.prepareStatement(sql); ResultSet rs = ps.executeQuery()) { while (rs.next()) { Provider p = new Provider(); p.setId(rs.getInt("id")); p.setName(rs.getString("name")); p.setContact(rs.getString("contact")); p.setPhone(rs.getString("phone")); list.add(p); } } catch (SQLException e) { e.printStackTrace(); } return list; }

这段代码的教训是:列的读取一定要用别名对齐,getInt("id")而不是getInt(1),这样 SQL 里字段顺序调整时不用改代码,抗变更能力要强得多。另外该类查出的Provider对象可以直接复用在商品下拉框的渲染里,一对多关系维护在Product包含Provider引用上,读源码时可以留意作者用的是关联查询还是二次查库。

4. 部署与运行避坑:老 JavaWeb 项目最容易踩到的五个坑

4.1 启动直接ClassNotFoundException,Tomcat 页面全报错

现象:Tomcat 一启动,日志里报ClassNotFoundException: com.mysql.cj.jdbc.Driver,或者项目类的其他包找不到。原因基本都是 IDEA 里 Tomcat 配置的 deployment 没有把项目的webapp目录和依赖的 lib jar 挂上去,Tomcat 运行时根本看不到你的类和驱动。解决:项目右键 Open Module Settings,Artifacts 里确认选的是war exploded,然后在 Available Elements 里把项目依赖的 jar 右键加进 WEB-INF/lib;如果是老项目,还要检查 Library 里有没有配置 Tomcat 自带的 servlet-api。这个配置一次性搞定,后面很少再出问题。

4.2 连接时报Public Key Retrieval is not allowed

现象:MySQL 8.0 下 JDBC 连接报Public Key Retrieval is not allowed,或者Access denied for user。原因:MySQL 8 默认的caching_sha2_password认证插件要求客户端支持公钥检索,而老驱动 JDBC 默认不允许;同时驱动类名也从com.mysql.jdbc.Driver换成了com.mysql.cj.jdbc.Driver。解决:把连接串改成jdbc:mysql://localhost:3306/supermarket?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai,这是 MySQL 8 连接最完整的配置,缺哪段都可能在后面报出不同错误。

4.3 结账后按 F5 刷新,库存重复扣减

现象:收银台结账成功后手滑按了 F5,再查库存变负了,销售流水里也多了一条重复记录。原因:表单 POST 提交到 Servlet,Servlet 处理完直接 forward 到 JSP,刷新就重发了请求。解决:按前面 3.3 的方式改成 POST-Redirect-GET,resp.sendRedirect("success.jsp")让成功页面变成一个全新 GET 请求;同时可以在数据库给销售流水表加一个(order_no)唯一约束做最后兜底,双保险。

4.4 页面显示中文全是问号或乱码

现象:商品名称、供应商名称在页面上显示成???或一堆乱码。原因:三层编码不一致——数据库表是utf8,但 JDBC 连接串没加characterEncoding=UTF-8,JSP 页面本身也没有pageEncoding标签。解决:连接串加上characterEncoding=UTF-8,同时在每个 JSP 顶部加<%@ page contentType="text/html;charset=UTF-8" language="java" %>,数据库建表统一用DEFAULT CHARSET=utf8。这是一套联动配置,只改其中一处其他两处还是会出问题。

4.5 导入 Eclipse 后 JSP 报错:javax.servlet不存在

现象:用 Eclipse 导入项目,打开 JSP 文件满屏红叉,报javax.servlet cannot be resolved to a type。原因:Eclipse 里没有给项目配置 Tomcat Runtime,Servlet 相关类就不在编译路径里。解决:项目 Properties → Targeted Runtimes → 勾选本机的 Tomcat,红叉会立刻消失。另外老项目可能会用到javax.servlet.jsp.jstl标签库,如果.jsp文件里<c:forEach>标签报红,记得把jstl.jar也加到 WEB-INF/lib 下。

这几条是我拆老 JavaWeb 项目时重复踩过的坑,基本都是环境或编码问题而不是业务逻辑问题。拿到的项目如果遇到类似报错,对照上面逐条排查,比去论坛里搜完整错误码省很多时间。

5. 从能跑到能改:用一份验证清单验收项目,再给它升级两个硬核功能

面对一份二手毕设,最怕的不是看不懂代码,而是不知道它到底是不是“真的能跑”。我每次拿到这类项目都会先走一遍全链路业务验证,而不是只看登录页能不能出。五分钟跑完一条完整流程,比读十遍代码更能判断项目底细:

步骤操作预期结果
1用 admin 账号登录跳转到后台主页,顶部显示管理员菜单
2供应商管理里新增一条记录列表新增成功,无报错
3商品管理里添加一个新商品,选刚才的供应商商品列表能看到新商品,库存为设定值
4会员管理里开一张会员卡,充值 100 元积分累计到对应金额
5拿着新商品去收银台结账,输入会员号折扣生效,库存减少,销售流水新增一条
6F5 刷新结账成功页没有重复扣库存(因为用了 PRG)

走完一遍发现问题就当场修,比被动等着答辩时被老师发现强得多。走过这条链路之后,如果想在答辩时多拿点分,我给两个改动成本不高但显得有想法的升级方向。第一个是给商品列表加一个“库存预警”入口,在ProductDaoImpl里加一个方法,SQL 一行就能完成:

SELECT * FROM t_product WHERE stock <= 10 AND is_delete = 0;

在主页加一个带红点的提醒按钮,点击后转发到一个专门的 JSP 展示低库存商品列表,这就是“库存预警功能”。第二是把DBUtil里用DriverManager.getConnection()的方式换成 Druid 连接池,核心改动在DBUtil类和druid.properties配置,但能让项目在答辩时多一个“性能优化”的话术,远比“加了缓存”这种虚无的表述能落地。

拆这个项目的过程里我最大的体会是:毕设分数和框架新不新没什么关系,关键看你能不能把每条业务线的来龙去脉讲清楚,出问题时能不能快速定位到属于哪一层。从那以后我每次拿到一份 Servlet 老项目,都强制自己先走完整业务闭环再去读具体代码,顺序反了很容易被细节牵着走。这份资源和这套排查思路,希望帮到你。

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

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

Text-to-CAD实战:用自然语言生成可编辑STEP模型

做了十年机械设计&#xff0c;再往前数几年&#xff0c;天天跟CAD打交道最烦的三件事&#xff1a;装完CAD发现缺 SHX 字体&#xff0c;图纸莫名打不开&#xff1b;想卸载重装又卸不干净&#xff0c;二次授权直接卡住&#xff1b;画个法兰盘改了三版尺寸&#xff0c;模型树里全…

作者头像 李华
网站建设 2026/10/9 12:51:11

UML用例图扩展特征详解:定义、识别方法与实战案例

1. 先从“扩展特征”到底是什么说起用例图上的箭头&#xff0c;大概是最容易画错的东西。尤其是那个带<<extend>>的虚线箭头——形式上学两天UML就会画&#xff0c;搁到真实项目里一用&#xff0c;十有八九会把它和包含关系、继承关系搅在一起&#xff0c;最后整个…

作者头像 李华
网站建设 2026/10/9 12:50:37

数据库课程设计选课系统:MySQL表结构设计与Java事务实现避坑指南

简介&#xff1a;这份数据库课程设计资源面向高校计算机相关专业学生与课程设计指导教师&#xff0c;围绕学生选课信息管理系统展开&#xff0c;采用Java语言与MySQL数据库、C/S架构实现&#xff0c;适合作为数据库原理、Java程序设计等课程的期末大作业或实训参考。系统按学生…

作者头像 李华
网站建设 2026/10/9 12:50:26

给 Claude 加个外置大脑:claude-mem 实现跨会话长期记忆

我自己的日常有个大痛点&#xff1a;和 Claude 聊了很久的项目&#xff0c;第二天新建会话&#xff0c;它完全不记得我们昨晚的结论。我试过把背景资料写进 system prompt&#xff0c;试过每次都重新贴一遍需求文档&#xff0c;结果要么 token 烧得太快&#xff0c;要么贴漏了一…

作者头像 李华
网站建设 2026/10/9 12:50:20

Python数据分析实战工具箱:从清洗到可视化的完整指南

做了五年业务数据分析&#xff0c;电脑里换过不少工具&#xff0c;但最后每天都会打开的&#xff0c;还是那套Python 工具箱。从给运营部门写周报自动化&#xff0c;到清洗几百万行订单明细&#xff0c;再到用 sklearn 搭一个简单的流失预测模型&#xff0c;Python 这三个词基本…

作者头像 李华
网站建设 2026/10/9 12:49:26

LangChain4j 集成 pgvector 的字段名陷阱与对齐指南

我接过不少 LangChain4j 集成 pgvector 的咨询&#xff0c;一多半最后都卡在同一个地方——不是模型选错了&#xff0c;不是索引建错了&#xff0c;而是建表时字段名跟框架的默认约定对不上。最典型的就是日志里突然冒出一句column "embedding" does not exist&#…

作者头像 李华