JSP+MySQL网上商城源码解析:从环境搭建到核心模块实战
2026/9/23 11:31:04 网站建设 项目流程

简介:一份基于JSP与MYSQL实现的网上商城系统源码,适合Java Web初学者、电商项目开发人员以及毕业设计选题者,用于掌握从页面展示到数据库交互的完整电商项目开发流程。系统核心模块包括用户注册登录、商品分类搜索、购物车管理、订单处理及支付接口对接,设计上运用JSP内置对象完成请求响应与会话控制,结合MYSQL实现数据持久化,并融入事务处理、SQL优化以及防SQL注入、XSS攻击等安全开发要点。压缩包为zip格式,大小2.2MB,内含完整的项目工程文件,涵盖前端展示、后端业务处理与数据库交互等层次,可导入开发工具进行部署与二次开发,便于对照学习MVC分层结构、Servlet控制逻辑和JSP视图表现层的协作方式。目前已有56人浏览学习,整体适合作为课程设计、毕业设计或入门电商系统的参考源码。

1. 这套 JSP+MySQL 网上商城源码:适合当毕设,更适合当 Web 入门的第一块完整拼图

如果你正在找 JSP 相关的课程设计或者毕业设计选题,这套基于 JSP + MySQL 的网上商城系统源码属于那种「麻雀虽小、五脏俱全」的项目。它不是一个只写了几个页面的玩具,而是把用户注册登录、商品浏览、购物车、下单支付流程串起来的完整闭环,后端用的是 Java 系最经典的 Servlet + JSP + JDBC 组合,数据库用 MySQL 存储业务数据。对正在学 JavaWeb 的人来说,最大的价值不在于它的技术有多新,而在于它把 JSP 内置对象、Session 状态管理、JDBC 操作、MVC 分层这些课程里分散的知识点,用一条真实的业务线全部串了起来。你把它跑起来之后,再回头去看课本上的 request、response、session,会有完全不一样的理解。本文就带你从环境搭建开始,把数据库初始化、项目部署、核心模块拆解、常见坑全部过一遍。

2. 先把地基打好:JDK、Tomcat 与 MySQL 8.0 的环境搭配

JSP 项目跑不起来,十有八九是环境问题。先强调一个结论:这套源码虽然是老技术栈,但完全可以在新版 JDK 和 MySQL 上运行,前提是版本搭配要合理,不要盲目追新。

2.1 版本选型:JDK 8 + Tomcat 8.5 是最稳的组合

不少同学上来就装 JDK 17、Tomcat 10,然后发现 JSP 页面直接 500,第一反应是源码有问题,其实大概率是版本不兼容。Tomcat 10 之后把 Jakarta EE 命名空间替换了原来的 javax,老项目里的import javax.servlet.*全部失效,而市面上绝大多数 JSP 教学源码都是基于 javax 写的,硬要适配需要全局替换包名,工程量不小。

我一般建议用这套组合:

  • JDK 8:JavaWeb 老项目的标准运行时,兼容性最好
  • Tomcat 8.5:支持 Servlet 3.1 和 JSP 2.3,完美匹配 JDK 8
  • MySQL 5.7 或 8.0:都可以,驱动注意选对版本就行

如果你的机器上已经装了更高版本的 JDK,不用卸载,把 JDK 8 装成独立目录,然后在 IDE 里单独指定项目的 JDK 版本即可。Tomcat 8.5 是绿色版,解压就能用,不涉及系统服务安装,省去很多权限问题。

2.2 MySQL 建库与导入:注意字符集和 SQL 文件编码

拿到源码包后,一般有一个.sql文件,这是建库脚本。在导入之前,建议先手动建一个空库,再指定库导入,避免脚本里没有CREATE DATABASE语句导致报错。字符集要统一用utf8mb4,否则商品名称里的中文可能显示成乱码。

# 登录 MySQL,root 密码换成你自己的 mysql -u root -p # 建库,统一字符集 CREATE DATABASE shop DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; # 退出后用 source 导入 mysql -u root -p shop < /path/to/shop.sql

如果 SQL 文件没有指定默认库,也可以先USE shop;再执行source。导入后建议看一眼表数量,一般商城系统至少有用户表、商品表、订单表、订单明细表、分类表这几张,少了任何一张都说明脚本执行不完整。检查完表再随便查一条商品记录,确认中文数据正常,再进入下一步。

2.3 数据库连接配置:改三个参数就能连上

