ARTICLE DETAIL

资讯详情

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

JavaWeb在线商城系统实战:Servlet+JSP+JDBC三层架构详解

JavaWeb在线商城系统实战:Servlet+JSP+JDBC三层架构详解 简介本资源是一套高分通过的JavaWeb课程设计实战项目——基于B/S架构的Java在线商城系统面向高校计算机类专业学生及JavaWeb初学者解决课程设计选题难、功能实现不完整、部署调试困难等常见痛点。压缩包共1197个文件涵盖328张JPG/GIF/PNG格式的前端界面截图与图标资源35个核心Java业务类如OrderDao、RegisterServlet、GoodsDetailServlet等26个JSP页面模板46个CSS与34个JS实现响应式交互70个编译后Class文件及1个SQL数据库脚本整体容量78.45MB结构规范、模块清晰开箱即用。已有350人学习下载项目已获95分以上高分评价代码完整、注释充分集成邮箱注册验证、商品浏览、购物车、订单管理等全流程功能配套数据库可一键导入无需额外修改即可本地Tomcat环境运行。1. 这不是“抄作业”而是用 JavaWeb 搭建一个能跑通、能调试、能改需求的真实在线商城系统——95 分课设背后是三层结构落地能力、数据库事务边界意识和 Tomcat 部署细节的硬功夫你下载到的javaweb课程设计-java在线商城系统源码数据库95分以上课设.zip表面看是一份“高分模板”实际是一套完整闭环的 JavaWeb 工程实践切片它不依赖 Spring Boot 自动装配纯 Servlet JSP JDBC 构建强制你直面 request 生命周期、session 管理、JDBC 手动事务控制、MySQL 表关联设计与 SQL 注入防御点。这不是玩具项目——购物车加减库存要扣减商品表余量、下单时要锁订单号生成、用户登录态要跨页面保持、后台管理页要分页查订单……每个功能都卡在 JavaWeb 基础链路的关节上。适合大三刚学完《Java Web 编程》但还没碰过真实业务流的同学它不教你“怎么写 Hello World”而是逼你搞懂“为什么 login.jsp 提交后跳转失败”“为什么 addCartServlet 里 conn.commit() 不生效”“为什么 MySQL 的 datetime 字段存进去变成 0000-00-00”。如果你正被 IDEA 配置 Tomcat 报错卡住、被 JSP 中 EL 表达式不渲染急哭、被数据库外键约束导致 insert 失败反复重装表——这份源码就是你的血泪对照组。2. 从解压到运行用最简路径在本地 IDEA 中跑通这个 JavaWeb 商城系统含 Tomcat 配置、数据库导入、编码校准三步闭环2.1 解压后目录结构解析看清 src、WebContent、db 目录的真实职责解压 ZIP 后你会看到典型传统 JavaWeb 项目结构online-shop/ ├── src/ # Java 源码servlet、dao、bean、util 全在这里 │ ├── servlet/ # 核心控制器LoginServlet、AddCartServlet、OrderServlet... │ ├── dao/ # 数据访问层UserDao、ProductDao、OrderDao —— 注意全是 JDBC 手写无框架 │ ├── bean/ # 实体类User、Product、Order、CartItem —— 字段名与数据库列严格对应 │ └── util/ # 工具类DBUtil封装 getConnection、DateUtil、MD5Util密码加密 ├── WebContent/ # Web 资源根目录等价于现代项目的 src/main/webapp │ ├── index.jsp # 首页含商品轮播、分类导航 │ ├── login.jsp # 登录页form actionlogin → LoginServlet │ ├── product_list.jsp # 商品列表页接收 category 参数查库渲染 │ ├── cart.jsp # 购物车页用 session.getAttribute(cart) 取购物车对象 │ └── WEB-INF/ │ ├── web.xml # 关键Servlet 映射全靠它别漏配 servlet 和 servlet-mapping │ └── lib/ # 必须有 mysql-connector-java-5.1.47.jar注意版本 ├── db/ # 数据库脚本shop.sql建库建表初始数据不是 .sql.gz 或 .bak └── README.md # 通常只有“导入数据库、配置 Tomcat、启动即可”但没说清编码坑提示WebContent是 Eclipse 时代遗留目录名IDEA 默认识别为 Web Root但若新建项目选了 “Dynamic Web Module”默认是src/main/webapp。不要强行改名——直接在 IDEA 项目设置里指定WebContent为 Web Resources Directory 即可否则 JSP 路径全崩。2.2 在 IDEA 中配置 Tomcat 并部署避开端口冲突、乱码、404 三大高频雷区步骤 1确认 Tomcat 版本兼容性该课设基于 Servlet 3.0要求 Tomcat ≥ 7.0推荐 8.5 或 9.0。严禁用 Tomcat 10—— 其 Jakarta EE 命名空间jakarta.servlet.*与源码中javax.servlet.*冲突编译直接报红。下载地址https://tomcat.apache.org/download.cgi选 Binary Distributions → Core → tar.gz / zip。步骤 2IDEA 中添加 Tomcat ServerRun → Edit Configurations → → Tomcat Server → LocalApplication server指向你解压的 Tomcat 根目录如D:\apache-tomcat-8.5.93Deployment → → Artifact → 选择online-shop:war exploded不是 warexploded 模式才能热更新 JSP关键参数在VM options中填入-Dfile.encodingUTF-8解决中文乱码端口检查默认HTTP port: 8080若被占用改8081同时检查JMX port和Debug port是否冲突步骤 3配置项目 SDK 和 FacetFile → Project Structure → ProjectSDK 选 JDK 8课设普遍用 JDK 8JDK 11 会因javax.xml.bind移除报错Modules → online-shop → Dependencies确认mysql-connector-java-5.1.47.jar已 Add as Library右键 jar → Add as LibraryFacets → WebWeb resource directory 选WebContentWeb.xml 选WebContent\WEB-INF\web.xml步骤 4启动前校验 web.xml打开WebContent\WEB-INF\web.xml确认servlet和servlet-mapping成对存在例如servlet servlet-nameLoginServlet/servlet-name servlet-classservlet.LoginServlet/servlet-class /servlet servlet-mapping servlet-nameLoginServlet/servlet-name url-pattern/login/url-pattern /servlet-mapping逻辑说明url-pattern/login/url-pattern表示浏览器访问http://localhost:8080/online-shop/login时Tomcat 将请求交给servlet.LoginServlet类处理。注意路径前缀/online-shop是项目上下文路径Context Path由 IDEA 部署时自动添加不是代码里写的。2.3 导入 MySQL 数据库执行 shop.sql 的实操要点与字符集陷阱步骤 1创建数据库并指定编码不要直接双击shop.sql用 Navicat 打开执行先手动建库避免脚本里CREATE DATABASE语句因权限或编码失败-- 在 MySQL 命令行或 Workbench 中执行 CREATE DATABASE shop CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; USE shop;为什么用 utf8mb4因为课设中商品名、用户昵称可能含 emoji如 、❤️utf8MySQL 中实际是 utf8mb3不支持四字节 Unicode。utf8mb4_unicode_ci排序更准确避免中文检索失效。步骤 2执行 shop.sql 脚本方式一推荐在 MySQL 命令行中 sourcemysql -u root -p mysql USE shop; mysql SOURCE D:/path/to/db/shop.sql;方式二用 MySQL Workbench → File → Run SQL Script → 选中shop.sql→ 设置 Default Schema 为shop→ Execute严禁方式用记事本打开shop.sql复制粘贴到命令行 —— 中文注释易乱码且长 INSERT 语句换行符可能被破坏。步骤 3验证关键表数据是否导入成功执行以下 SQL确认返回非空结果SELECT COUNT(*) FROM user; -- 应有 2~3 条测试用户admin/admin, user1/123 SELECT COUNT(*) FROM product; -- 应有 10 商品category_id 为 1~4手机、电脑、配件、图书 SELECT COUNT(*) FROM order_info; -- 初始为空下单后才有数据参数说明shop.sql中user表密码字段为 MD5 加密如admin密码是21232f297a57a5a743894a0e4a801fc3登录时LoginServlet会调用MD5Util.getMD5(password)匹配不是明文比对。3. 深度拆解核心业务链路从用户登录到生成订单手把手跟踪 request 如何穿透三层架构3.1 登录流程request.getParameter() → DB 查询 → session.setAttribute() 的完整生命周期以login.jsp提交表单为例跟踪一次登录如何走完 MVC 链路① JSP 表单提交View 层login.jsp中关键代码form actionlogin methodpost input typetext nameusername required input typepassword namepassword required button typesubmit登录/button /form逻辑说明actionlogin对应web.xml中url-pattern/login/url-patternTomcat 将 POST 请求路由给LoginServlet。② Servlet 接收并调用 DAOController 层src/servlet/LoginServlet.java核心逻辑protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String username request.getParameter(username); // 获取表单值注意此处未做 trim()空格用户名会登录失败 String password request.getParameter(password); UserDao userDao new UserDao(); User user userDao.login(username, password); // 调用 DAO 方法查库 if (user ! null) { request.getSession().setAttribute(user, user); // 登录成功存入 session response.sendRedirect(index.jsp); // 重定向到首页 } else { request.setAttribute(msg, 用户名或密码错误); request.getRequestDispatcher(login.jsp).forward(request, response); // 转发回登录页保留 msg } }参数说明request.getSession()创建或获取当前会话setAttribute(user, user)将 User 对象存入 session后续所有页面可通过session.getAttribute(user)获取这是实现用户状态保持的核心机制。③ DAO 层执行 SQL 查询Model 层src/dao/UserDao.java中login()方法public User login(String username, String password) { String sql SELECT * FROM user WHERE username ? AND password ?; // 注意这里 password 是明文传入DAO 内部需调用 MD5 加密再查库 Object[] params {username, MD5Util.getMD5(password)}; return (User) queryRunner.query(sql, new BeanHandler(User.class), params); }关键细节queryRunner是 Apache Commons DbUtils 的 QueryRunner 实例封装了 Connection 获取、PreparedStatement 设置、ResultSet 映射。BeanHandlerUser.class自动将结果集映射为 User 对象字段名必须与 User 类属性名完全一致如数据库列user_name对应 Java 属性userName则需在 User 类中加 Column 注解或改列名为username。3.2 购物车添加session 与 domain 对象协同管理临时状态的实战技巧购物车不是存数据库而是存在 session 中的ListCartItem对象。AddCartServlet的核心逻辑如下// 1. 从 session 获取购物车若不存在则新建 HttpSession session request.getSession(); ListCartItem cart (ListCartItem) session.getAttribute(cart); if (cart null) { cart new ArrayList(); session.setAttribute(cart, cart); } // 2. 获取商品 ID 和数量 String pidStr request.getParameter(pid); int quantity Integer.parseInt(request.getParameter(quantity)); // 3. 查商品信息为计算小计 Product product new ProductDao().getProductById(pidStr); // 4. 构建 CartItem 并加入 cart 列表 CartItem item new CartItem(); item.setProduct(product); item.setQuantity(quantity); item.setSubtotal(product.getPrice() * quantity); // 5. 去重逻辑若商品已存在则累加数量 boolean found false; for (CartItem existing : cart) { if (existing.getProduct().getPid().equals(product.getPid())) { existing.setQuantity(existing.getQuantity() quantity); existing.setSubtotal(existing.getProduct().getPrice() * existing.getQuantity()); found true; break; } } if (!found) { cart.add(item); }避坑重点session.setAttribute(cart, cart)后cart是引用传递。后续对cart列表的add()、setQuantity()操作会实时反映在 session 中无需每次 setAttribute。但若你cart new ArrayList()后忘记session.setAttribute()则新购物车不会保存。3.3 订单生成JDBC 手动事务控制的硬核写法与 ACID 保障下单是课设中最考验事务意识的环节需同时插入order_info主表和order_item子表任一失败则全部回滚。OrderServlet中关键代码Connection conn null; try { conn DBUtil.getConnection(); // 从 DBUtil 获取连接 conn.setAutoCommit(false); // 关键关闭自动提交开启事务 // 1. 插入订单主表 String sql1 INSERT INTO order_info VALUES(?, ?, ?, ?, ?, ?, ?); Object[] params1 {oid, uid, address, phone, total, 未付款, new Date()}; queryRunner.update(conn, sql1, params1); // 2. 插入订单项子表遍历购物车 String sql2 INSERT INTO order_item VALUES(?, ?, ?, ?); for (CartItem item : cart) { Object[] params2 {oid, item.getProduct().getPid(), item.getQuantity(), item.getSubtotal()}; queryRunner.update(conn, sql2, params2); } // 3. 清空购物车 session.removeAttribute(cart); conn.commit(); // 全部成功才提交 request.setAttribute(msg, 订单创建成功); } catch (SQLException e) { try { if (conn ! null) conn.rollback(); // 任一异常回滚事务 } catch (SQLException ex) { ex.printStackTrace(); } request.setAttribute(msg, 订单创建失败 e.getMessage()); } finally { try { if (conn ! null) conn.close(); // 手动关闭连接 } catch (SQLException e) { e.printStackTrace(); } }参数说明conn.setAutoCommit(false)是事务起点conn.commit()是成功终点conn.rollback()是失败兜底。注意queryRunner.update(conn, ...)的重载方法必须传入 Connection 对象否则 DbUtils 会自己获取新连接导致事务失效。4. 避坑指南95 分课设里藏着的 5 个致命细节踩中一个就卡死三天4.1 现象启动 Tomcat 后访问http://localhost:8080/online-shop显示 404但http://localhost:8080/online-shop/index.jsp能打开原因web.xml中未配置 welcome-file-listTomcat 不知道默认首页是哪个文件。课设源码常遗漏此配置。解决在web.xml的web-app标签下添加welcome-file-list welcome-fileindex.jsp/welcome-file /welcome-file-list血泪经验很多同学以为index.jsp是默认首页其实 Servlet 规范中必须显式声明。IDEA 的 exploded 部署模式下/路径不自动映射到index.jsp。4.2 现象登录时输入正确账号密码但userDao.login()返回 null日志显示 SQL 执行无异常原因shop.sql中user表的password字段是 MD5 加密值如admin对应21232f297a57a5a743894a0e4a801fc3而LoginServlet中userDao.login(username, password)传入的是明文密码DAO 层未调用MD5Util.getMD5(password)就直接查库。解决检查UserDao.login()方法确保密码参数已加密// ✅ 正确写法 public User login(String username, String password) { String encryptedPwd MD5Util.getMD5(password); // 必须加密 String sql SELECT * FROM user WHERE username ? AND password ?; return (User) queryRunner.query(sql, new BeanHandler(User.class), username, encryptedPwd); }4.3 现象添加商品到购物车后cart.jsp显示数量为 0 或价格为 0原因CartItem类中product属性是Product对象但Product的price字段类型为double而数据库product表中price列是DECIMAL(10,2)。DbUtils 的BeanHandler默认将DECIMAL映射为BigDecimal若Product类中price是double类型则赋值失败类型不匹配导致price0.0。解决修改Product.java中 price 类型为BigDecimal并重写 getter/setterprivate BigDecimal price; public BigDecimal getPrice() { return price; } public void setPrice(BigDecimal price) { this.price price; }玄学提醒BigDecimal是金融计算唯一安全类型double会有精度丢失如 0.10.2≠0.3课设虽不涉及支付但养成习惯。4.4 现象MySQL 导入shop.sql后中文显示为??商品名、用户昵称全乱码原因MySQL 服务端、数据库、表、连接四层编码不统一。常见于 Windows 系统下shop.sql文件本身是 GBK 编码但 MySQL 默认用 utf8mb4。解决用 Notepad 打开shop.sql→ 编码 → 转为 UTF-8 无 BOM 格式 → 保存确认 MySQL 配置文件my.ini中有[client] default-character-setutf8mb4 [mysqld] character-set-serverutf8mb4 collation-serverutf8mb4_unicode_ci重启 MySQL 服务重新执行CREATE DATABASE shop CHARACTER SET utf8mb4...和SOURCE shop.sql4.5 现象在 IDEA 中修改 JSP 后刷新页面内容未更新仍显示旧版原因Tomcat 的 exploded 部署模式下JSP 会被编译成.java和.class文件缓存在work/Catalina/localhost/online-shop/目录。修改 JSP 后Tomcat 有时不自动重新编译。解决方式一推荐Run → Reload project快捷键 CtrlF5强制重载方式二删除tomcat/work/Catalina/localhost/online-shop/下所有文件重启 Tomcat方式三一劳永逸在tomcat/conf/web.xml中找到servlet标签下的jsp配置添加init-param param-namedevelopment/param-name param-valuetrue/param-value /init-param5. 进阶改造把课设升级为可演示、可答辩、可延展的工程级项目含三层解耦、日志增强、SQL 注入防御5.1 将 DAO 层升级为接口实现分离为未来切换 MyBatis 做铺垫当前 DAO 是具体类UserDao.java紧耦合 DbUtils。改为接口定义便于后期替换// src/dao/UserDao.java接口 public interface UserDao { User login(String username, String password); boolean register(User user); ListUser findAll(); } // src/dao/impl/UserDaoImpl.java实现类 public class UserDaoImpl implements UserDao { private QueryRunner queryRunner new QueryRunner(); Override public User login(String username, String password) { String sql SELECT * FROM user WHERE username ? AND password ?; try { return (User) queryRunner.query(DBUtil.getConnection(), sql, new BeanHandler(User.class), username, MD5Util.getMD5(password)); } catch (SQLException e) { throw new RuntimeException(e); } } }价值点UserDao userDao new UserDaoImpl();改为UserDao userDao new UserDaoImpl();后续只需改这一行就能换成MyBatisUserDao或JpaUserDao业务逻辑层Servlet完全不用动。5.2 为关键操作添加日志用 log4j 记录用户行为答辩时展示“可观测性”添加log4j.properties到src/目录log4j.rootLoggerINFO, Console, File log4j.appender.Consoleorg.apache.log4j.ConsoleAppender log4j.appender.Console.layoutorg.apache.log4j.PatternLayout log4j.appender.Console.layout.ConversionPattern%d{yyyy-MM-dd HH:mm:ss} [%t] %-5p %c{1} - %m%n log4j.appender.Fileorg.apache.log4j.RollingFileAppender log4j.appender.File.Filelogs/app.log log4j.appender.File.MaxFileSize10MB log4j.appender.File.MaxBackupIndex5 log4j.appender.File.layoutorg.apache.log4j.PatternLayout log4j.appender.File.layout.ConversionPattern%d{yyyy-MM-dd HH:mm:ss} [%t] %-5p %c{1} - %m%n在LoginServlet中添加日志import org.apache.log4j.Logger; public class LoginServlet extends HttpServlet { private static final Logger logger Logger.getLogger(LoginServlet.class); protected void doPost(...) { String username request.getParameter(username); logger.info(用户尝试登录: username); // 记录登录尝试 if (user ! null) { logger.info(用户登录成功: username); ... } else { logger.warn(用户登录失败: username); ... } } }答辩技巧展示logs/app.log中的记录证明系统具备基础运维能力。“老师您看这里用户 admin 在 14:22:35 登录成功14:23:10 下单全程可追溯。”5.3 防御 SQL 注入对所有request.getParameter()输入做白名单校验LoginServlet中不能只信任username和password// 添加校验工具类 src/util/Validator.java public class Validator { // 用户名字母数字下划线3-20位 public static boolean isValidUsername(String username) { return username ! null username.matches(^[a-zA-Z0-9_]{3,20}$); } // 密码至少8位含大小写字母和数字 public static boolean isValidPassword(String password) { return password ! null password.length() 8 password.matches(^(?.*[a-z])(?.*[A-Z])(?.*\\d).$); } } // 在 LoginServlet 中校验 if (!Validator.isValidUsername(username) || !Validator.isValidPassword(password)) { request.setAttribute(msg, 用户名或密码格式错误); request.getRequestDispatcher(login.jsp).forward(request, response); return; // 提前返回阻止后续执行 }为什么必须做课设虽不连公网但这是 JavaWeb 开发铁律。username or 11这种注入在WHERE username ?中因 PreparedStatement 预编译已免疫但若某处写了WHERE username username字符串拼接就会沦陷。白名单校验是最后一道防线。5.4 数据库连接池化用 Druid 替代 DBUtil 的单连接支撑多用户并发当前DBUtil.getConnection()每次都新建连接高并发下会耗尽 MySQL 连接数。引入 Druidpom.xml添加依赖若用 Maven或lib目录放druid-1.2.16.jar创建src/druid.propertiesdriverClassNamecom.mysql.cj.jdbc.Driver urljdbc:mysql://localhost:3306/shop?useUnicodetruecharacterEncodingutf8mb4serverTimezoneGMT%2B8 usernameroot password123456 initialSize5 maxActive20 minIdle5 maxWait60000修改DBUtil.javapublic class DBUtil { private static DataSource dataSource; static { try { Properties props new Properties(); props.load(DBUtil.class.getClassLoader().getResourceAsStream(druid.properties)); dataSource DruidDataSourceFactory.createDataSource(props); } catch (Exception e) { throw new RuntimeException(e); } } public static Connection getConnection() throws SQLException { return dataSource.getConnection(); } }效果对比单连接模式下10 人同时抢购会排队Druid 连接池可并发处理 20 请求且自动回收空闲连接这才是生产级数据库访问姿势。我带过 3 届课设辅导见过太多同学卡在404或NullPointerException上反复重装环境。后来我定了个死规矩拿到源码第一件事不是跑起来而是打开web.xml数servlet标签个数再打开shop.sql数CREATE TABLE语句行数最后打开LoginServlet找request.getParameter—— 三个数字必须对得上少一个就说明缺配置、少建表、或参数名写错。这招救了至少 27 个濒临放弃的同学。希望帮到你。本文还有配套的精品资源点击获取
返回列表