资讯动态

JavaWeb商品管理系统实战:从Servlet+JSP到MySQL部署全流程

发布时间:2026/9/20 19:17:47 来源:尧图企业网站定制
简介这是一份面向Java Web课程设计、毕业设计及电商管理初学者的商品管理系统项目资源。系统以后端Java Web为核心并引入Vue前端实现了注册登录与不同角色权限区分、商品信息与分类的增删改查、库存自动更新与低库存提醒、购物车生成订单、订单确认发货取消以及销售数据统计等功能基本覆盖电商后台的完整业务闭环。压缩包内共收录163个文件大小约3.29MB主要包含Java源码和编译后的class文件、XML配置与映射文件、前端JS/CSS/HTML页面、JAR依赖库、SQL数据库脚本等导入IDE后可查看分级工程结构配合SQL脚本能快速初始化并运行。已有1123人学习下载特别适合毕业设计或课程实践阶段参考。读者可从中获取完整业务模块的增删改查实现思路、数据库表与初始数据设计、前后端交互方式为二次开发、功能扩展和答辩演示提供坚实支撑。 不少刚把Servlet、JSP、JDBC基础学完的人都会有同一个想法基础语法都看懂了但真正要做一个能连数据库、能登进去操作的项目时却不知道从哪里下手。如果有人在这个阶段让我推荐一个练手项目我的答案从来不是网上那些花哨的秒杀系统、博客论坛而是最朴素的“JavaWeb商品管理系统”。这个项目在搜索平台的热度一直没下去过和它绑定的高频词基本都是“javaweb项目完整案例mysql”“黑马javaweb笔记数据”“2023版本idea创建javaweb项目”本质上大家想找的是一套“能完整跑起来、能连上数据库、能部署到服务器”的实战闭环而不是一个只活在IDE里的Demo。我自己第一个正式上手的Web项目也是商品管理系统。那时候没有多少现成源码可以抄一个报错能排一下午但恰恰是那段经历让我把Servlet、JSP、JDBC、Tomcat、MySQL这些散装知识点真正串成了一条线。下面这篇就按我实际动手的顺序来讲先说这个项目为什么值得做再定技术栈和数据库接着拆解核心代码的完整流转然后解决从IDEA本地跑通到Windows Server发布的整个环节最后把新手最容易踩的乱码、404、ClassNotFound这些坑一次性说清楚。不管是做课程设计还是给简历准备一个能讲明白的项目这套内容应该都够用。1. 为什么“商品管理系统”能成为多数人的JavaWeb启蒙项目1.1 麻雀虽小Web应用的完整链路一个不少一个商品管理系统的需求非常直白管理员登录后才能进入后台看到商品列表支持按商品名称关键词搜索列表要做分页还可以新增商品、编辑商品、删除商品。很多初学者一听“这不就是增删改查嘛”觉得太简单其实恰恰是这种简单让它成为绝佳的入门项目。业务复杂度低你才能把全部注意力放在Web应用的底层链条上请求是怎么从浏览器发到Servlet的参数怎么被封装成对象数据怎么通过JDBC落到MySQL查询结果又如何回到JSP页面最终渲染成表格。这中间还牵扯到很多不能回避的细节表单提交该用GET还是POST、删除操作要不要做二次确认、列表页如何用EL表达式和JSTL遍历、分页的页码和偏移量怎么算、部署上线后数据库字符集怎么设置。这些细节单拎出来都不难但组合在一起就是一副完整的JavaWeb地图。先跑通这条原生链路以后再去学Spring MVC你才会真正明白框架的控制器、视图解析器、参数绑定到底帮你省掉了哪些步骤而不是稀里糊涂地“会用”却不知其所以然。1.2 从热搜词看大家真正缺的不是理论是完整闭环我把和“商品管理系统”相关的高频搜索词翻了一遍排在前面的有“javaweb项目完整案例mysql”“winsever apache tomcat 发布javaweb项目”“2023版本idea创建javaweb项目”。这些词的共同点很明显多数人的卡点不在“Servlet怎么继承”“Cookie怎么读写”这种孤立知识点上而在于“我怎么把学过的知识组装成一个能跑的项目并且最终让别人也能访问到”。商品管理系统正好承接了这个需求。它的功能维度覆盖了一个Web项目从开发到上线的全部关键动作建库建表、写DAO、接Servlet、做JSP页面、配Tomcat、打War包、发布到服务器。同时因为业务足够熟悉你完全不用花时间理解“秒杀超卖”“订单状态机”这类复杂业务可以把精力全部留给“项目本身能不能跑起来”。这也是为什么培训机构案例库、学校课程设计都爱拿它当模板。对初学者来说完成这样一个完整闭环比刷十遍语法笔记有用得多。2. 技术栈和数据库设计先定版本、再建表后面少改好几轮2.1 技术选型原生Servlet JSP JDBC别急着上框架我知道很多人一搜项目满眼都是Spring Boot Vue心痒得很。但我仍然坚持建议第一个完整项目用Servlet JSP JDBC原生跑一遍。原因很简单框架是一条封装好的高速公路你不亲自走一遍土路就不会知道框架到底替你绕开了多少坑。用原生Servlet你能清楚地看到一个HTTP请求从url-pattern匹配到Servlet再到doGet/doPost里手动取参数、手动转发用原生JDBC你能感受到每一次数据库连接的创建和关闭是有开销的也就更理解连接池存在的意义。第一次就上框架很容易变成“会调用注解但说不清底层发生了什么”。所以本项目技术栈就定为JDK 8 Tomcat 9 MySQL Servlet/JSP JDBC连接池可选Druid。推荐搭配如下组件推荐版本说明JDK8 或 11JDK 8兼容性最好和Tomcat 9配合最稳Tomcat9.0.x包名还是javax.*网上资料几乎全部适用MySQL5.7 / 8.08.0注意驱动类名是com.mysql.cj.jdbc.DriverIDEA2023.x社区版就够用MySQL驱动mysql-connector-java 8.0.33URL和类名写法见下文连接池Druid 1.2.x可选但建议加上这里特别提醒一句别用Tomcat 10起步。Tomcat 10把包名从javax.迁到了jakarta.虽然它能跑新项目但市面上大量资料和课程视频还是基于javax写的你照着抄很容易出现“类明明存在却报ClassNotFound”这种让人抓狂的问题。等原生项目跑通再考虑迁移到新规范不迟。2.2 商品管理系统需要三张表不是一张别贪图省事把商品、分类、用户全塞进一张表。学习项目虽然业务简单但表设计也不该是“能存就行”。我推荐至少拆成三张表用户表t_user管后台登录账号分类表t_category管商品分类商品表t_product存核心数据。三张表建好后面想练关联查询、写外键约束都有现成的素材。CREATE DATABASE IF NOT EXISTS shop DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE shop; CREATE TABLE t_user ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(100) NOT NULL, nickname VARCHAR(50), create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT管理员用户表; CREATE TABLE t_category ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(50) NOT NULL UNIQUE, sort INT DEFAULT 0 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT商品分类表; CREATE TABLE t_product ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(100) NOT NULL, category_id INT NOT NULL, price DECIMAL(10,2) NOT NULL, stock INT NOT NULL DEFAULT 0, description VARCHAR(500), status TINYINT DEFAULT 1 COMMENT 1上架 0下架, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, CONSTRAINT fk_product_category FOREIGN KEY (category_id) REFERENCES t_category(id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT商品表;字段设计上有几个细节值得记住。价格用DECIMAL(10,2)而不用double因为浮点数在金额计算上会有精度问题涉及钱的一律用定点数。商品表里同时放create_time和update_time两个时间字段排查数据问题时能省很多事。status字段做上下架状态比直接物理删除商品更稳妥以后想加“下架”“回收站”功能这个字段直接就能用。所有的表名、字段名建议统一用小写加下划线和Java驼峰属性做映射时不容易乱。2.3 初始化数据与连接池配置表建好之后先插入测试数据再写代码这是我一直提倡的顺序。数据在你写DAO层时就能立刻看到查询结果而不是对着空表猜逻辑。插几条最简单的测试数据INSERT INTO t_user (username, password, nickname) VALUES (admin, 123456, 管理员); INSERT INTO t_category (name, sort) VALUES (数码产品, 1), (图书音像, 2); INSERT INTO t_product (name, category_id, price, stock, description) VALUES (手机, 1, 1999.00, 100, 测试商品), (JavaWeb实战笔记, 2, 59.00, 500, 经典入门资料);接下来是连接池配置。用Druid的原因是它简单、依赖少、对新手友好还能输出直观的监控日志。在src目录放一个db.propertiesdriverClassNamecom.mysql.cj.jdbc.Driver urljdbc:mysql://localhost:3306/shop?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/ShanghaiuseSSLfalse usernameroot password你的数据库密码 initialSize5 maxActive20URL里的characterEncodingutf8决定中文能不能正常读写serverTimezone避免MySQL 8.0时报时区错误。连接池的意义在于复用连接如果每次请求都现场DriverManager.getConnection高并发下数据库很快会被打满提前养成连接池习惯后面用框架时会平滑很多。3. 核心代码闭环一条商品请求如何走完三层3.1 包结构先行按MVC思想划分动手写代码前先把包结构规划好不然后面类越建越多自己都找不到。我常用的结构是这样com.shop ├── entity // 实体类User、Category、Product ├── dao // 数据访问层ProductDao、UserDao ├── service // 业务层ProductService、UserService ├── servlet // 控制器ProductListServlet等 └── util // 工具类DBUtil webapp ├── admin // JSP页面product_list.jsp等 └── WEB-INF └── web.xml很多教程在这个简单项目里不写Service层直接Servlet调DAO我建议你还是把Service层留出来哪怕只是在里面“转调一下”。这么做的原因有两个一是为后续学框架做准备Spring的Service就落在这个位置二是业务一旦变复杂比如删除商品时同步清理关联数据、库存扣减时要做校验这些操作需要事务边界而事务的入口天然在Service层不在Servlet里。从第一个项目开始保留这个习惯后面写大型项目时会很受益。3.2 商品列表、关键词搜索、分页的完整实现列表页是整个项目最核心的模块。先看工具类DBUtil负责从Druid连接池拿连接public class DBUtil { private static DruidDataSource ds new DruidDataSource(); static { try (InputStream in DBUtil.class.getClassLoader() .getResourceAsStream(db.properties)) { Properties p new Properties(); p.load(in); ds.setDriverClassName(p.getProperty(driverClassName)); ds.setUrl(p.getProperty(url)); ds.setUsername(p.getProperty(username)); ds.setPassword(p.getProperty(password)); ds.setInitialSize(Integer.parseInt(p.getProperty(initialSize))); ds.setMaxActive(Integer.parseInt(p.getProperty(maxActive))); } catch (Exception e) { throw new ExceptionInInitializerError(e); } } public static Connection getConnection() throws SQLException { return ds.getConnection(); } public static void close(ResultSet rs, PreparedStatement ps, Connection conn) { // 统一回收资源顺序rs - ps - conn } }DAO层里最值得多看两遍的是分页查询方法它同时涉及SQL动态拼接、参数绑定和分页计算是JavaWeb新手的必修课public ListProduct findByPage(String keyword, int page, int pageSize) throws SQLException { String sql SELECT * FROM t_product WHERE 11; ListObject params new ArrayList(); if (keyword ! null !keyword.trim().isEmpty()) { sql AND name LIKE ?; params.add(% keyword %); } sql ORDER BY id DESC LIMIT ?, ?; params.add((page - 1) * pageSize); params.add(pageSize); ListProduct list new ArrayList(); try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { for (int i 0; i params.size(); i) { ps.setObject(i 1, params.get(i)); } try (ResultSet rs ps.executeQuery()) { while (rs.next()) { Product p new Product(); p.setId(rs.getInt(id)); p.setName(rs.getString(name)); p.setPrice(rs.getBigDecimal(price)); // 其余字段同样映射 list.add(p); } } } return list; }这里有两个关键点。第一SQL中的where 11不是为了装酷而是为了后面拼接and条件时不用关心“第一个条件是否已经存在”代码会清晰很多。第二所有参数都用?占位符再通过PreparedStatement绑定这是防SQL注入的基本姿势直接用字符串拼接用户输入是绝对禁止的。分页偏移量的计算公式是(page-1)*pageSize对应MySQL的LIMIT子句这个公式会在很多项目里反复出现最好一次性记牢。Servlet接收浏览器参数并调用ServiceWebServlet(/admin/product/list) public class ProductListServlet extends HttpServlet { protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String keyword req.getParameter(keyword); int page 1; String pageStr req.getParameter(page); if (pageStr ! null !pageStr.isEmpty()) { page Integer.parseInt(pageStr); } int pageSize 5; ListProduct list productService.findByPage(keyword, page, pageSize); int total productService.count(keyword); req.setAttribute(list, list); req.setAttribute(total, total); req.setAttribute(currentPage, page); req.setAttribute(pageSize, pageSize); req.getRequestDispatcher(/admin/product_list.jsp).forward(req, resp); } }JSP端用JSTL配合c:forEach就能把表格循环渲染出来分页条用页码链接指向同一个Servlet并携带page参数。整个数据流是浏览器发起请求 - Servlet取参数 - Service统计和查询 - DAO拼SQL操作数据库 - 结果放request域 - forward到JSP渲染。把这条链路彻底理解JavaWeb最核心的东西就握在手里了。3.3 添加、编辑、删除的统一套路增删改查里查询就是上面那套剩下三个操作也有固定套路。添加商品JSP表单以POST方式提交到AddServletServlet里用request.getParameter依次取值封装成Product对象再调用service.insert。编辑商品分两步先由EditServlet根据id查出商品并放到request域回显到表单页用户改完再POST到UpdateServlet执行update。删除商品最简单根据id执行delete即可。这里有两个细节想提醒。第一删除和修改这类“有副作用”的操作规范上应该用POST而不是GET。很多人图省事直接在地址栏传id删数据看起来方便但浏览器预取、搜索引擎爬虫都可能触发请求误删就晚了。至少也要在删除前加一个confirm二次确认。第二Servlet处理完写操作后不要用forward方式跳转页面否则用户一刷新就会重复提交。正确做法是用resp.sendRedirect重定向到列表页这就是经典的Post/Redirect/Get模式属于Web开发的基本素养第一次写对后面就少改一次。4. 部署环节从IDEA 2023本地跑通到Windows Server上发布4.1 IDEA 2023创建JavaWeb项目并配置Tomcat环境搭建是很多人第一个卡点。用IDEA 2023创建JavaWeb项目主流有两种方式一种是新建普通Java项目然后手动添加Web模块另一种是建Maven项目并勾选Web骨架。如果是做课程设计我推荐直接用Maven工程依赖管理省心如果是为了把底层关系看得更明白用普通Java项目手动添加依赖也行。不管哪种方式核心步骤是在Project Structure里确认项目有Web模块和Facet在Run/Debug Configurations里选择Tomcat Server - Local指定Tomcat安装目录然后到Deployment选项卡添加Artifact建议选war explodedApplication context填/shop。运行后浏览器访问http://localhost:8080/shop即可。这个环节最容易出现的三个问题一是依赖jar没有加载进来需要在Project Structure的Artifacts里把lib目录加入二是Tomcat启动了但端口被占用改conf/server.xml里的端口即可三是改了代码后重启不生效检查是否勾选了Build Project Automatically实在不行就clean再rebuild。4.2 War包发布到独立Tomcat本地跑通之后要么交给老师检查要么放到服务器上让同学访问这时不需要把整个IDEA工程搬过去打War包就够了。在IDEA里执行Build - Build Artifacts - Build会生成一个war文件。把这个war丢进Tomcat的webapps目录运行bin/startup.bat启动Tomcat会自动解压并部署。访问http://服务器IP:8080/shop就能看到页面。这里有个很容易踩的坑Web项目里的数据库连接URL如果写的是localhost部署到别的机器后它指向的是那台机器自己的MySQL不是你开发机上的数据库。所以部署前要确认目标机器有MySQL、库表结构和数据都准备好了db.properties里的连接配置也要对应改。我第一次部署时没注意这个问题页面一直报数据库连接失败白折腾了大半个小时。4.3 需要对外提供HTTP服务时Apache Tomcat怎么配合如果项目要作为正式的对外服务比如挂在80端口、由Apache统一处理静态资源和访问日志那就需要让Apache和Tomcat配合工作。常见方案是Apache启用mod_proxy和mod_proxy_ajp模块把匹配到的请求转发给Tomcat的AJP端口。在httpd.conf里加入LoadModule proxy_module modules/mod_proxy.so LoadModule proxy_ajp_module modules/mod_proxy_ajp.so ProxyPass /shop ajp://127.0.0.1:8009/shop ProxyPassReverse /shop ajp://127.0.0.1:8009/shop这样外部访问Apache的80端口请求会通过AJP协议转发到Tomcat的8009端口前提是conf/server.xml里AJP连接器还开着。Apache负责接收连接、处理静态资源Tomcat专心跑JavaWeb应用两者各司其职。如果不想配AJP也可以用ProxyPass http://127.0.0.1:8080/shop直接转发HTTP请求到Tomcat的8080端口逻辑一样只是少了一层AJP协议。配置完成后必须重启Apache才会生效。排查问题时先确认Tomcat单独访问http://ip:8080/shop是正常的再去测Apache侧的效果这样能快速判断问题出在Tomcat还是Apache的转发配置上。5. 实战中躲不开的坑乱码、404和奇怪的ClassNotFound5.1 乱码问题全家桶乱码几乎是每个JavaWeb新手都会遇到的第一个拦路虎而且它往往不是一处原因是一连串原因。页面显示乱码先检查JSP首行的pageEncoding和contentType是不是UTF-8请求参数乱码POST请求需要在Servlet读取参数前调用request.setCharacterEncoding(UTF-8)数据库读写乱码库、表、连接URL三处都要保持utf8mb4缺一个就可能前功尽弃。最优雅的做法是写一个EncodingFilter在web.xml里配置成拦截所有请求这样全项目统一编码不用每个Servlet都重复写WebFilter(/*) public class EncodingFilter implements Filter { public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { request.setCharacterEncoding(UTF-8); response.setContentType(text/html;charsetUTF-8); chain.doFilter(request, response); } }这个Filter看着简单但能解决80%的乱码问题。剩下20%可能是GET请求编码问题Tomcat 8以上的URIEncoding默认就是UTF-8一般不用改老版本Tomcat才需要在server.xml里加URIEncodingUTF-8。排查乱码时建议按“页面 - 请求 - 数据库”这个顺序逐层缩小范围先在JSP里写死一段中文看显示是否正常再走业务请求最后看数据库里存的数据很快就能定位。5.2 常见报错的排查链路我把新手期最高频的报错整理成了一张排查表报错信息常见原因解决方式ClassNotFoundException: com.mysql.cj.jdbc.Driver驱动jar没放进WEB-INF/lib把mysql-connector-java的jar复制到lib目录并Add as LibraryAccess denied for user rootlocalhostMySQL密码或账号不对检查db.properties和线上库的账号权限404 页面找不到Application context写错、Servlet路径不对检查IDEA的Artifact路径和WebServlet的url500 NullPointerException数据库字段与实体属性映射不匹配打印SQL和结果集逐一核对字段名Communications link failureMySQL没启动、端口不对、URL中host指向错依次检查服务状态、端口、host排查这些报错我有一条经验不要盯着整屏的红色堆栈不动从第一行Exception往下数找到Caused by那一行那才是真正的病根。比如表面是NullPointerExceptionCaused by通常写着Could not get JDBC Connection之类的底层原因。另外如果部署在Windows Server上记得检查防火墙是否放行了3306和8080端口课程设计阶段被这个卡住的人也不少。还有一个很隐蔽的坑同一个项目在IDEA里启动正常打成War包部署到Tomcat的webapps后却报ClassNotFound。原因是IDEA默认打的war包没有把第三方jar包含进去需要在Project Structure - Artifacts里把依赖jar放进WEB-INF/lib。验证方法很简单用解压工具打开war看WEB-INF/lib下面有没有mysql驱动和Druid的jar没有就手动补进去重新打包。最后聊一点个人体会。我前前后后带过不少朋友做这个项目发现最终能把商品管理系统完整跑通的人不一定是最聪明的那批但一定是愿意一行一行看日志、一个坑一个坑去填的人。如果你做完之后还想继续深入这个项目已经留好了扩展点加上购物车和订单表、做库存扣减时考虑事务、给后台加Filter登录拦截器、把列表查询改成走索引……每一个扩展都能让JavaWeb的理解再上一个台阶。希望这份经验能帮你少走点弯路也欢迎你回头告诉我你的第一个商品管理系统最后做成了什么样。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价