找到项目里的 JDBC 连接文件,通常是db.properties或者DBUtil.java,把 URL、用户名、密码改成你自己的。这里有个容易忽略的坑:MySQL 8.0 的驱动类名和 5.7 不一样,8.0 要用com.mysql.cj.jdbc.Driver,并且 URL 里要加时区参数。

// db.properties 配置示例 jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/shop?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai jdbc.username=root jdbc.password=123456

这段配置里,characterEncoding=utf8是保证中文不出乱码的关键,serverTimezone=Asia/Shanghai是 MySQL 8.0 必须加的,否则报时区错误。如果你的 MySQL 是 5.7,serverTimezone无所谓,但useSSL=false建议保留。改完这一步,项目部署到 Tomcat 后,打开首页能看到商品列表,就说明数据库连接没问题。

3. 部署与运行:把源码塞进 Tomcat 的正确姿势

很多人卡在「代码在 IDE 里能编译,但浏览器访问 404」。这里要说清楚 IDEW 中直接运行和手动部署到 webapps 是两条不同的路径,坑也不一样。

3.1 项目目录结构:先看懂再动手

JSP 项目有个硬性要求:所有页面和资源必须放在WebContent(有的叫webroot)目录下,WEB-INF下的web.xml是部署描述符,WEB-INF/lib放依赖 jar 包,WEB-INF/classes放编译后的 class 文件。如果你用 IDEA 导入源码后没有配置 Web facet,项目根本不会以 Web 项目的方式运行。

shop/ ├── src/ # Java 源代码 │ └── com/shop/ │ ├── bean/ # 实体类 │ ├── dao/ # 数据库访问层 │ ├── servlet/ # 控制器 │ └── util/ # 工具类 ├── WebContent/ │ ├── WEB-INF/ │ │ ├── web.xml # 部署配置 │ │ └── lib/ # JDBC 驱动等 jar 包 │ ├── css/ │ ├── js/ │ ├── index.jsp # 首页 │ ├── login.jsp # 登录页 │ ├── cart.jsp # 购物车 │ └── ...

在这个结构里,bean对应数据模型,dao负责和 MySQL 交互,servlet处理请求并跳转 JSP 页面。看到这个结构,你就可以确认这套源码是标准的 MVC 分层,而不是把所有代码都塞进 JSP 页面里的那种教学残次品。

3.2 IDEA 导入与 Artifact 配置

IDEA 社区版对 Web 项目支持不完整,建议使用 IntelliJ IDEA Ultimate。导入流程本身不复杂,但 Artifact 配置容易漏。

打开项目结构,在 Artifacts 里确认输出类型是Web Application: Exploded,然后设置lib目录为 jar 包目录。如果不做这一步,Tomcat 运行时找不到 MySQL 驱动,会直接报ClassNotFoundException: com.mysql.jdbc.Driver。配置完成后,在 Run Configuration 里选 Tomcat Server,Deployment 选这个 Artifact,Application context 设置成/shop,对应访问路径就是http://localhost:8080/shop/

这里有一个常见操作误区:很多人喜欢把整个项目文件夹直接拷贝到 Tomcat 的webapps目录下部署。这种方式也可以,但前提是源码已经被编译过,并且WEB-INF/classes里有完整的 class 文件。源码包里的src目录只是源文件,不经过编译直接丢进 webapps 是跑不起来的。我一般会在 IDEA 里先跑通,再用 IDEA 的 Build Artifact 功能导出 war 包,最后丢到 Tomcat 的 webapps 下做生产环境模拟。

3.3 启动与验证:三步确认系统真的活了

Tomcat 启动后,不要急着打开页面,按顺序做三件事:

先看 Tomcat 控制台日志,确认没有ExceptionERROR级别输出。再看数据库连接池是否初始化成功,老项目一般没有连接池而是每次请求新建连接,所以看不到池化日志是正常的。最后访问http://localhost:8080/shop/index.jsp,如果页面能正常显示商品列表和分类导航,就说明部署成功。

提示:如果 Tomcat 启动时报端口被占用,改conf/server.xml里的 Connector port,把 8080 改成 8081 或 8090。改完后所有访问路径也要跟着改,IDE 里的 URL 也要同步更新。

4. 核心代码拆解:登录校验、购物车 Session 与订单事务

部署成功后,重点就是读懂核心代码。这是源码的真正价值所在,比你自己从零写一遍省太多时间。

4.1 用户登录:JSP 表单到 Servlet 再到 DAO 的完整链路

