ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

JSP超市管理系统实战:MyEclipse+MySQL经典组合开发指南

JSP超市管理系统实战:MyEclipse+MySQL经典组合开发指南 简介这是一套基于JSP与Java技术栈的超市管理系统源码采用B/S架构配合MyEclipse与Tomcat开发适合正在学习Java Web、需要课程设计或毕业设计参考的初学者与在校学生。系统围绕超市日常业务展开涵盖人事管理、销售管理、进货管理、库存管理和客户管理五大模块具体实现职工与供货商信息的增删改查、商品销售盘点、进货与库存信息维护以及用户添加、删除和密码修改等功能有助于理解JSP页面与后台逻辑的协作方式。压缩包为rar格式大小约1.06MB内含完整源代码与MySQL数据库文件数据库脚本为jspsupermarket.sql管理员默认账号密码均为admin数据库配置集中在DBO.java中导入后即可在本地环境运行调试。目前已有1023人学习下载可作为掌握JSP数据库编程与Web项目结构的实践素材。1. 从零搭一套 JSP 超市管理系统为什么 MyEclipse MySQL 这套老组合还在被反复用如果你现在打开招聘网站搜“Java Web 实习”会发现一个有意思的现象不少中小企业的内部管理系统后台依然是 JSP Servlet MySQL 的结构开发工具还停留在 MyEclipse。这不是技术倒退而是存量项目的现实——一套跑了七八年的超市进销存系统只要还能稳定出单、管库存、算毛利没人愿意花几十万重构成 Spring Boot。所以当你拿到“JSP超市管理系统 MyEclipse 开发 MySQL 数据库 Web 结构 Java 编程”这个题目时本质上要解决的是用最经典的 Java Web 三层结构把商品、库存、销售、用户这几条业务线跑通并且能在 MyEclipse 里一键部署到 Tomcat 上直接访问。这套方案适合两类人一是课程设计或毕设需要快速交付一个“能演示、能答辩、能改”的完整系统二是刚学完 Java 基础、JSP 入门想找一个真实业务场景把 JDBC、Servlet、Session、MVC 串起来练手的人。它不追求高并发也不玩微服务核心价值在于结构清晰、依赖少、调试直观。你只要把 MySQL 装好、MyEclipse 配好 Tomcat、把数据库连接参数改对剩下的就是按业务表写 DAO 和 JSP 页面。下面我按实际搭建顺序把选型理由、建库建表、项目结构、核心代码和踩坑点全部拆开讲。2. 环境选型与项目骨架MyEclipse 里怎么建一个能跑的 JSP Web 项目2.1 为什么是 MyEclipse 而不是 IDEATomcat 版本怎么选先说工具。MyEclipse 本质上是 Eclipse 加了一堆 Java EE 插件它对 JSP 的语法提示、Tomcat 集成、数据库浏览器做得比较“傻瓜化”特别适合不熟悉 Maven 目录结构的新手。你不需要手动配 web.xml 的 servlet 映射右键新建 Servlet 它会帮你生成你也不需要自己下 Tomcat 插件装好 MyEclipse 后直接在 Preferences 里指向 Tomcat 安装目录就行。常见做法是选 MyEclipse 2017 或 2019 版本配合 JDK 8 和 Tomcat 8.5这三个版本凑在一起兼容性最稳不会出现“JDK 17 编译报错、Tomcat 10 的 javax 包变成 jakarta 包”这种让人想摔键盘的问题。Tomcat 版本这里有个血泪经验Tomcat 10 之后Servlet 的包名从javax.servlet改成了jakarta.servlet而网上绝大多数 JSP 超市管理系统的教程和源码还是javax的写法。如果你不小心下了 Tomcat 10导入项目后满屏红叉改包名能改到你怀疑人生。所以直接锁定 Tomcat 8.5 或 9.0别追新。MySQL 选 5.7 或 8.0 都行8.0 的驱动类名变成了com.mysql.cj.jdbc.Driver连接 URL 要加时区和 SSL 参数这个后面会细说。项目骨架用 MyEclipse 的“Web Project”模板创建不要用 Maven 项目。因为 MyEclipse 的 Web Project 会自动生成WebRoot目录或者叫WebContent里面放 JSP、CSS、JS 和WEB-INF/lib。你只需要把 MySQL 驱动 jar 包丢进WEB-INF/libMyEclipse 会自动把它加到构建路径。目录结构大致是这样SupermarketSystem/ ├── src/ │ └── com/supermarket/ │ ├── dao/ // 数据库操作层 │ ├── entity/ // 实体类对应数据库表 │ ├── servlet/ // 控制层处理请求 │ └── util/ // 数据库连接工具类 ├── WebRoot/ │ ├── css/ │ ├── js/ │ ├── WEB-INF/ │ │ ├── web.xml │ │ └── lib/ // mysql-connector-java.jar │ ├── login.jsp │ ├── index.jsp │ └── ...这个结构就是最朴素的 MVCJSP 负责显示Servlet 负责接收请求和跳转DAO 负责跟 MySQL 打交道entity 是数据载体。别小看它土调试的时候哪一层出问题一目了然比 Spring 那一套注解满天飞要直观得多。2.2 数据库建库建表商品、库存、销售、用户四张核心表超市管理系统的业务逻辑绕不开四张表用户表登录和权限、商品表商品基础信息、库存表入库出库记录、销售表订单和明细。这里给出一套可以直接在 Navicat 或 MySQL 命令行里执行的建表 SQL字段类型和约束都按实际业务设好避免后期改表。-- 创建数据库字符集用 utf8mb4 防止中文乱码 CREATE DATABASE supermarket_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE supermarket_db; -- 用户表区分管理员和普通收银员 CREATE TABLE t_user ( user_id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(100) NOT NULL, real_name VARCHAR(50), role INT DEFAULT 0 COMMENT 0-收银员 1-管理员, create_time DATETIME DEFAULT CURRENT_TIMESTAMP ); -- 商品表商品基础信息 CREATE TABLE t_product ( product_id INT PRIMARY KEY AUTO_INCREMENT, product_name VARCHAR(100) NOT NULL, category VARCHAR(50), price DECIMAL(10,2) NOT NULL DEFAULT 0.00, stock INT NOT NULL DEFAULT 0, unit VARCHAR(20) DEFAULT 件, remark VARCHAR(255) ); -- 销售记录表每笔订单的主记录 CREATE TABLE t_sale ( sale_id INT PRIMARY KEY AUTO_INCREMENT, sale_no VARCHAR(32) NOT NULL UNIQUE, total_amount DECIMAL(10,2) NOT NULL DEFAULT 0.00, user_id INT, sale_time DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (user_id) REFERENCES t_user(user_id) ); -- 销售明细表订单里每个商品的购买数量和小计 CREATE TABLE t_sale_detail ( detail_id INT PRIMARY KEY AUTO_INCREMENT, sale_id INT NOT NULL, product_id INT NOT NULL, quantity INT NOT NULL DEFAULT 1, subtotal DECIMAL(10,2) NOT NULL DEFAULT 0.00, FOREIGN KEY (sale_id) REFERENCES t_sale(sale_id), FOREIGN KEY (product_id) REFERENCES t_product(product_id) );建表时注意几个参数price和total_amount用DECIMAL(10,2)而不是FLOAT因为金额计算用浮点数会出现0.1 0.2 0.30000000000000004这种玄学问题对账的时候差一分钱都麻烦。stock字段设了NOT NULL DEFAULT 0这样新商品入库前库存就是 0不会出现 null 导致库存扣减报错。字符集统一用utf8mb4不然商品名里带个 emoji 或者生僻字就存不进去。2.3 数据库连接工具类JDBC 参数怎么配才不报错在com.supermarket.util包下新建DBUtil.java这是整个系统跟 MySQL 打交道的唯一入口。很多教程喜欢把连接写死在每个 DAO 里那是给自己挖坑——改个密码要翻十几个文件。正确做法是抽成工具类用静态代码块加载驱动用DriverManager获取连接。package com.supermarket.util; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; public class DBUtil { // MySQL 8.0 驱动类名5.x 版本用 com.mysql.jdbc.Driver private static final String DRIVER com.mysql.cj.jdbc.Driver; // 连接 URL指定时区、关闭 SSL 警告、允许批量操作 private static final String URL jdbc:mysql://localhost:3306/supermarket_db ?useUnicodetruecharacterEncodingutf8 serverTimezoneAsia/ShanghaiuseSSLfalseallowPublicKeyRetrievaltrue; private static final String USER root; private static final String PASSWORD 123456; static { try { Class.forName(DRIVER); } catch (ClassNotFoundException e) { throw new RuntimeException(MySQL 驱动加载失败检查 WEB-INF/lib 下是否有驱动 jar, e); } } public static Connection getConnection() throws SQLException { return DriverManager.getConnection(URL, USER, PASSWORD); } // 统一关闭资源顺序ResultSet - Statement - Connection public static void close(Connection conn, Statement stmt, ResultSet rs) { try { if (rs ! null) rs.close(); } catch (SQLException e) { e.printStackTrace(); } try { if (stmt ! null) stmt.close(); } catch (SQLException e) { e.printStackTrace(); } try { if (conn ! null) conn.close(); } catch (SQLException e) { e.printStackTrace(); } } }这段代码里最关键的是 URL 参数。serverTimezoneAsia/Shanghai不加的话MySQL 8.0 会报The server time zone value ?D1ú±ê×?ê±?? is unrecognized因为系统时区识别不了中文时区名。useSSLfalse是为了关掉 SSL 连接警告本地开发不需要加密。allowPublicKeyRetrievaltrue是 MySQL 8.0 默认密码插件caching_sha2_password要求的不加会报Public Key Retrieval is not allowed。这三个参数少一个项目就跑不起来很多新手卡在这一步以为是代码写错了其实是连接串没配全。提示如果你用的是 MySQL 5.7驱动类名改成com.mysql.jdbc.DriverURL 里去掉serverTimezone和allowPublicKeyRetrieval也能跑但建议统一按 8.0 的写法配方便以后升级。3. 核心业务代码落地登录、商品管理、销售结算三条线怎么写3.1 登录模块Servlet 接收表单 Session 保存用户状态登录是系统的入口逻辑很简单JSP 页面提交用户名密码Servlet 调 DAO 查数据库查到就把用户信息存进HttpSession然后跳转到主页查不到就返回登录页并提示错误。这里用login.jsp做表单LoginServlet做处理。!-- login.jsp 核心表单部分 -- form actionLoginServlet methodpost 用户名input typetext nameusername requiredbr 密码input typepassword namepassword requiredbr input typesubmit value登录 /form %-- 显示错误信息 --% % String msg (String) request.getAttribute(msg); if (msg ! null) { out.print(span stylecolor:red msg /span); } %// LoginServlet.java 核心 doPost 方法 protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding(UTF-8); String username request.getParameter(username); String password request.getParameter(password); UserDao userDao new UserDao(); User user userDao.login(username, password); if (user ! null) { // 登录成功把用户对象存入 Session request.getSession().setAttribute(currentUser, user); response.sendRedirect(index.jsp); } else { request.setAttribute(msg, 用户名或密码错误); request.getRequestDispatcher(login.jsp).forward(request, response); } }这里有两个容易翻车的点。第一request.setCharacterEncoding(UTF-8)必须写在getParameter之前否则中文用户名会乱码。第二登录成功后用sendRedirect而不是forward因为重定向会改变浏览器地址栏用户刷新页面不会重复提交表单如果用了forward刷新时浏览器会提示“是否重新提交表单”体验很差。Session 里存的currentUser对象可以在后续所有 JSP 页面里通过session.getAttribute(currentUser)取出来用来显示当前登录人或者做权限判断。3.2 商品管理DAO 层增删改查与分页查询的 SQL 写法商品管理是超市系统里操作最频繁的模块包含商品列表、添加商品、修改商品、删除商品。DAO 层用PreparedStatement防止 SQL 注入查询列表时加分页逻辑避免商品多了以后页面卡死。// ProductDao.java 核心方法片段 public ListProduct getProductList(int page, int pageSize) { ListProduct list new ArrayList(); String sql SELECT * FROM t_product ORDER BY product_id DESC LIMIT ?, ?; try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setInt(1, (page - 1) * pageSize); ps.setInt(2, pageSize); ResultSet rs ps.executeQuery(); while (rs.next()) { Product p new Product(); p.setProductId(rs.getInt(product_id)); p.setProductName(rs.getString(product_name)); p.setPrice(rs.getBigDecimal(price)); p.setStock(rs.getInt(stock)); list.add(p); } } catch (SQLException e) { e.printStackTrace(); } return list; } public int getProductCount() { String sql SELECT COUNT(*) FROM t_product; try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql); ResultSet rs ps.executeQuery()) { if (rs.next()) return rs.getInt(1); } catch (SQLException e) { e.printStackTrace(); } return 0; }分页 SQL 用LIMIT ?, ?第一个参数是偏移量(page - 1) * pageSize第二个参数是每页条数。比如第 1 页每页 10 条偏移量是 0第 2 页偏移量是 10。getProductCount用来算总页数在 JSP 里配合循环生成页码链接。注意rs.getBigDecimal(price)对应实体类里的BigDecimal类型不要用getDouble否则金额精度会丢。添加商品和修改商品的 SQL 用INSERT INTO和UPDATE删除用DELETE。这里不展开贴全部代码核心是每个方法都要用 try-with-resources 自动关闭连接避免连接池耗尽。如果你用的是原生 JDBC 而不是连接池每次请求都新建连接并发一高 MySQL 就会报Too many connections。小系统用DBCP或C3P0连接池能缓解但配置稍复杂课程设计级别直接用 JDBC 也够用只要记得关连接。3.3 销售结算事务控制库存扣减与订单写入销售结算是整个系统里唯一需要事务的地方。收银员选好商品、输入数量、点击结算后台要做三件事往t_sale插一条订单主记录、往t_sale_detail插明细、扣减t_product的库存。这三步必须在一个事务里要么全成功要么全回滚否则会出现“订单生成了但库存没扣”或者“库存扣了但订单没生成”的对账灾难。// SaleService.java 结算核心逻辑 public boolean checkout(ListCartItem cart, int userId) { Connection conn null; try { conn DBUtil.getConnection(); conn.setAutoCommit(false); // 开启事务 // 1. 插入订单主记录 String saleNo S System.currentTimeMillis(); BigDecimal total BigDecimal.ZERO; for (CartItem item : cart) { total total.add(item.getPrice().multiply(new BigDecimal(item.getQuantity()))); } String saleSql INSERT INTO t_sale(sale_no, total_amount, user_id) VALUES(?,?,?); PreparedStatement psSale conn.prepareStatement(saleSql, Statement.RETURN_GENERATED_KEYS); psSale.setString(1, saleNo); psSale.setBigDecimal(2, total); psSale.setInt(3, userId); psSale.executeUpdate(); ResultSet rsKey psSale.getGeneratedKeys(); int saleId 0; if (rsKey.next()) saleId rsKey.getInt(1); // 2. 插入明细并扣库存 String detailSql INSERT INTO t_sale_detail(sale_id, product_id, quantity, subtotal) VALUES(?,?,?,?); String stockSql UPDATE t_product SET stock stock - ? WHERE product_id ? AND stock ?; PreparedStatement psDetail conn.prepareStatement(detailSql); PreparedStatement psStock conn.prepareStatement(stockSql); for (CartItem item : cart) { psDetail.setInt(1, saleId); psDetail.setInt(2, item.getProductId()); psDetail.setInt(3, item.getQuantity()); psDetail.setBigDecimal(4, item.getPrice().multiply(new BigDecimal(item.getQuantity()))); psDetail.executeUpdate(); psStock.setInt(1, item.getQuantity()); psStock.setInt(2, item.getProductId()); psStock.setInt(3, item.getQuantity()); int affected psStock.executeUpdate(); if (affected 0) { throw new SQLException(库存不足商品ID item.getProductId()); } } conn.commit(); // 全部成功提交事务 return true; } catch (SQLException e) { if (conn ! null) { try { conn.rollback(); } catch (SQLException ex) { ex.printStackTrace(); } } e.printStackTrace(); return false; } finally { if (conn ! null) { try { conn.setAutoCommit(true); conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } }这段代码的关键在于conn.setAutoCommit(false)和conn.commit()之间的所有操作要么一起成功要么在 catch 里rollback()。库存扣减的 SQL 加了AND stock ?条件如果库存不够executeUpdate返回 0直接抛异常触发回滚。这样能防止超卖——两个收银员同时卖最后一件商品只有一个能成功。Statement.RETURN_GENERATED_KEYS用来拿到刚插入的订单主键sale_id再传给明细表做外键关联。整个逻辑写下来不到 60 行但把事务的原子性、一致性都覆盖到了。4. 避坑与排查JSP 超市管理系统最常见的 5 个翻车现场4.1 中文乱码从 JSP 页面到数据库全链路排查现象登录时输入中文用户名后台查不到或者商品名保存到数据库变成????。原因通常有三层JSP 页面没声明编码、Servlet 没设request.setCharacterEncoding、数据库连接 URL 没加characterEncodingutf8。解决方法是逐层检查。JSP 文件第一行加% page contentTypetext/html;charsetUTF-8 languagejava %Servlet 的doPost第一行加request.setCharacterEncoding(UTF-8)DBUtil的 URL 里确认有useUnicodetruecharacterEncodingutf8。三层都对了中文就不会乱。如果还乱检查 MySQL 数据库和表的字符集是不是utf8mb4用SHOW CREATE TABLE t_product;看一眼。4.2 MySQL 8.0 连接报错时区、SSL、公钥检索三连坑现象项目启动后第一次查数据库就抛异常报错信息里带serverTimezone或者Public Key Retrieval is not allowed。原因是 MySQL 8.0 默认时区是 UTC驱动不知道本地时区默认密码插件是caching_sha2_password不允许公钥检索。解决方法是在 JDBC URL 里加上serverTimezoneAsia/ShanghaiuseSSLfalseallowPublicKeyRetrievaltrue。如果加了还报错检查 MySQL 服务是不是没启动或者端口是不是被改成了 3307 之类。用netstat -an | findstr 3306确认端口监听状态。4.3 Tomcat 启动报 404web.xml 版本与 Servlet 映射对不上现象Tomcat 启动没报错但访问http://localhost:8080/SupermarketSystem/login.jsp显示 404。原因可能是web.xml里servlet-mapping的url-pattern写错了或者项目没有部署到 Tomcat 的webapps目录下。在 MyEclipse 里右键项目 → Run As → MyEclipse Server Application它会自动部署。如果手动部署确认WebRoot下的文件被复制到了webapps/SupermarketSystem/。另外web.xml的version属性如果是 2.5 以上Servlet 可以用WebServlet注解代替配置但 MyEclipse 老版本对注解支持不稳定建议还是老老实实写web.xml。4.4 数据库连接池耗尽Too many connections 的应急处理现象系统用了一会儿之后所有数据库操作都卡住MySQL 日志里出现Too many connections。原因是每次请求都DriverManager.getConnection新建连接用完没关连接数越积越多。应急处理是重启 MySQLservice mysql restart但根治方法是确保每个 DAO 方法都在 finally 里关闭连接或者引入 DBCP 连接池。用SHOW PROCESSLIST;能看到当前所有连接如果有一堆Sleep状态的连接就是没关干净。小系统并发不高的话把 MySQL 的max_connections调到 500 也能扛一阵但这不是长久之计。4.5 JSP 页面报 500空指针与类型转换异常定位技巧现象点击某个菜单页面直接 500Tomcat 控制台打印一堆堆栈。最常见的是NullPointerException和NumberFormatException。空指针通常是因为session.getAttribute(currentUser)返回 null 却直接调方法或者 DAO 查询返回 null 没判断。类型转换异常通常是Integer.parseInt(request.getParameter(stock))时参数为空字符串。定位技巧是看堆栈里第一个at com.supermarket.xxx的行号直接定位到代码。在 JSP 里用c:if test${not empty currentUser}做空判断在 Servlet 里用try-catch包住parseInt给默认值 0。别小看这些防御性代码能省下大量调试时间。5. 从能跑到好用三个让 JSP 超市管理系统更抗造的进阶技巧5.1 用 Filter 统一处理编码和登录拦截系统里每个 Servlet 都写一遍request.setCharacterEncoding(UTF-8)太啰嗦而且容易漏。更好的做法是写一个EncodingFilter在web.xml里配成拦截所有请求。同时再加一个LoginFilter判断session里有没有currentUser没有就踢回登录页。这样权限控制和编码处理都集中在一处后期加新页面不用改代码。// EncodingFilter.java public class EncodingFilter implements Filter { public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { req.setCharacterEncoding(UTF-8); resp.setContentType(text/html;charsetUTF-8); chain.doFilter(req, resp); } }web.xml里配filter-mapping的url-pattern为/*所有请求都会先过这个 Filter。登录拦截的 Filter 判断请求路径是不是login.jsp或LoginServlet不是的话检查 Session没有就response.sendRedirect(login.jsp)。这两个 Filter 加起来不到 40 行代码但能让整个系统的健壮性提升一个档次。5.2 商品库存预警用 SQL 定时任务或页面标记实现低库存提醒超市系统里库存低于某个阈值就该提醒补货。最简单的做法是在商品列表页用 JSP 的c:if判断stock 10就显示红色“库存不足”。如果想做得更自动可以在 MySQL 里建一个事件调度器每天凌晨把低库存商品写进一张t_alert表后台登录时弹窗提示。MySQL 事件调度器默认是关闭的用SET GLOBAL event_scheduler ON;开启然后CREATE EVENT定时执行INSERT INTO t_alert SELECT ... WHERE stock 10。这个方案不需要额外写 Java 定时任务适合小系统。5.3 传统 JSP 项目打包 WAR部署到独立 Tomcat 的完整命令在 MyEclipse 里开发完最终要部署到服务器上的独立 Tomcat。右键项目 → Export → WAR file选择导出路径。然后把 WAR 包丢进 Tomcat 的webapps目录启动 Tomcat 会自动解压。如果服务器上的 MySQL 密码和本地不一样记得改DBUtil里的PASSWORD再重新打包。Linux 下启动 Tomcat 用./startup.sh查看日志用tail -f ../logs/catalina.out。如果访问 404检查 WAR 包名和访问路径是否一致比如SupermarketSystem.war解压后目录是SupermarketSystem访问路径就是http://服务器IP:8080/SupermarketSystem/。我自己的习惯是每次改完数据库连接配置先在本地 Tomcat 跑一遍全流程——登录、加商品、结算、查库存确认没问题再导出 WAR。这个习惯帮我省过好几次“本地好好的服务器上连不上数据库”的尴尬。JSP 这套东西虽然老但把事务、Filter、连接池这几个点吃透应付中小型管理系统绰绰有余。希望帮到你。本文还有配套的精品资源点击获取
返回列表