
简介一套基于Javaweb的超市会员管理系统毕业设计项目面向计算机相关专业毕业生、课程设计学生以及需要项目实战的Java学习者可作为毕设或期末大作业直接使用。资源共742个文件压缩包约15.72MB涵盖项目源码、数据库脚本、开发工具与项目说明文档主要文件类型为java、jsp等后端代码js、css等前端资源sql数据库脚本以及大量jpg/png图片用于界面展示与文档截图。前端集成了bootstrap、jquery-ui等组件库样式与交互素材丰富便于二次开发目录结构按功能分类方便快速定位。系统采用JSP、Servlet、JDBC技术栈搭配MySQL数据库支持JDK、Eclipse、Tomcat标准环境经过严格调试确保可运行功能覆盖超市会员管理核心场景界面美观、操作简便、管理便捷。从环境配置、数据库初始化到代码部署均有配套资料可帮助快速跑通项目并理解前后端交互流程。已有668人学习适合需要完整可运行项目作为毕业设计参考的读者可直接部署使用也可基于现有代码进行功能扩展和优化。1. 从压缩包到答辩演示这套“Javaweb超市会员系统”到底能给你什么论文写完了代码还没影这是毕业设计周最常见的状态。这个“基于Javaweb的超市会员管理系统源码数据库毕业设计.zip”也正是因此被反复下载、解压、再被打回修改。先别急着点运行——这个项目的价值不在代码量而在于它把“注册会员、消费、积分、等级”这条超市主链路完整串了起来并且自带MySQL建表脚本数据库不是截图是能直接导入的SQL。对第一次做Web项目的学生来说这是能在几天内跑通并讲清楚思路的最小闭环对在职工程师来说这是一个现成的JSPServletMySQL脚手架改造一下就能做内部工具。下面按我实际带人跑通这类项目的顺序拆先看懂骨架再配环境然后改核心代码最后对付答辩前的各种玄学问题。2. 拆解交易链路四张表怎么把会员、商品、消费、积分算平很多同学拿到压缩包直接翻代码这是最常见的失策。一个Javaweb项目完整案例mysql的结构再怎么乱核心都写在数据库里。超市会员管理系统的本质是“储值—消费—积分—等级”的资金和积分两条账代码只是给这两条账做了界面和接口。所以第一步不是打开IDEA而是先把表读懂。2.1 项目骨架先定格JSPServletDAOMySQL这五层在做什么这类毕业设计为了照顾课程要求通常不上Spring Boot而是用最传统的分层JSP负责展示Servlet负责接收请求和跳转Service层放业务逻辑DAO层负责数据库增删改查底层是MySQL。这条链路在答辩时很有优势——每层职责单一老师问“登录流程怎么走的”你可以从浏览器一步步说到数据库不像Spring Boot那样要解释一堆自动装配的东西。运行一条主链路时请求是这样流转的浏览器提交表单到Servlet的doPost方法Servlet取出参数、调用ServiceService处理完调DAODAO执行SQL把结果映射成对象再返回给Servlet最后由Servlet决定转发到哪个JSP渲染页面。角色划分清楚后后面改任何功能都有明确落点。加一个“积分兑换”功能要动的是新增兑换页面、新增ExchangeServlet、在Service里写兑换业务、在DAO里加兑换方法、在t_integral_log里记一条负数的change_points。如果压缩包里的代码把SQL直接写在Servlet里不要慌把它当作纯演示代码照这个分层重写一遍比在原基础上打补丁省事得多。2.2 会员表、商品表、消费表、积分流水表字段这么设计才算及格打开SQL脚本后先找四张核心表。第一张是会员表登录账号、密码、姓名、手机号、储值余额、累计积分、当前积分、等级、状态一个都不能少。下面是一份可以直接用的建表脚本CREATE TABLE t_member ( id INT NOT NULL AUTO_INCREMENT COMMENT 自增主键, username VARCHAR(30) NOT NULL COMMENT 登录账号, password VARCHAR(64) NOT NULL COMMENT 登录密码建议存MD5摘要, real_name VARCHAR(30) DEFAULT NULL COMMENT 会员姓名, phone VARCHAR(11) DEFAULT NULL COMMENT 手机号, balance DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT 储值余额, total_points INT NOT NULL DEFAULT 0 COMMENT 累计积分只增不减决定等级, current_points INT NOT NULL DEFAULT 0 COMMENT 当前可用积分可兑换扣减, level TINYINT NOT NULL DEFAULT 1 COMMENT 会员等级1普通 2银卡 3金卡, status TINYINT NOT NULL DEFAULT 1 COMMENT 账号状态1正常 0冻结, create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 注册时间, PRIMARY KEY (id), UNIQUE KEY uk_username (username), KEY idx_phone (phone) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT会员表;这里有几个字段是踩过坑才明白为什么要单独设计的。余额用DECIMAL(10,2)而不是FLOAT因为浮点数存金额会有0.10.2算不干净的经典事故total_points和current_points必须分两个字段累计积分用于定级当前积分用于兑换如果只存总数“升级”和“兑换”两个动作会互相覆盖status字段在答辩时常被忽略但它对应“冻结账号”这个管理功能没有它演示“管理员禁用会员”时只能删数据。第二张是商品表t_goods核心字段是商品编码、名称、单价、积分赠送规则、库存。积分赠送规则用“每满多少元赠送1积分”的整型字段give_points_rate来表示比直接存比例更符合超市小票的算法后面算积分时用金额除以这个数取整就行。CREATE TABLE t_goods ( id INT NOT NULL AUTO_INCREMENT, goods_no VARCHAR(20) NOT NULL COMMENT 商品编码对应条码, goods_name VARCHAR(100) NOT NULL COMMENT 商品名称, price DECIMAL(10,2) NOT NULL COMMENT 销售单价, unit VARCHAR(10) DEFAULT 个 COMMENT 计量单位, give_points_rate INT NOT NULL DEFAULT 1 COMMENT 每满多少元赠送1积分, stock INT NOT NULL DEFAULT 0 COMMENT 库存, status TINYINT NOT NULL DEFAULT 1 COMMENT 1上架 0下架, PRIMARY KEY (id), UNIQUE KEY uk_goods_no (goods_no) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT商品表;第三张是消费记录表t_sale_record。注意这里存的是“单行消费明细”而不是“整张小票”因为一个会员一次结账可能买多件商品。要记录小票号、会员ID、商品、数量、实付金额、本次积分以及消费时间CREATE TABLE t_sale_record ( id BIGINT NOT NULL AUTO_INCREMENT, sale_no VARCHAR(30) NOT NULL COMMENT 小票号格式建议yyyyMMddHHmmss随机数, member_id INT NOT NULL COMMENT 会员ID, goods_id INT NOT NULL, goods_name VARCHAR(100) NOT NULL COMMENT 冗余商品名防止商品改名后历史记录错乱, quantity INT NOT NULL DEFAULT 1 COMMENT 购买数量, amount DECIMAL(10,2) NOT NULL COMMENT 实付金额, points_gained INT NOT NULL DEFAULT 0 COMMENT 本次赠送积分, sale_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 成交时间, PRIMARY KEY (id), UNIQUE KEY uk_sale_no (sale_no), KEY idx_member_time (member_id, sale_time) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT消费记录表;goods_name在这里冗余一份是为了防止后台改了商品名之后历史小票全部跟着变。字段冗余在数据量小的系统里不是罪反而能在列表页少写一次联表查询。第四张是积分流水表t_integral_log。这张表是这个项目里最出彩的设计也是答辩时最能讲的东西。会员表里的current_points只是一个“余额”而这张表记录的是每一笔积分变动的“账本”变动类型、变动值、变动前余额、变动后余额、关联单号。只要余额和账本对不上执行一条对账SQL就能找到是哪笔变更漏记了。CREATE TABLE t_integral_log ( id INT NOT NULL AUTO_INCREMENT, member_id INT NOT NULL COMMENT 会员ID, change_type TINYINT NOT NULL COMMENT 1消费赠送 2兑换扣减 3手工调整 4过期回收, change_points INT NOT NULL COMMENT 变动积分数正数增加负数扣减, before_points INT NOT NULL COMMENT 变动前余额, after_points INT NOT NULL COMMENT 变动后余额, ref_no VARCHAR(30) DEFAULT NULL COMMENT 关联业务单号, remark VARCHAR(255) DEFAULT NULL COMMENT 备注, create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY idx_member (member_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT积分流水表;我见过很多同学把积分流水省掉只在会员表里维护一个总数。演示时确实看不出差别但老师问到“你如何审计积分数据异常”就答不上来。加了这张表后你可以现场演示手动改掉一个会员的积分余额然后用流水表重算找出问题。这是整个项目从“能跑”到“有设计感”的分水岭。2.3 主键选型自增、业务编码、UUID怎么分工四张表的主键用自增int是合理的但小票号sale_no不能直接用自增ID替代。真实小票号需要体现时间格式通常是yyyyMMddHHmmss加四位随机数这样同一秒内多笔消费不会撞号而且对账时一眼能看出是哪天的订单。生成方式放在Service层用当前时间字符串加随机数拼出来。UUID在小票号上不建议用太长、无序演示时打出来的小票一串乱码观感很差。会员表还有一个隐含问题要注意username做了唯一索引phone只做了普通索引。如果业务要求一个手机号只能注册一次应该在建表脚本里把phone改成UNIQUE KEY而不是在代码里先查一遍再插入——并发情况下先查再插会重复唯一索引才是数据库层面的兜底。这类细节不需要多高深的技术但能在答辩时体现你考虑过并发。3. 用 IDEA 本地跑通项目版本选型、导入数据库与启动配置看懂了表结构下一步就是让项目在本地转起来。这一步卡住最多人的不是代码而是IDEA运行javaweb项目配置里那串版本组合。我的经验是先确认依赖版本再导数据库最后配Tomcat顺序不能乱。3.1 JDK、Tomcat、MySQL 驱动三者的兼容组合这类老牌Javaweb毕业设计十有八九按JDK 8的标准写用到了javax.servlet、JSTL标签库这些老接口。最稳的组合是JDK 8 Tomcat 8.5或9.0 MySQL 5.7或8.0 mysql-connector-java 5.1.49连MySQL 8时用8.0.x驱动。如果机器上装了JDK 17项目往往能编译但Tomcat启动时报非法反射访问警告严重时直接起不来JDK 17移除了很多老版本依赖的反射能力跟老项目里的CGLIB代理类容易起冲突。不要为了追新版本去折腾毕业设计的评分标准里没有“用了最新JDK”这一项。Tomcat版本是另一个大坑。Tomcat 10起把Servlet规范从javax.servlet包迁移到jakarta.servlet老项目里所有import javax.servlet.*的代码在Tomcat 10上会直接编译不过即使强行编译过也会在启动时ClassNotFoundException。源码包说明文档如果写的是“Tomcat 8/9”就老老实实用这个版本。Tomcat 9就是Javaweb老项目的安全区。MySQL驱动要看数据库版本我把常见组合整理成一个表方便对照组件推荐版本关键点JDK1.8项目SDK选8Tomcat8.5 / 9.0不要用10MySQL5.7 / 8.08.0必须配时区参数驱动5.1.49 / 8.0.x类名和URL写法不同注意Tomcat目录不要解压到带空格的路径下比如C:\Program Files下的Tomcat在老版本IDEA里偶尔出现部署异常放到D:\dev\tomcat这类路径最省心。3.2 数据库导入、项目导入与Tomcat配置的操作步骤先把数据库准备好。本地直接打开Navicat或IDEA自带的Database面板新建一个空库库名建议就叫supermarket然后右键运行SQL脚本文件把压缩包里的.sql完整执行一遍。执行完的检查点不是“执行成功”的绿色提示而是展开表列表确认t_member、t_goods、t_sale_record、t_integral_log都在并且t_member里至少有一条测试账号数据。接下来导入项目。File → New → Project from Existing Sources选中解压后的目录一路Next到了SDK选择步骤时选JDK 8。如果项目里没有Maven的pom.xml它就是纯Web项目用File → Project Structure → Modules → Web补一个Web模块把Web资源目录指向src/main/webapp或WebRoot再看Artifacts里生成一个war exploded包。纯源码包经常缺这步导致项目能编译但Tomcat里没有可部署的artifact。关于资源目录判断我有个习惯看源码包根目录里有没有WebRoot或src/main/webapp。有WebRoot的Project Structure的Web模块里要把Web Resource Directory指到这个目录Java源码目录单独加到Sources只有src/main/webapp的按标准Maven风格设置。这一步很多教程一笔带过但恰恰是“IDEA运行javaweb项目配置”最常见的卡点。配置错了最典型的表现是代码能编译Tomcat启动也正常但访问时只有Tomcat默认页找不到你的Servlet——因为你打出来的Artifact是个空壳。Tomcat配置在IDEA里是Run → Edit Configurations → 左上角加号 → Tomcat Server → Local。Name随便起Application Server选到本地的Tomcat 9目录Deployment标签页里点加号选Artifact把war exploded添加到部署列表Application context填/supermarket这样访问路径就是http://localhost:8080/supermarket。这里建议把Server标签页里端口记下来默认是8080后面所有演示都以这个端口为准。还有一个配置容易漏少部分项目把数据库连接写死在Java代码里比如DriverManager.getConnection(jdbc:mysql://localhost:3306/supermarket, root, 123456)你需要核对这里的库名、用户名、密码是否和自己的MySQL一致。如果项目里有jdbc.properties或db.properties改那个文件就行。这一步不做好点完Run看到的是Tomcat启动成功的日志但一登录就报数据库连接失败。3.3 启动后的第一个自测用初始化数据把主链路走一遍项目真正启动后先别点任何功能按这个顺序做冒烟测试。第一浏览器打开登录页确认页面没有满屏报错第二用测试账号登录如果SQL脚本自带账号就能直接登登不进去就查数据库看密码字段存的是不是MD5摘要——很多时候源码里登录逻辑是做MD5后再比对而你手动往数据库插了一条明文密码怎么登都是密码错误第三登进去后点一个列表页比如消费记录看有没有报500第四执行一次完整的消费下单去数据库里查t_sale_record和t_integral_log各多出一行。验证的SQL我一般这样写三分钟出结果SELECT m.username, m.balance, m.current_points, COUNT(DISTINCT s.id) AS sale_count, SUM(s.points_gained) AS total_sale_points FROM t_member m LEFT JOIN t_sale_record s ON s.member_id m.id GROUP BY m.id;这条语句检查会员表余额、积分和消费流水是否吻合。如果SUM(s.points_gained)和m.current_points对不上就是事务或积分计算逻辑有漏比肉眼看页面可靠得多。如果第四步两条流水都出现并且对得上这个系统的主链路就是通的。后面改任何代码都以这一步作为回归标准。把操作过程录屏存下来答辩前再按同样顺序走一遍比临时翻代码稳得多。4. 核心代码改造登录、消费、积分入账的完整链路环境跑通了接下来把核心代码读明白、改明白。这个系统的主链路就两条登录会话和消费积分。这两段代码是答辩时老师最可能让你现场讲的其它像商品管理、会员列表都是增删改查没有主线价值。4.1 登录与会话管理一个能直接落地的 LoginServlet登录接口在大多数毕业设计里长这样取用户名密码查库比对成功把用户塞进Session失败回到登录页带一条错误提示。下面是我调整过的最小可用版本WebServlet(/login) public class LoginServlet extends HttpServlet { Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setCharacterEncoding(UTF-8); String username req.getParameter(username); String password req.getParameter(password); MemberDao dao new MemberDao(); Member member dao.findByUsername(username); if (member ! null member.getStatus() 1 member.getPassword().equals(Md5Util.md5(password))) { req.getSession().setAttribute(loginMember, member); resp.sendRedirect(index.jsp); } else { req.setAttribute(error, 账号或密码错误或该账号已被冻结); req.getRequestDispatcher(login.jsp).forward(req, resp); } } }逻辑说明doPost拿到表单参数后先设置请求编码UTF-8这是解决中文参数乱码的前置条件然后按用户名查库注意比对顺序——把status等于1的冻结判断放在密码比对之前这样即使是合法密码的冻结账号也进不来密码比对时把用户输入做MD5再与库中摘要比较而不是把数据库里的明文密码取出来直接比。登录成功后用Session保存member对象后续所有JSP页面通过session.getAttribute(loginMember)取当前用户。这里用sendRedirect代替forward是为了防止刷新页面时表单重复提交。参数说明dao.findByUsername里面走的是PreparedStatement预编译查询SQL是“SELECT ... FROM t_member WHERE username ?”参数通过setString绑定不直接拼字符串。这一步是堵SQL注入的底线。很多源码包里的登录是直接用Statement拼“SELECT * FROM t_member WHERE username username ”演示虽然能跑但答辩被问到SQL注入就露怯。把findByUsername改成预编译是改动最小、得分最明显的优化。Session有个特性要了解默认空闲30分钟过期。演示时开着页面半天不动再点操作会被重定向回登录页这不是bug是会话超时。要调整可以在web.xml里配置session-config单位是分钟session-config session-timeout60/session-timeout /session-config4.2 消费下单与积分赠送事务不写对账就算不平消费功能是整个系统的资金核心它要做的事不止是插一条记录要扣减会员余额、增加消费明细、计算并累加积分、再写积分流水四个动作必须一起成功或一起失败。如果两步之间抛了异常余额扣了但积分没加这个会员的账就永远对不上了。这就是必须用数据库事务的原因。下面是一个Service层方法的典型写法public boolean doConsume(int memberId, int goodsId, int quantity) { Connection conn null; try { conn JdbcUtil.getConnection(); conn.setAutoCommit(false); Goods goods goodsDao.findById(conn, goodsId); Member member memberDao.findById(conn, memberId); if (goods null || member null) { conn.rollback(); return false; } BigDecimal amount goods.getPrice() .multiply(BigDecimal.valueOf(quantity)); int gainedPoints amount.divideToIntegralValue( BigDecimal.valueOf(goods.getGivePointsRate())).intValue(); String saleNo generateSaleNo(); saleRecordDao.insert(conn, memberId, goods, quantity, amount, gainedPoints, saleNo); memberDao.updateBalanceAndPoints(conn, memberId, amount.negate(), gainedPoints); integralLogDao.insert(conn, memberId, 1, gainedPoints, member.getCurrentPoints(), member.getCurrentPoints() gainedPoints, saleNo); conn.commit(); return true; } catch (Exception e) { if (conn ! null) { try { conn.rollback(); } catch (SQLException ex) { ex.printStackTrace(); } } e.printStackTrace(); return false; } finally { JdbcUtil.close(conn); } }逻辑说明先把连接设为手动提交这样三条SQL执行完再统一commit中途任何一步抛异常都走rollback余额、积分、流水全部还原。DAO方法都接收同一个Connection对象保证它们在同一个事务里执行。这是裸JDBC写事务的标准姿势也是Spring的Transactional在底层做的事。如果能把这段逻辑讲清楚比死记硬背“事务四个特性ACID”有说服力得多。积分计算有个细节容易算错。商品单价10.5元购买1件give_points_rate是1每满1元送1积分正确结果是10积分而不是10.5。我用BigDecimal.divideToIntegralValue来做整数除法它只取商的整数部分。用amount.intValue()直接截断也能得到10但涉及多件商品合计金额时先除再取整和先取整再除结果可能不同统一用BigDecimal保证规则一致。如果希望四舍五入就改用divide(rate, RoundingMode.HALF_UP)这是积分规则的问题代码里留好注释即可。4.3 DAO 细节PreparedStatement 与 try-with-resources 的标准写法DAO层是项目里最机械但也最容易写错的部分。一个合格的查询方法应该长这样public Member findByUsername(String username) { String sql SELECT id, username, password, real_name, phone, balance, total_points, current_points, level, status FROM t_member WHERE username ?; try (Connection conn JdbcUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setString(1, username); try (ResultSet rs ps.executeQuery()) { if (rs.next()) { Member m new Member(); m.setId(rs.getInt(id)); m.setUsername(rs.getString(username)); m.setPassword(rs.getString(password)); m.setRealName(rs.getString(real_name)); m.setPhone(rs.getString(phone)); m.setBalance(rs.getBigDecimal(balance)); m.setTotalPoints(rs.getInt(total_points)); m.setCurrentPoints(rs.getInt(current_points)); m.setLevel(rs.getInt(level)); m.setStatus(rs.getInt(status)); return m; } } } catch (SQLException e) { e.printStackTrace(); } return null; }逻辑说明try-with-resources可以让Connection、PreparedStatement、ResultSet在方法结束时自动关闭不需要在finally里手工写三行close。注意Connection和PreparedStatement是外层tryResultSet是内层try这样rs在ps之后关闭符合JDBC规范的建议关闭顺序。如果项目用老式finally手动关闭最容易漏关ResultSet运行几次之后连接耗尽数据库就卡住了。参数说明ResultSet手动映射到Member对象这段代码看起来繁琐但它是理解“ORM到底在做什么”的入口。可以对比MyBatis或Spring JDBC怎么通过反射和映射文件省掉这些setXXX答辩时这一对比就是加分项。至于为什么用BigDecimal接balance而不是double因为数据库里的DECIMAL转成double会丢失精度接成BigDecimal才和金额计算闭环。5. 避坑记录Javaweb 毕业设计本地运行的 5 个高频故障这一节写的是这类项目里反复出现的故障按“现象→原因→解决”的方式记录。每一条都是实际排过的照着处理能省掉半天起步的排查时间。5.1 Tomcat 10 启动老项目整个应用全是 404现象Tomcat正常启动日志没有报错但访问项目路径永远是404连登录页都出不来。原因Tomcat 10把Servlet API的包名从javax.servlet换成了jakarta.servlet老项目的代码和依赖JAR里全是javaxTomcat找不到对应的Servlet类Web应用注册失败却不影响主进程启动。解决把Tomcat换成8.5或9.0项目不用改一行代码。如果你用IDEA的Tomcat集成驱动换版本后记得在Run Configuration里重新选Application Server。5.2 MySQL 8 驱动加载失败与时区导致的连接超时现象点击登录后页面卡住十几秒然后报Communications link failure或者Cannot create PoolableConnectionFactory。原因有两种一种是用5.1驱动去连MySQL 8驱动类名com.mysql.jdbc.Driver在MySQL 8的服务端协议下握手失败另一种是驱动版本对了但JDBC URL少了serverTimezone参数。解决MySQL 8对应使用mysql-connector-java 8.0.x驱动类名改成com.mysql.cj.jdbc.DriverURL写成jdbc:mysql://localhost:3306/supermarket?useSSLfalseserverTimezoneAsia/ShanghaicharacterEncodingUTF-8。characterEncodingUTF-8也要带上否则中文乱码接踵而至。5.3 中文乱码JSP、请求、响应、数据库四个环节一起查现象登录页面正常显示中文但登录后用户名里的中文变成问号或者往数据库里插入中文后读出乱码。原因乱码是编码链断裂。解决要按四个环节排查JSP页面头部设置pageEncodingUTF-8且charsetUTF-8Servlet接收参数前调用req.setCharacterEncoding(UTF-8)返回前设置resp.setCharacterEncoding(UTF-8)数据库连接URL带characterEncodingUTF-8建表时用utf8mb4。项目里常出现只改了数据库没改Servlet或者只改了页面没改连接URL四个地方必须同时满足。检查方法很直接提交一个含中文的用户名断点打在Servlet第一行看请求参数是否正常再在DAO层打点看SQL里的中文是否正常哪个环节断了改哪里。5.4 运行十几分钟后卡死连接没关导致的连接池耗尽现象刚启动时一切正常点几次查询后整个应用越来越慢最后Tomcat完全无响应重启后恢复。原因项目里用的是裸JDBCJdbcUtil的close方法只关了Statement没关Connection甚至ResultSet也没关。每查一次就漏一个连接数据库最大连接数耗尽后所有线程都在等连接。解决把所有DAO方法的连接收进try-with-resources见4.3的写法。如果项目配置了数据库连接池比如DBCP或C3P0检查连接池的最大连接数和获取超时时间把maxTotal设到20起步、maxWaitMillis设5000避免无限等待。这一步也对应了mysql的数据库连接池的核心思路——连接要复用而不是用完不还。5.5 登录页能打开一提交就 500建表脚本根本没执行干净现象页面正常所有静态资源都能出来一点登录就500Tomcat日志报SQLException: Table supermarket.t_member doesnt exist。原因压缩包里的SQL脚本可能包含多段内容你只执行了一部分或者脚本用了CREATE DATABASE IF NOT EXISTS但表和代码不在同一个库下。解决重新执行完整脚本执行完看表列表确认四张核心表都出现。另一个隐性问题很多毕业设计源码里的SQL脚本建的表不带前缀代码里却写成了t_member两边对不上。检查配置文件里没有表名全局替换规则就按代码里的实际表名建表或者统一改代码。最麻烦的一种是SQL里塞了存储过程建表成功但存储过程编译失败Navicat默认继续执行看起来“成功”实际后续语句全没跑解决方式是分步执行每段单独看错误。6. 答辩前的一小时加固三个改动换来体面演示到了这步项目能跑、主链路能走通但距离“敢演示”还差一点火候。每次帮人过这种项目我都会做三个改动每个都能在一小时内完成收益却很明显。先改登录接口把SQL拼接换成预编译把密码从明文或裸MD5提到加盐摘要。具体做法是给MD5Util加一个带盐的重载用用户名或注册时间作盐登录校验时用相同规则重新计算后比对。老师问“MD5能破解吗”你答“裸MD5查彩虹表就能撞加盐后每个用户摘要不同彩虹表失效”这是一个完整的安全讨论。再改数据库连接方式把裸JDBC换成连接池。不用引一堆依赖用Tomcat自带的JNDI数据源就够在context.xml里配一个Resource代码里通过InitialContext查找DataSource每次getConnection都从池里拿。这段改动至少解决两件事——连接不泄漏导致的卡死以及把数据库密码从Java源码挪到配置文件里。答辩时指着一行driverClassName讲连接复用和超时控制比讲一百行页面代码都实用。最后做一轮完整回环验证。把数据库里四张表清空重新执行脚本按“注册新会员→后台添加商品→会员消费→查看积分增长→兑换扣减积分”的顺序全流程走一遍。每走一步去数据库看对应表有没有产生预期记录。这轮验证的价值在于你亲手确认了每个功能都有数据闭环而不是停留在“页面点开没报错”。很多演示事故都发生在真实提交数据的那一刻提前走一遍就能把事故留在机房。我自己的血泪教训是毕业设计答辩前夜发现消费1000元以上的订单积分算错原因是积分计算直接用金额intValue()截断没考虑打折后的角分。后来统一改成BigDecimal处理并把积分规则抽成了可配置的字段项目才算真正收工。这一套改下来你的超市会员管理系统就不只是源码包里的半成品而是你自己能讲清每条链路、每张表来龙去脉的作品。希望帮到你。本文还有配套的精品资源点击获取