登录功能看着简单,但它完整地展示了 MVC 三层是怎么协作的。用户在前端输入用户名密码,表单提交到 Servlet,Servlet 获取参数后调用 DAO 层方法查询数据库,DAO 执行 SQL 并返回结果,Servlet 根据结果跳转到不同页面。关键代码分两段:

// LoginServlet.java 的核心逻辑 protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 1. 获取前端提交的参数 String username = request.getParameter("username"); String password = request.getParameter("password"); // 2. 调用 DAO 层验证用户 UserDao userDao = new UserDao(); User user = userDao.findByUsernameAndPassword(username, password); // 3. 根据结果跳转 if (user != null) { // 登录成功,把用户信息写入 session HttpSession session = request.getSession(); session.setAttribute("user", user); response.sendRedirect("index.jsp"); // 重定向到首页 } else { // 登录失败,回传错误信息到登录页 request.setAttribute("errorMsg", "用户名或密码错误"); request.getRequestDispatcher("login.jsp").forward(request, response); } }

这里有两个必考点:sendRedirect是重定向,浏览器地址栏会变化,而forward是服务器内部跳转,地址栏不变。登录成功后用sendRedirect能避免表单重复提交——用户刷新页面时不会再次触发登录请求。登录失败用forward是为了把errorMsg这个请求属性带到 JSP 页面显示。

再看 DAO 层的 SQL 写法:

// UserDao.java 中查询用户的方法 public User findByUsernameAndPassword(String username, String password) { Connection conn = null; PreparedStatement ps = null; ResultSet rs = null; User user = null; try { conn = DBUtil.getConnection(); // 使用 PreparedStatement 防止 SQL 注入 String sql = "SELECT * FROM user WHERE username=? AND password=?"; ps = conn.prepareStatement(sql); ps.setString(1, username); ps.setString(2, password); rs = ps.executeQuery(); if (rs.next()) { user = new User(); user.setId(rs.getInt("id")); user.setUsername(rs.getString("username")); } } catch (Exception e) { e.printStackTrace(); } finally { DBUtil.closeAll(rs, ps, conn); // 释放资源 } return user; }

参数用?占位并通过setString传值,这是防止 SQL 注入的标准写法。如果源码里用的是字符串拼接 SQL,那就说明这份代码质量较差,建议你手动改成PreparedStatement的方式,这本身就是一个很好的改造练习。

4.2 购物车实现:为什么用 Session 而不是数据库表

购物车是这套系统里最有学习价值的部分。它的设计思路是:用户没登录也可以添加商品到购物车,登录后购物车数据依然保留。实现上用的是 Session 存储购物车数据,而不是在数据库里建购物车表。

购物车的数据结构是一个Map<Integer, CartItem>,key 是商品 ID,value 是购物车条目对象,包含商品信息和数量。这样设计的好处是读写快,不需要频繁操作数据库,缺点也明显——Session 过期后购物车数据就清了,用户关掉浏览器再打开购物车是空的。

// CartServlet.java 中添加到购物车的方法 protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { int goodsId = Integer.parseInt(request.getParameter("goodsId")); // 从 Session 中获取购物车 HttpSession session = request.getSession(); Map<Integer, CartItem> cart = (Map<Integer, CartItem>) session.getAttribute("cart"); if (cart == null) { cart = new HashMap<Integer, CartItem>(); // 首次使用创建购物车 session.setAttribute("cart", cart); } // 判断商品是否已在购物车中 if (cart.containsKey(goodsId)) { CartItem item = cart.get(goodsId); item.setCount(item.getCount() + 1); // 数量加一 } else { // 查询商品信息并封装成购物车条目 GoodsDao goodsDao = new GoodsDao(); Goods goods = goodsDao.findById(goodsId); CartItem item = new CartItem(); item.setGoods(goods); item.setCount(1); cart.put(goodsId, item); } // 重定向回购物车页面 response.sendRedirect("cart.jsp"); }

这段代码体现了 Session 的一个核心特性:跨请求共享数据。每次请求都会带着 JSESSIONID 这个 Cookie,Tomcat 根据它找到对应的 Session 对象,所以购物车能在多次请求之间保持状态。理解了这一点,你对「HTTP 是无状态协议」这句话的体会会深很多。

4.3 订单提交:事务保证多表操作的一致性

下单模块是这套系统里事务处理的代表。一次下单要操作三个表:订单表插入一条订单记录,订单明细表插入多条商品记录,商品表扣减库存。任何一个环节失败,数据就会不一致——比如订单生成了但库存没扣,或者库存扣了但订单明细缺失。

