ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Java Web超市管理系统课设:从数据库建表到Servlet接口的完整实现方案

Java Web超市管理系统课设:从数据库建表到Servlet接口的完整实现方案 简介这是一套基于Java Web的超市管理系统完整项目资料面向计算机相关专业的在校学生、课程设计或毕业设计需求者以及希望以真实项目练手的学习者。项目已通过导师指导与答辩评审获得95分评价代码经过测试可正常运行适合直接用于数据库课程设计、作业提交或项目初期立项演示。压缩包共125个文件约3.05MB以30个Java源文件、20个JSP页面、23个JavaScript脚本为核心辅以XML配置、CSS样式、图片资源及properties配置并包含war包与说明文档结构完整、层次清晰。目前已有40人学习关注。读者可获得一套可运行的超市管理业务源码涵盖商品、供应商等模块的前后端实现便于理解Java Web分层架构与数据库交互逻辑同时可参考其目录组织与配置方式在现有基础上修改扩展功能或作为课程设计、毕业设计的参考模板降低从零搭建的成本。1. 超市管理系统课设从建表到答辩一套能跑通的 Java Web 方案做过数据库课程设计的人都清楚最磨人的不是写代码而是把需求、E-R 图、建表脚本、后端接口、前端页面和答辩文档串成一条线。这套基于 Java Web 的超市管理系统核心就是围绕商品、库存、订单、会员、员工这几张表把数据库增删改查完整落地成一个能演示的 Web 应用。它适合正在做数据库课程设计的学生也适合想用一个小型项目把 JDBC、Servlet、MySQL 串起来练手的开发者。我见过太多人卡在“表建好了但接口对不上”或者“功能能跑但文档写不出来”这两步所以这篇笔记按实际交付顺序来拆先定表结构再搭后端然后接前端最后补文档和答辩要点。整套方案不依赖复杂框架用最朴素的 Servlet JSP MySQL 就能跑通重点是让你在有限时间内交出一个逻辑自洽、演示不翻车的课设。2. 数据库设计先定死超市管理系统的表结构与字段取舍2.1 五张核心表怎么划商品、库存、订单、会员、员工超市管理系统的数据库设计最怕一上来就追求大而全。我一般会先把业务闭环画出来员工登录系统 → 商品录入并绑定分类 → 库存随入库出库变动 → 会员下单生成订单 → 订单明细扣减库存。围绕这个闭环五张表足够撑起一个课设的体量。商品表product是主表字段包括商品编号、名称、分类、进价、售价、单位、状态。这里有个取舍分类到底用单独的分类表还是直接存字符串如果课设要求体现范式就拆一张category表用外键关联如果时间紧直接在product里存分类名也能跑但答辩时容易被问“这算第几范式”。我的建议是拆出来多写一个下拉框查询而已但 E-R 图会好看很多。库存表inventory单独建而不是把库存数量塞进商品表。原因是库存变动需要记录流水入库和出库要留痕。表里放商品 ID、当前数量、预警阈值、最后更新时间。订单表orders和订单明细表order_item必须分开这是数据库课设的经典考点一对多关系。orders存订单号、会员 ID、员工 ID、总金额、下单时间、状态order_item存订单号、商品 ID、数量、单价。会员表member和员工表employee分开字段差异大硬合在一起反而别扭。提示表名不要用order它是 SQL 关键字MySQL 里虽然可以用反引号但 JDBC 拼接时容易翻车直接用orders省心。2.2 建表 SQL 与索引主键、外键、唯一约束一次写对下面这段 SQL 是我在课设里反复用过的建表脚本字段类型和约束都按 MySQL 8.0 来写。注意字符集用utf8mb4否则商品名里的生僻字或表情符号会报错。CREATE DATABASE IF NOT EXISTS supermarket DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE supermarket; -- 商品分类表 CREATE TABLE category ( cid INT PRIMARY KEY AUTO_INCREMENT, cname VARCHAR(50) NOT NULL UNIQUE ) ENGINEInnoDB; -- 商品表 CREATE TABLE product ( pid INT PRIMARY KEY AUTO_INCREMENT, pname VARCHAR(100) NOT NULL, cid INT NOT NULL, purchase_price DECIMAL(10,2) NOT NULL DEFAULT 0.00, sale_price DECIMAL(10,2) NOT NULL DEFAULT 0.00, unit VARCHAR(10) DEFAULT 件, status TINYINT DEFAULT 1 COMMENT 1上架 0下架, CONSTRAINT fk_product_category FOREIGN KEY (cid) REFERENCES category(cid) ) ENGINEInnoDB; -- 库存表 CREATE TABLE inventory ( iid INT PRIMARY KEY AUTO_INCREMENT, pid INT NOT NULL UNIQUE, quantity INT NOT NULL DEFAULT 0, warn_threshold INT DEFAULT 10, update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, CONSTRAINT fk_inventory_product FOREIGN KEY (pid) REFERENCES product(pid) ) ENGINEInnoDB; -- 会员表 CREATE TABLE member ( mid INT PRIMARY KEY AUTO_INCREMENT, mname VARCHAR(50) NOT NULL, phone VARCHAR(20) UNIQUE, points INT DEFAULT 0, reg_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB; -- 员工表 CREATE TABLE employee ( eid INT PRIMARY KEY AUTO_INCREMENT, ename VARCHAR(50) NOT NULL, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(100) NOT NULL, role VARCHAR(20) DEFAULT cashier ) ENGINEInnoDB; -- 订单表 CREATE TABLE orders ( oid INT PRIMARY KEY AUTO_INCREMENT, order_no VARCHAR(32) NOT NULL UNIQUE, mid INT, eid INT NOT NULL, total_amount DECIMAL(10,2) NOT NULL DEFAULT 0.00, order_time DATETIME DEFAULT CURRENT_TIMESTAMP, status TINYINT DEFAULT 1 COMMENT 1已完成 0已取消, CONSTRAINT fk_orders_member FOREIGN KEY (mid) REFERENCES member(mid), CONSTRAINT fk_orders_employee FOREIGN KEY (eid) REFERENCES employee(eid) ) ENGINEInnoDB; -- 订单明细表 CREATE TABLE order_item ( oiid INT PRIMARY KEY AUTO_INCREMENT, oid INT NOT NULL, pid INT NOT NULL, quantity INT NOT NULL, unit_price DECIMAL(10,2) NOT NULL, CONSTRAINT fk_item_orders FOREIGN KEY (oid) REFERENCES orders(oid), CONSTRAINT fk_item_product FOREIGN KEY (pid) REFERENCES product(pid) ) ENGINEInnoDB; -- 常用索引 CREATE INDEX idx_product_cid ON product(cid); CREATE INDEX idx_orders_time ON orders(order_time); CREATE INDEX idx_item_oid ON order_item(oid);这段脚本的逻辑说明category和product是一对多product和inventory是一对一orders和order_item是一对多。外键约束保证不会出现孤儿记录但要注意插入顺序——先插分类再插商品再插库存。索引建在product.cid、orders.order_time、order_item.oid上因为后台按分类筛选商品、按时间查订单、按订单查明细是最频繁的操作。参数说明DECIMAL(10,2)表示总共 10 位小数 2 位金额字段不要用FLOAT否则对账时会出现0.30000000000000004这种玄学数字。status用TINYINT而不是BOOLEANMySQL 里BOOLEAN本质也是TINYINT(1)但显式写TINYINT更直观。ON UPDATE CURRENT_TIMESTAMP让库存表在更新数量时自动刷新时间省去手动维护。2.3 初始化数据与测试用例让演示不空屏建完表直接跑系统页面全是空的演示效果很差。我一般会准备一份初始化数据脚本至少插 3 个分类、10 个商品、5 个会员、2 个员工、3 条订单。INSERT INTO category (cname) VALUES (饮料), (零食), (日用品); INSERT INTO product (pname, cid, purchase_price, sale_price, unit) VALUES (可乐 330ml, 1, 2.00, 3.50, 瓶), (薯片 原味, 2, 4.50, 7.00, 袋), (抽纸 3层, 3, 6.00, 9.90, 提); INSERT INTO inventory (pid, quantity, warn_threshold) VALUES (1, 100, 20), (2, 50, 10), (3, 30, 5); INSERT INTO member (mname, phone, points) VALUES (张三, 13800000001, 120), (李四, 13800000002, 80); INSERT INTO employee (ename, username, password, role) VALUES (管理员, admin, 123456, admin), (收银员小王, wang, 123456, cashier);测试用例要覆盖正常下单扣库存、库存不足时提示、会员积分累加、按分类查商品。这些用例直接写进课设文档的“测试章节”答辩时老师问“你怎么验证的”你就有东西说。3. 后端接口落地Servlet JDBC 把增删改查串起来3.1 数据库连接池怎么配别再用 DriverManager 裸连课设里最常见的翻车点就是每个 Servlet 里写一遍DriverManager.getConnection()演示时点几下就报Too many connections。正确做法是用连接池。Tomcat 自带 DBCP但配置略繁琐我一般直接引HikariCP或者用Druid。如果课设不允许引额外 jar就用 Tomcat 的context.xml配 JNDI 数据源。下面以 HikariCP 为例写一个工具类DBUtilimport com.zaxxer.hikari.HikariConfig; import com.zaxxer.hikari.HikariDataSource; import java.sql.Connection; import java.sql.SQLException; public class DBUtil { private static HikariDataSource dataSource; static { HikariConfig config new HikariConfig(); config.setJdbcUrl(jdbc:mysql://localhost:3306/supermarket?useSSLfalseserverTimezoneAsia/ShanghaicharacterEncodingutf8); config.setUsername(root); config.setPassword(your_password); config.setMaximumPoolSize(10); config.setMinimumIdle(2); config.setConnectionTimeout(30000); config.setIdleTimeout(600000); dataSource new HikariDataSource(config); } public static Connection getConnection() throws SQLException { return dataSource.getConnection(); } public static void close(AutoCloseable... resources) { for (AutoCloseable r : resources) { if (r ! null) { try { r.close(); } catch (Exception e) { /* 忽略 */ } } } } }逻辑说明静态代码块在类加载时初始化连接池整个应用生命周期只执行一次。maximumPoolSize设 10 足够课设演示connectionTimeout设 30 秒避免数据库没启动时线程一直挂起。close方法用可变参数统一关闭Connection、PreparedStatement、ResultSet减少重复代码。参数说明serverTimezoneAsia/Shanghai必须加否则 MySQL 8.0 的DATETIME字段取出来会差 8 小时订单时间全乱。useSSLfalse在本地演示时关掉避免证书警告。characterEncodingutf8配合数据库的utf8mb4防止中文乱码。3.2 商品模块的增删改查一个 Servlet 写透商品管理是课设里最核心的模块包含列表查询、新增、修改、删除、按分类筛选。我一般用一个ProductServlet配合action参数来分发而不是写五个 Servlet。WebServlet(/product) public class ProductServlet extends HttpServlet { Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String action req.getParameter(action); if (action null) action list; switch (action) { case list: list(req, resp); break; case delete: delete(req, resp); break; case edit: edit(req, resp); break; default: list(req, resp); } } Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setCharacterEncoding(UTF-8); String action req.getParameter(action); if (add.equals(action)) add(req, resp); else if (update.equals(action)) update(req, resp); } private void list(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String cid req.getParameter(cid); String sql SELECT p.pid, p.pname, c.cname, p.purchase_price, p.sale_price, p.unit, p.status, i.quantity FROM product p LEFT JOIN category c ON p.cid c.cid LEFT JOIN inventory i ON p.pid i.pid; if (cid ! null !cid.isEmpty()) { sql WHERE p.cid ?; } sql ORDER BY p.pid DESC; ListProductVO list new ArrayList(); try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { if (cid ! null !cid.isEmpty()) { ps.setInt(1, Integer.parseInt(cid)); } try (ResultSet rs ps.executeQuery()) { while (rs.next()) { ProductVO vo new ProductVO(); vo.setPid(rs.getInt(pid)); vo.setPname(rs.getString(pname)); vo.setCname(rs.getString(cname)); vo.setPurchasePrice(rs.getBigDecimal(purchase_price)); vo.setSalePrice(rs.getBigDecimal(sale_price)); vo.setUnit(rs.getString(unit)); vo.setStatus(rs.getInt(status)); vo.setQuantity(rs.getInt(quantity)); list.add(vo); } } } catch (SQLException e) { throw new ServletException(查询商品失败, e); } req.setAttribute(products, list); req.getRequestDispatcher(/product_list.jsp).forward(req, resp); } private void add(HttpServletRequest req, HttpServletResponse resp) throws IOException { String pname req.getParameter(pname); int cid Integer.parseInt(req.getParameter(cid)); BigDecimal purchasePrice new BigDecimal(req.getParameter(purchasePrice)); BigDecimal salePrice new BigDecimal(req.getParameter(salePrice)); String unit req.getParameter(unit); int quantity Integer.parseInt(req.getParameter(quantity)); String sqlProduct INSERT INTO product (pname, cid, purchase_price, sale_price, unit) VALUES (?, ?, ?, ?, ?); String sqlInventory INSERT INTO inventory (pid, quantity) VALUES (?, ?); try (Connection conn DBUtil.getConnection()) { conn.setAutoCommit(false); try (PreparedStatement ps1 conn.prepareStatement(sqlProduct, Statement.RETURN_GENERATED_KEYS); PreparedStatement ps2 conn.prepareStatement(sqlInventory)) { ps1.setString(1, pname); ps1.setInt(2, cid); ps1.setBigDecimal(3, purchasePrice); ps1.setBigDecimal(4, salePrice); ps1.setString(5, unit); ps1.executeUpdate(); int pid; try (ResultSet rs ps1.getGeneratedKeys()) { rs.next(); pid rs.getInt(1); } ps2.setInt(1, pid); ps2.setInt(2, quantity); ps2.executeUpdate(); conn.commit(); } catch (SQLException e) { conn.rollback(); throw e; } } catch (SQLException e) { throw new RuntimeException(新增商品失败, e); } resp.sendRedirect(product?actionlist); } private void delete(HttpServletRequest req, HttpServletResponse resp) throws IOException { int pid Integer.parseInt(req.getParameter(pid)); try (Connection conn DBUtil.getConnection()) { conn.setAutoCommit(false); try (PreparedStatement ps1 conn.prepareStatement(DELETE FROM inventory WHERE pid ?); PreparedStatement ps2 conn.prepareStatement(DELETE FROM product WHERE pid ?)) { ps1.setInt(1, pid); ps1.executeUpdate(); ps2.setInt(1, pid); ps2.executeUpdate(); conn.commit(); } catch (SQLException e) { conn.rollback(); throw e; } } catch (SQLException e) { throw new RuntimeException(删除商品失败, e); } resp.sendRedirect(product?actionlist); } private void edit(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { int pid Integer.parseInt(req.getParameter(pid)); String sql SELECT * FROM product WHERE pid ?; try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setInt(1, pid); try (ResultSet rs ps.executeQuery()) { if (rs.next()) { req.setAttribute(product, rs); req.getRequestDispatcher(/product_edit.jsp).forward(req, resp); } } } catch (SQLException e) { throw new ServletException(查询商品详情失败, e); } } private void update(HttpServletRequest req, HttpServletResponse resp) throws IOException { int pid Integer.parseInt(req.getParameter(pid)); String pname req.getParameter(pname); int cid Integer.parseInt(req.getParameter(cid)); BigDecimal purchasePrice new BigDecimal(req.getParameter(purchasePrice)); BigDecimal salePrice new BigDecimal(req.getParameter(salePrice)); String unit req.getParameter(unit); String sql UPDATE product SET pname ?, cid ?, purchase_price ?, sale_price ?, unit ? WHERE pid ?; try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setString(1, pname); ps.setInt(2, cid); ps.setBigDecimal(3, purchasePrice); ps.setBigDecimal(4, salePrice); ps.setString(5, unit); ps.setInt(6, pid); ps.executeUpdate(); } catch (SQLException e) { throw new RuntimeException(更新商品失败, e); } resp.sendRedirect(product?actionlist); } }逻辑说明doGet处理查询和删除doPost处理新增和修改这是 HTTP 语义的常见做法。list方法用LEFT JOIN把分类名和库存数量一起查出来避免在 JSP 里再查一次数据库。add方法用事务保证商品和库存同时插入Statement.RETURN_GENERATED_KEYS拿到自增主键再插入库存表。delete方法先删库存再删商品因为有外键约束顺序反了会报错。参数说明req.setCharacterEncoding(UTF-8)必须放在doPost第一行否则表单里的中文会乱码。conn.setAutoCommit(false)开启事务conn.commit()提交conn.rollback()回滚。BigDecimal接收金额参数避免浮点精度问题。3.3 订单与库存联动一个事务里扣减库存订单模块是课设的加分项也是最能体现数据库能力的地方。下单时要同时做三件事插入订单主表、插入订单明细、扣减库存。这三步必须在一个事务里否则会出现“订单生成了但库存没扣”的脏数据。public void createOrder(int mid, int eid, ListCartItem items) throws SQLException { String sqlOrder INSERT INTO orders (order_no, mid, eid, total_amount) VALUES (?, ?, ?, ?); String sqlItem INSERT INTO order_item (oid, pid, quantity, unit_price) VALUES (?, ?, ?, ?); String sqlStock UPDATE inventory SET quantity quantity - ? WHERE pid ? AND quantity ?; try (Connection conn DBUtil.getConnection()) { conn.setAutoCommit(false); try { BigDecimal total BigDecimal.ZERO; for (CartItem item : items) { total total.add(item.getUnitPrice().multiply(new BigDecimal(item.getQuantity()))); } int oid; try (PreparedStatement ps conn.prepareStatement(sqlOrder, Statement.RETURN_GENERATED_KEYS)) { ps.setString(1, generateOrderNo()); ps.setInt(2, mid); ps.setInt(3, eid); ps.setBigDecimal(4, total); ps.executeUpdate(); try (ResultSet rs ps.getGeneratedKeys()) { rs.next(); oid rs.getInt(1); } } try (PreparedStatement psItem conn.prepareStatement(sqlItem); PreparedStatement psStock conn.prepareStatement(sqlStock)) { for (CartItem item : items) { psItem.setInt(1, oid); psItem.setInt(2, item.getPid()); psItem.setInt(3, item.getQuantity()); psItem.setBigDecimal(4, item.getUnitPrice()); psItem.addBatch(); psStock.setInt(1, item.getQuantity()); psStock.setInt(2, item.getPid()); psStock.setInt(3, item.getQuantity()); if (psStock.executeUpdate() 0) { throw new SQLException(库存不足商品ID item.getPid()); } } psItem.executeBatch(); } conn.commit(); } catch (SQLException e) { conn.rollback(); throw e; } } }逻辑说明sqlStock的WHERE quantity ?是关键它保证库存不足时更新影响行数为 0直接抛异常触发回滚。psItem.addBatch()和executeBatch()批量插入明细减少数据库往返。订单号用时间戳加随机数生成保证唯一。参数说明generateOrderNo()可以用System.currentTimeMillis() String.format(%04d, new Random().nextInt(10000))。CartItem是自定义的购物车项类包含商品 ID、数量、单价。事务隔离级别用默认的REPEATABLE READ即可课设场景不需要调整。4. 前端页面与文档让演示流畅、答辩有料4.1 JSP 页面怎么写列表、表单、分页三件套课设的前端不需要多漂亮但必须能跑通。我一般用 JSP JSTL 写三个页面product_list.jsp、product_edit.jsp、order_list.jsp。列表页用c:forEach遍历后端传来的products表单页用form提交到product?actionadd或update。分页是课设的常见要求但很多人写得很复杂。我的做法是在 SQL 里用LIMIT和OFFSET后端算好总页数前端只渲染上一页、下一页和页码。SELECT p.pid, p.pname, c.cname, p.sale_price, i.quantity FROM product p LEFT JOIN category c ON p.cid c.cid LEFT JOIN inventory i ON p.pid i.pid ORDER BY p.pid DESC LIMIT ? OFFSET ?;参数说明LIMIT是每页条数OFFSET是(当前页码 - 1) * 每页条数。总条数用SELECT COUNT(*)单独查一次。注意LIMIT和OFFSET在 MySQL 里不能用?占位符直接传需要用PreparedStatement.setInt()设置。注意JSP 里不要写 Java 代码块% %用 JSTL 和 EL 表达式否则答辩时老师会问“你这算 MVC 吗”。4.2 课设文档结构从需求到测试的完整链条文档是课设的另一半分数。我一般按这个结构写封面、目录、需求分析、概念设计E-R 图、逻辑设计表结构、物理设计建表 SQL、系统实现关键代码截图、测试测试用例表、总结与展望。E-R 图用 draw.io 画导出 PNG 插入文档。表结构用表格列出字段名、类型、约束、说明。测试章节要写具体测试功能、输入数据、预期结果、实际结果、是否通过。比如“库存不足时下单”输入数量 999预期提示“库存不足”实际提示一致通过。这样答辩时老师翻到这一页基本不会为难你。4.3 答辩演示脚本三分钟讲清一个闭环答辩演示不要从头到尾点菜单而是讲一个完整故事登录 → 新增商品 → 查看库存 → 会员下单 → 库存扣减 → 订单查询。每一步说清楚“我点了什么、系统做了什么、数据库哪张表变了”。提前准备好数据避免现场录入浪费时间。如果老师问“并发下单会不会超卖”你就答“我在库存扣减的 SQL 里加了quantity ?条件并且用事务保证原子性”这是加分项。5. 避坑与排查课设里最容易翻车的五个地方5.1 中文乱码从数据库到 JSP 的完整链路现象商品名在数据库里正常页面上显示???。原因数据库字符集、连接 URL、JSP 页面编码、Servlet 请求编码四处不一致。解决数据库用utf8mb4JDBC URL 加characterEncodingutf8JSP 顶部加% page contentTypetext/html;charsetUTF-8 %doPost第一行加req.setCharacterEncoding(UTF-8)。四处都对了乱码基本消失。5.2 外键约束报错插入顺序和删除顺序现象新增商品时报Cannot add or update a child row。原因product.cid引用了category.cid但插入时分类不存在。解决先插分类再插商品。删除时反过来先删库存和订单明细再删商品和订单主表。如果课设不要求严格外键可以在建表时去掉FOREIGN KEY但答辩时会被问“你怎么保证数据一致性”所以建议保留。5.3 连接池耗尽Too many connections现象演示时点几下就报Too many connections。原因每次请求都DriverManager.getConnection()没有关闭连接。解决用连接池并且在finally块里关闭Connection、PreparedStatement、ResultSet。如果用了try-with-resources就不需要手动关。检查代码里有没有漏关的地方尤其是ResultSet。5.4 事务不回滚库存扣了但订单没生成现象下单后库存减少了但订单列表里没有记录。原因conn.setAutoCommit(false)之后没有commit()或者异常被吞了没有rollback()。解决把commit()放在try块最后rollback()放在catch块里。确保异常往上抛不要catch了什么都不做。5.5 时间差 8 小时serverTimezone 没配现象订单时间比实际时间早 8 小时。原因MySQL 8.0 默认时区是 UTCJDBC 没指定时区。解决JDBC URL 加serverTimezoneAsia/Shanghai。如果已经建了表DATETIME字段的历史数据不会自动修正需要手动UPDATE或重新插入。6. 进阶技巧把课设变成能写进简历的项目课设做完只是及格想拿高分或者写进简历还得加一点进阶内容。我一般会从三个方向挑一个加 Redis 缓存商品列表、加拦截器做登录校验、加日志记录关键操作。这里说一个最实用的用过滤器做统一编码和登录校验。WebFilter(/*) public class AuthFilter implements Filter { Override public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { HttpServletRequest request (HttpServletRequest) req; HttpServletResponse response (HttpServletResponse) resp; request.setCharacterEncoding(UTF-8); response.setContentType(text/html;charsetUTF-8); String uri request.getRequestURI(); if (uri.endsWith(login.jsp) || uri.endsWith(login) || uri.contains(css) || uri.contains(js)) { chain.doFilter(req, resp); return; } Object employee request.getSession().getAttribute(employee); if (employee null) { response.sendRedirect(request.getContextPath() /login.jsp); return; } chain.doFilter(req, resp); } }逻辑说明过滤器拦截所有请求先设置编码再判断是否是登录页或静态资源最后检查 session 里有没有员工信息。没有就跳转登录页。这样每个 Servlet 里不用重复写编码和登录校验。参数说明WebFilter(/*)拦截所有路径uri.contains(css)放行静态资源。request.getContextPath()拿到项目上下文路径避免跳转时路径错误。验证方法启动 Tomcat直接访问product?actionlist如果没登录应该跳转到login.jsp登录后再访问应该正常显示列表。如果过滤器不生效检查web.xml的版本是否支持注解或者WebFilter是否被扫描到。我自己的习惯是课设做完后把代码传到 GitHubREADME 里写清楚技术栈、功能列表、启动步骤和截图。面试时直接打开仓库比说“我做过一个超市管理系统”有说服力得多。数据库课设的价值不在于系统多复杂而在于你能不能把需求、设计、实现、测试这条链路讲清楚。希望帮到你。本文还有配套的精品资源点击获取
返回列表