JavaWeb购物商城项目:完整源码+MySQL数据库,从零跑通全流程 简介这是一套面向JavaWeb初学者与进阶练手者的购物商城实战项目源码适合在掌握Servlet、JSP等基础后通过完整业务闭环巩固MVC设计模式与动态代理模式的应用。项目基于Java与MySQL开发涵盖主页热销展示、商品搜索、详情页库存校验、立即购买与购物车增减数量、地址管理与订单确认、重复提交与库存不足响应以及后台会员、商品、订单管理等模块业务链路较为完整。资源包共613个文件约16.85MB包含66个java源文件、50个jsp页面、46个js与41个css前端资源、37个jar依赖以及137个png与62个jpg界面素材另附sql脚本、xml配置和bat/sh启动脚本便于直接导入运行。目前已有21947人学习下载可作为课程设计或毕业设计的参考蓝本帮助读者理解分层结构、数据库交互与前后台联动逻辑快速积累项目排错与调试经验。1. JavaWeb购物商城项目从源码到数据库一套能跑起来的完整落地路径很多人搜「JavaWeb购物商城项目 完整源码Mysql数据库」其实卡在一个很具体的点上网上代码包一抓一大把但真正能在本地跑通、数据库能连上、页面能打开、下单流程能走完的少。我自己第一次搭这类项目时光 MySQL 连接池配置就折腾了一下午最后发现是驱动版本和连接串参数对不上。这个标题背后要解决的不是「有没有代码」而是「怎么让一套 JavaWeb 购物商城从源码到数据库完整跑起来并且知道每一步为什么这么配」。它适合三类人课程设计需要交完整案例的学生、想拿一个真实 CRUD 项目练手的初级开发者、以及需要快速搭一个电商原型做验证的工程师。接下来我按实际搭建顺序把选型、建库、配置、排错和进阶技巧拆开讲每一步都落到能抄的代码和参数上。2. 技术选型与项目骨架为什么这套组合仍然是主流练手方案2.1 分层结构怎么定Servlet JSP JDBC 还是上框架拿到一个 JavaWeb 购物商城项目第一件事不是急着跑而是看它的分层。常见做法是经典三层表现层用 JSP/Servlet业务层用 Service数据访问层用 DAO数据库用 MySQL。这套结构看起来老但它是理解 Web 请求生命周期的好底子。如果你搜到的是带 Spring Boot 的版本那分层会变成 Controller、Service、Mapper、Entity数据库访问换成 MyBatis 或 JPA。两种都能用区别在于纯 Servlet 版本依赖少、启动快、适合搞懂底层Spring Boot 版本配置集中、生态全、适合往生产靠。我一般会先看项目根目录有没有pom.xml。有就是 Maven 项目依赖用坐标管理没有就是传统 Web 项目jar 包在WEB-INF/lib下。这一步决定了你后面怎么导入 IDE。用 IDEA 的话Maven 项目直接 Open 根目录等依赖下载完传统项目要配 Artifacts把 lib 加进输出。很多人卡在「项目能编译但 404」八成是 Artifacts 没配对。2.2 数据库表结构购物商城最少需要哪几张表一个能走通下单流程的购物商城数据库至少要有用户、商品、分类、购物车、订单、订单明细这几张表。下面是我常用的最小建库脚本字段做了精简但保留了关键约束-- 创建数据库字符集用 utf8mb4 支持中文和表情 CREATE DATABASE shop DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE shop; -- 用户表 CREATE TABLE user ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(100) NOT NULL, phone VARCHAR(20), address VARCHAR(200), create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB; -- 商品表 CREATE TABLE product ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(100) NOT NULL, price DECIMAL(10,2) NOT NULL DEFAULT 0.00, stock INT NOT NULL DEFAULT 0, category_id INT, image VARCHAR(200), status TINYINT DEFAULT 1 ) ENGINEInnoDB; -- 订单表 CREATE TABLE orders ( id INT PRIMARY KEY AUTO_INCREMENT, user_id INT NOT NULL, total DECIMAL(10,2) NOT NULL, status TINYINT DEFAULT 0, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, INDEX idx_user (user_id) ) ENGINEInnoDB; -- 订单明细表 CREATE TABLE order_item ( id INT PRIMARY KEY AUTO_INCREMENT, order_id INT NOT NULL, product_id INT NOT NULL, price DECIMAL(10,2) NOT NULL, quantity INT NOT NULL, INDEX idx_order (order_id) ) ENGINEInnoDB;逻辑说明utf8mb4是为了避免中文乱码这是血泪经验用utf8在某些客户端下会出问题。DECIMAL(10,2)存金额别用FLOAT否则会出现 0.10.2 不等于 0.3 的玄学。orders表名用复数是因为order是 SQL 关键字直接写会报语法错误。索引加在user_id和order_id上订单查询会快很多。参数说明stock默认 0status默认 1 表示上架。如果你要支持软删除加一个is_deleted字段默认 0。字符集和排序规则在建库时就定好后面改表很麻烦。2.3 连接池配置别再用 DriverManager 裸连了源码里如果每次请求都DriverManager.getConnection并发一上来数据库就扛不住。常见做法是配连接池Druid 或 HikariCP 都行。下面是一个 Druid 的druid.properties示例driverClassNamecom.mysql.cj.jdbc.Driver urljdbc:mysql://localhost:3306/shop?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/ShanghaiuseSSLfalseallowPublicKeyRetrievaltrue usernameroot password你的密码 initialSize5 maxActive20 maxWait3000逻辑说明serverTimezone必须配否则 MySQL 8 会报时区错误。useSSLfalse在本地开发关掉避免证书警告。allowPublicKeyRetrievaltrue是 MySQL 8 的 caching_sha2_password 认证需要的不加上会连不上。maxWait单位毫秒超过就抛异常别设太大否则请求堆积。参数说明initialSize初始连接数本地 5 够用maxActive最大活跃连接按你机器和数据库承受力调一般 20 到 50。连接池的关闭要在ServletContextListener里做别让连接泄漏。3. 从源码到运行IDEA 导入、数据库初始化和启动排错3.1 用 IDEA 跑通 JavaWeb 项目的最小步骤假设你拿到的是一个传统 Servlet 项目目录结构是src加web。按下面走第一步IDEA 里File - New - Project from Existing Sources选项目根目录类型选 Web Application。第二步进Project Structure - Modules确认src是 Sourcesweb是 Web 资源目录。第三步Project Structure - Libraries把web/WEB-INF/lib下的 jar 全部加进来。第四步Project Structure - Artifacts新建Web Application: Exploded输出目录设成out/artifacts/项目名。第五步Run/Debug Configurations 里加 Tomcat LocalDeployment 选刚才的 ArtifactApplication context 设成/shop。# 如果你用 Maven命令行先编译再跑 mvn clean package -DskipTests # 把 target 下的 war 丢进 Tomcat webapps cp target/shop.war $CATALINA_HOME/webapps/ # 启动 Tomcat $CATALINA_HOME/bin/startup.sh逻辑说明IDEA 的 Artifact 配置是 404 的重灾区Exploded 模式改 JSP 不用重启适合调试。Maven 打包方式适合部署到独立 Tomcat-DskipTests跳过测试加快速度。参数说明Application context 决定访问路径设/shop后浏览器访问http://localhost:8080/shop/。Tomcat 端口默认 8080被占用就在conf/server.xml里改。3.2 数据库初始化导入 SQL 和改连接配置建库脚本准备好后用 Navicat 或命令行导入。命令行方式mysql -u root -p shop.sql # 进入后检查表 mysql -u root -p -e USE shop; SHOW TABLES;然后找到项目里的数据库配置文件通常是db.properties或jdbc.properties把url、username、password改成你本地的。如果源码里是硬编码在 Java 类里搜DriverManager或getConnection改成读配置文件。// 读取配置文件的工具类写法 InputStream in DBUtil.class.getClassLoader().getResourceAsStream(druid.properties); Properties props new Properties(); props.load(in); DataSource ds DruidDataSourceFactory.createDataSource(props); Connection conn ds.getConnection();逻辑说明配置文件放src下编译后会进WEB-INF/classes用 ClassLoader 读最稳。别用绝对路径换机器就废。参数说明getResourceAsStream的路径不带/开头相对 classpath。连接用完必须close()Druid 的close是归还连接不是真关闭。3.3 启动后 404 和 500 的排查顺序启动后页面打不开先看 Tomcat 日志logs/catalina.out。404 一般是路径不对或 Artifact 没部署检查 Application context 和web.xml里的url-pattern。500 看异常栈最常见的是ClassNotFoundException驱动没加、SQLException连接串错、NullPointerException配置没读到。# 实时看 Tomcat 日志 tail -f $CATALINA_HOME/logs/catalina.out # 检查端口占用 netstat -tlnp | grep 8080逻辑说明日志是黑匣子别猜。tail -f边操作边看报错立刻定位。端口占用就换端口或杀进程。参数说明netstat在 Linux 用Windows 用netstat -ano | findstr 8080。杀进程kill -9 PID慎用先试kill PID。4. 核心功能实现登录、商品列表和下单的代码落点4.1 登录功能从表单到 Session 的完整链路登录是购物商城的入口链路是 JSP 表单提交到 ServletServlet 调 Service 查用户查到就写 Session。下面是一个精简的 LoginServletWebServlet(/login) public class LoginServlet extends HttpServlet { private UserService userService new UserServiceImpl(); protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String username req.getParameter(username); String password req.getParameter(password); // 密码建议加密后比对这里演示明文 User user userService.login(username, password); if (user ! null) { req.getSession().setAttribute(user, user); resp.sendRedirect(req.getContextPath() /product/list); } else { req.setAttribute(msg, 用户名或密码错误); req.getRequestDispatcher(/login.jsp).forward(req, resp); } } }逻辑说明WebServlet注解替代web.xml配置Servlet 3.0 以上支持。登录成功用sendRedirect防止刷新重复提交失败用forward保留提示信息。Session 里存 user 对象后续页面用${sessionScope.user.username}取。参数说明req.getContextPath()拿到应用上下文保证跳转路径带/shop。密码实际项目要加盐哈希别存明文这是底线。4.2 商品列表与分页SQL 的 limit 和页码计算商品列表要分页核心是LIMIT offset, size。下面是对应 DAO 方法public ListProduct findByPage(int page, int size) { int offset (page - 1) * size; String sql SELECT * FROM product WHERE status 1 ORDER BY id DESC LIMIT ?, ?; // 用 PreparedStatement 设参防注入 return jdbcTemplate.query(sql, new Object[]{offset, size}, new ProductRowMapper()); }逻辑说明offset从 0 开始第 1 页 offset 是 0。ORDER BY id DESC让新品在前。用PreparedStatement占位符别拼字符串否则 SQL 注入一打一个准。参数说明size每页条数一般 10 到 20。总页数用SELECT COUNT(*)算totalPage (total size - 1) / size向上取整。4.3 下单流程事务保证库存和订单一致下单要同时扣库存、写订单、写明细必须在一个事务里。下面用 JDBC 手动事务Connection conn null; try { conn DBUtil.getConnection(); conn.setAutoCommit(false); // 扣库存stock 要大于等于购买量 productDao.reduceStock(conn, productId, quantity); // 写订单 int orderId orderDao.insert(conn, order); // 写明细 orderItemDao.insert(conn, orderId, productId, quantity); conn.commit(); } catch (Exception e) { if (conn ! null) conn.rollback(); throw new RuntimeException(下单失败, e); } finally { DBUtil.close(conn); }逻辑说明setAutoCommit(false)开启事务任何一步异常都rollback。扣库存的 SQL 要带AND stock ?防止超卖。orderId用getGeneratedKeys拿自增主键。参数说明reduceStock返回影响行数为 0 说明库存不足要抛异常触发回滚。事务里别做远程调用否则连接占用时间长。5. 避坑与排查跑 JavaWeb 商城项目最容易翻车的 5 个点5.1 中文乱码从请求到响应全链路排查现象表单提交后数据库里是问号或者页面显示乱码。原因请求编码、响应编码、数据库字符集三者不一致。解决JSP 页头加% page contentTypetext/html;charsetUTF-8 %Servlet 里req.setCharacterEncoding(UTF-8)和resp.setContentType(text/html;charsetUTF-8)数据库和表用utf8mb4连接串加characterEncodingutf8。四个地方都对齐乱码就没了。5.2 MySQL 8 连接报错时区和认证插件现象The server time zone value ... is unrecognized或Public Key Retrieval is not allowed。原因MySQL 8 默认时区和旧驱动不兼容认证插件换成了caching_sha2_password。解决连接串加serverTimezoneAsia/ShanghaiuseSSLfalseallowPublicKeyRetrievaltrue驱动用com.mysql.cj.jdbc.Driver别用老的com.mysql.jdbc.Driver。5.3 404 但 Tomcat 启动了Artifact 和上下文路径现象Tomcat 日志没报错浏览器访问 404。原因IDEA 的 Artifact 没配或者 Application context 和访问路径不一致。解决Project Structure 里确认 Artifact 包含WEB-INF和编译输出Run 配置里 Deployment 选对 Artifact访问路径按 Application context 拼。改完重启 Tomcat。5.4 连接池耗尽连接没关的典型表现现象用一会儿就卡住日志报wait millis 3000, active 20。原因DAO 里Connection没在 finally 关闭连接泄漏。解决每个方法用 try-with-resources 或 finally 里close()Druid 的 close 是归还。检查所有getConnection的地方确保成对出现。5.5 下单超卖库存扣减的并发问题现象库存只剩 1两个人同时下单都成功。原因先查库存再扣中间有并发窗口。解决扣减 SQL 写成UPDATE product SET stock stock - ? WHERE id ? AND stock ?用影响行数判断是否成功。或者用乐观锁加版本号。事务隔离级别用默认的REPEATABLE READ就够关键是 SQL 原子性。6. 进阶技巧把项目改造成能写进简历的样子跑通只是起点要让这个 JavaWeb 购物商城项目在面试或课程设计里拿得出手得做几件事。第一把密码明文改成 BCrypt 哈希加一个PasswordUtil注册和登录都用它这是安全底线面试官必问。第二给商品列表加 Redis 缓存热点商品先查缓存再查库缓存 key 用product:list:page:size过期时间设 5 分钟能讲清楚缓存穿透和雪崩的应对。第三把下单接口加一个简单的限流用Semaphore或 Guava RateLimiter防止刷单代码量不大但能体现并发意识。验证改造是否成功我一般用三步先跑单元测试覆盖 Service 层再用 Postman 压一下登录和下单接口最后看数据库连接池的监控页面Druid 自带/druid/index.html确认活跃连接数在合理范围。如果时间够把日志换成 Logback按天切割出问题能回溯。// BCrypt 密码哈希示例 String hash BCrypt.hashpw(rawPassword, BCrypt.gensalt()); boolean ok BCrypt.checkpw(inputPassword, hash);逻辑说明gensalt()每次生成不同盐同一个密码哈希结果不同防彩虹表。checkpw比对时从哈希里提取盐不用自己存。参数说明gensalt(12)是计算强度默认 10越高越慢越安全本地开发 10 够用。我自己踩过最深的坑是早期图省事把数据库密码写死在 Java 里换环境时改漏了一处排查了两小时。从那以后所有配置一律外置db.properties加进.gitignore提交一个db.properties.example模板。这个习惯帮我省了无数次后悔药。希望帮到你。本文还有配套的精品资源点击获取