// OrderServlet.java 中提交订单的方法 public boolean createOrder(Order order, List<OrderItem> items) { Connection conn = null; try { conn = DBUtil.getConnection(); // 关闭自动提交,手动开启事务 conn.setAutoCommit(false); // 第一步:插入订单记录 String sqlOrder = "INSERT INTO orders (user_id, total_price, create_time, status) VALUES (?,?,?,?)"; PreparedStatement psOrder = conn.prepareStatement(sqlOrder, Statement.RETURN_GENERATED_KEYS); psOrder.setInt(1, order.getUserId()); psOrder.setDouble(2, order.getTotalPrice()); psOrder.setTimestamp(3, new Timestamp(System.currentTimeMillis())); psOrder.setInt(4, 1); // 1 表示待付款 psOrder.executeUpdate(); // 获取自增主键,作为订单号 ResultSet keys = psOrder.getGeneratedKeys(); int orderId = 0; if (keys.next()) { orderId = keys.getInt(1); } // 第二步:插入订单明细 for (OrderItem item : items) { String sqlItem = "INSERT INTO order_item (order_id, goods_id, price, count) VALUES (?,?,?,?)"; PreparedStatement psItem = conn.prepareStatement(sqlItem); psItem.setInt(1, orderId); psItem.setInt(2, item.getGoodsId()); psItem.setDouble(3, item.getPrice()); psItem.setInt(4, item.getCount()); psItem.executeUpdate(); } // 第三步:扣减库存 for (OrderItem item : items) { String sqlStock = "UPDATE goods SET stock=stock-? WHERE id=? AND stock>=?"; PreparedStatement psStock = conn.prepareStatement(sqlStock); psStock.setInt(1, item.getCount()); psStock.setInt(2, item.getGoodsId()); psStock.setInt(3, item.getCount()); int rows = psStock.executeUpdate(); if (rows == 0) { throw new RuntimeException("库存不足"); } } conn.commit(); // 全部成功,提交事务 return true; } catch (Exception e) { try { conn.rollback(); // 任何一个环节失败,回滚所有操作 } catch (SQLException ex) { ex.printStackTrace(); } e.printStackTrace(); return false; } finally { DBUtil.closeAll(null, null, conn); } }

这个方法的精妙之处在于conn.setAutoCommit(false)之后,所有 SQL 都在同一个事务里执行,全部成功才commit,任何一个异常都会触发rollback。库存扣减的 SQL 用了WHERE stock>=?条件,这一步不仅判断库存是否充足,而且利用数据库行锁防止了并发下的超卖问题。如果你将来自己写商城,这个写法可以直接抄。

5. 避坑指南:JSP 商城系统运行中常见的五个翻车现场

这套源码在部署和二次开发阶段,我帮人远程调过不少问题,总结出五个高频坑。每个都是「现象 → 原因 → 解决」的结构,以后你跑任何 JSP 老项目都能用到。

坑一:页面中文全部变成问号

现象:浏览器打开 JSP 页面,商品名称、分类导航全是???或者乱码字符。原因:JSP 页面编码、数据库连接编码、MySQL 表编码三者不一致。这是 JSP 老项目的通病,涉及三层编码。解决:先看 JSP 文件头部有没有<%@ page contentType="text/html; charset=UTF-8" %>,没有就加上;再看 MySQL 连接 URL 里有没有characterEncoding=utf8;最后查数据库表的字符集,用SHOW CREATE TABLE goods;确认是utf8mb4。三层统一后刷新页面即可。

坑二:启动时报com.mysql.jdbc.Driver找不到

现象:Tomcat 启动没有问题,但一访问数据库相关页面就报 ClassNotFoundException。原因:JDBC 驱动 jar 包没有放到WEB-INF/lib目录下,或者放的位置不对。IDEA 里常见的是 jar 只存在于项目外部库,没有打包进 Artifact。解决:把mysql-connector-java-xx.jar文件手动拷贝到WebContent/WEB-INF/lib/下,然后在 IDEA 项目结构里选中它右键 Add as Library,再重新 Build Artifact。如果用的是 MySQL 8.0,驱动包版本要 8.x 且驱动类名改成com.mysql.cj.jdbc.Driver

坑三:点击登录按钮地址栏变成login.jsp?username=xxx

现象:表单提交后 URL 变成了 GET 方式,密码明文显示在地址栏里,而且登录逻辑不生效。原因:<form>标签里的method属性没有设置成post,默认就是 GET。解决:把登录表单、注册表单、搜索表单的method统一改成post。这个坑在练习项目里非常常见,因为它不影响表单提交这个动作本身,只是提交方式不对,所以很多人查半天才发现。顺手检查一下 Servlet 里对应的doGetdoPost方法,如果doPost没写逻辑,改成method="post"后照样 404 或 405。

坑四:Tomcat 正常启动但页面 404

现象:IDEA 控制台显示 Tomcat 已启动,但浏览器访问任何 JSP 页面都是 404。原因:项目没有成功部署到 Tomcat,或者 Artifact 输出目录不对。最常见的是 Deployment 里没有添加 Artifact,或者 Application context 配置成了/,访问路径对不上。解决:在 Run Configuration 的 Deployment 标签页点加号,选 Artifact,然后确认 Application context 是/shop,访问http://localhost:8080/shop/index.jsp。这里有一个判断技巧:如果 Tomcat 访问首页能看到默认的 Tomcat 欢迎页,说明 Tomcat 本身没问题,问题一定出在部署配置上。

坑五:修改 JSP 页面后刷新无变化

现象:改了 JSP 里的标题或者样式,浏览器刷新看到的还是老内容。原因:JSP 页面被 Tomcat 编译成了 Servlet class 文件,浏览器缓存了旧页面。这是 JSP 项目开发中很常见的假象。解决:先强制刷新浏览器,快捷键 Ctrl+Shift+R;如果还没变,检查 IDEA 里 JSP 文件的位置——是不是改错了文件,比如源码包里有index.jsphome.jsp,首页跳转可能指向的不是你以为的那个。再不行就重启 Tomcat,因为 Tomcat 只有在调试模式下才会热部署 JSP 修改,很多情况下重启是最快的解决方式。

6. 二次开发思路:给这套商城系统加三个实用功能

源码跑通之后,推荐做三个方向的改造练习。这几个方向都是真实开发中绕不过去的场景,做完之后你对这套系统的理解会超过 90% 的下载用户。

第一个改造:把用户密码从明文改成 MD5 加盐存储

源码里如果密码直接存明文,属于安全隐患。你可以写一个简单的加密工具类,注册时对密码加密后再存库,登录时对输入的密码做同样的加密再比对。加盐的方式是生成一个随机字符串拼接在密码后面再加密,加密结果和盐一起存入数据库。这样即使数据库泄露,也无法直接还原出原始密码。动手改的时候要注意:已有用户的数据要兼容处理,可以写一个迁移 SQL 脚本,批量把旧密码更新成新格式,或者干脆清空用户表重新注册——测试阶段我一般选后者。

第二个改造:接入 Druid 连接池替换 JDBC 直连

这份源码的 DAO 层每次请求都新建数据库连接,在高并发场景下性能很差。Druid 是阿里开源的数据库连接池,用它替换直连代码量不大:引入两个 jar 包,改配置文件,把DBUtil里的获取连接逻辑换成从 Druid 数据源获取。改造后你可以通过 Druid 的监控页面看到 SQL 执行次数、慢查询记录,这个监控页面是 Druid 自带的,访问/druid/index.html就能看到。这一步改造做完,你再去看 Spring 框架里的数据源配置就会觉得很轻松。

第三个改造:给评论功能加分页

商城系统一般有商品评价模块,评价列表不分页的话数据多了页面会非常卡。用 MySQL 的LIMIT offset, pageSize实现分页是最简单的做法:

-- 查询第 2 页,每页 10 条评论 SELECT * FROM comment WHERE goods_id = 12 ORDER BY create_time DESC LIMIT 10, 10;

JSP 页面做一个分页导航条,显示上一页、下一页、总页数和当前页码,页码参数通过 URL 传递,比如comment.jsp?goodsId=12&page=2。Servlet 里先查总数算出总页数,再按当前页查对应数据,把结果封装成PageBean传给 JSP。这个功能练习的价值在于让你理解 Web 应用中分页的标准流程,它和框架无关,SSM 项目和 Spring Boot 项目里的分页底层原理完全一样。

这套系统我实际带着学生跑过很多次,每一次都会发现新的问题,但也正因为如此,每跑通一个功能模块,你对 JSP、Session、JDBC 这些基础知识的掌握就扎实一层。从最初复制代码改参数,到后来能动手把评论分页、密码加密这些功能自己写出来,这个过程本身就是技术能力在涨。希望这篇笔记能帮你在 JSP 项目上少走一段弯路。

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询