鞋类电商的JavaWeb课题,算是校园开发项目里生命力最长的一类。尤其是“运动鞋店产品推广网站”这种选题,看起来简单,其实把商品展示、活动运营、下单交易、后台维护全都串起来了,做一遍基本能把Web开发的主干流程摸透。刚接这个题目的时候,我一度觉得“推广网站”不就是商品列表加几张轮播图吗,真正动手做才发现,里面的边界划分、数据设计、前后端交互,每一步都是坑。这篇就按我当时从零梳理到最终落地的顺序,把选题拆解、技术选型、数据库设计、核心模块实现,以及调试中遇到的问题全部写清楚,给后面选类似题目的同学做个参考。
1. 课题内核解读:推广网站不只是“卖鞋”
1.1 这个题目真正考察什么
先说最核心的一点:很多同学看到“产品推广网站”会下意识把它理解成一个静态展示页面,这是第一个认知误区。在JavaWeb课程设计或毕业设计的语境里,“推广网站”从来不是单纯的信息展示,而是带有明确业务逻辑的动态Web应用。运动鞋店需要什么?需要把鞋展示给用户,让用户了解鞋的卖点、价格、库存,并且能通过网站完成咨询或购买意向的表达,同时运营人员可以在后台维护商品、发布活动、查看用户反馈。
所以这个课题真正考察的能力包括:
- Java基础扎实程度:Servlet生命周期、请求响应模型、Session管理、集合与JDBC操作。
- 数据库设计能力:商品分类、商品信息、用户、订单、活动这些实体之间的关系,字段类型怎么定,索引怎么建。
- 前后端衔接能力:表单提交、AJAX局部刷新、JSON数据交换、页面跳转与参数传递。
- 业务逻辑抽象能力:购物车是临时存储还是会持久化?订单状态怎么流转?优惠活动怎样和商品关联?
掌握这四点,就不只是“做一个网站”,而是做了一个真正能跑的迷你电商系统。
1.2 推广与销售:两个容易被忽视的区别
既然题目说的是“推广”,就不应该把设计重心完全放在交易闭环上。传统购物网站的核心是“加购、下单、支付、物流”,而推广网站更强调“吸引用户、展示商品、引导互动”。我在设计时把两者做了区分:
- 推广侧:首页轮播图推荐、新品首发模块、限量抽签活动、优惠券发放、鞋款搭配推荐。
- 业务侧:用户注册登录、收藏夹、购物车、模拟下单、订单查看。
这样划分的好处是,课题答辩时你可以很清晰地讲出“这个网站和普通电商网站的区别在哪里”,不用硬做一个支付模块——很多同学就是死磕支付,结果既做不完整,又偏离了题目里的“推广”重心。模拟下单到生成订单这一步就够了,支付环节可以选择按线下转账方式展示说明,这样反而符合推广网站的真实业务场景。
2. 技术选型:先别急着上框架
2.1 JavaWeb技术栈的取舍
这个课题的技术方案,主要分两个流派。传统流派是JSP + Servlet + Tomcat + MySQL,纯手工处理请求转发和JDBC数据访问;现代流派是Spring Boot + Thymeleaf(或JSP)+ MyBatis/JPA。大多数高校的课程设计大纲会基于传统JavaWeb出题,因为它能直接考察你对Servlet、Filter、Listener这些底层机制的理解,但如果你有一定的自学基础,Spring Boot会让开发效率提升一个量级,也更好向面试官展示工程化能力。
我当时采用的是一个折中方案:用Servlet + JSP完成核心代码,但是把项目结构按照MVC的分层思想来组织。Model层用JavaBean + DAO,View层用JSP,Control层用Servlet。这样做有几个好处:
- 符合课程设计和毕业设计最常见的验收要求,不会被质疑“这是你自己写的吗”。
- 代码结构清晰,每一层职责单一,答辩时好讲。
- 后期如果要迁移到Spring Boot,逻辑几乎不需要改动,只是把对象的创建和管理交给容器。
如果学校没有硬性规定必须用纯JSP/Servlet,我建议优先考虑Spring Boot + MyBatis + Thymeleaf的组合,理由后面在开发节奏那一节里展开说。
2.2 环境配置与项目骨架
无论选哪种方案,基础环境都是Java 8/11 + Tomcat 9 + MySQL 5.7/8.0 + Maven。这里有一个容易踩坑的点:Maven项目的web.xml和JDK版本不匹配会导致部署后404或者启动直接报错。我习惯的配置是:
- JDK 1.8
- Maven 3.6+
- Tomcat 9.0
- MySQL 8.0(驱动用 com.mysql.cj.jdbc.Driver)
- JSTL 1.2
- 连接池用 DBCP 或 C3P0
项目包结构建议直接分层命名,减少后期混乱:
com.sneaker.shop ├── entity(实体类) ├── dao(数据访问接口和实现) ├── service(业务逻辑) ├── servlet(控制器) ├── filter(编码和登录过滤器) ├── util(数据库连接、字符串处理等工具类) └── listener(启动初始化监听器)WebContent目录下按角色分文件夹:
view ├── index.jsp(首页) ├── product(商品详情、列表) ├── user(登录、注册、个人中心) ├── cart(购物车、结算页) └── admin(后台管理页面)静态资源单独放在assets目录下,图片、CSS、JS分类管理。很多同学图省事把CSS直接写在JSP里,后期改个颜色都要翻半天文件,这个习惯应该从一开始就改掉。
3. 数据库设计:一切功能的前置条件
3.1 表结构与字段设计
运动鞋店的推广网站,数据库设计至少要覆盖这几张核心表:用户表、商品分类表、商品表、商品图片表、购物车表、订单表、订单明细表、活动表、用户收藏表。不要觉得表多,这些都是一个电商网站最基础的数据模型。
拿商品表来举例,我当时设计的字段如下:
| 字段名 | 类型 | 说明 |
|---|---|---|
| id | int | 商品ID,主键自增 |
| category_id | int | 所属分类ID,外键关联分类表 |
| name | varchar(100) | 商品名称 |
| subtitle | varchar(200) | 商品副标题/卖点 |
| description | text | 商品详细描述 |
| price | decimal(10,2) | 售价(注意用decimal,不能用float) |
| original_price | decimal(10,2) | 原价,用于展示折扣 |
| stock | int | 库存数量 |
| sales | int | 销量(用于排序和人气展示) |
| status | tinyint | 上下架状态,1上架 0下架 |
| create_time | datetime | 录入时间 |
| update_time | datetime | 更新时间 |
价格用decimal而不是float,这个坑我是吃过的。float在计算时会出现精度丢失,比如99.99乘以3,结果可能算出299.9699999,展示出来非常难看。decimal(10,2)直接按两位小数存储,省去很多格式化麻烦。
商品图片单独建一张表,原因是运动鞋这种商品通常会有多张图,比如主图、侧面图、鞋底图、上脚效果图,放一张表里字段会非常冗余。图片表结构就是id、product_id、img_url、sort_order,一个商品对应多条图片记录,取第一条当封面。
3.2 关键表关系的设计逻辑
用户和订单是一对多,订单和订单明细是一对多,商品和分类是多对一,用户和收藏是多对多(通过收藏表关联)。
订单表和订单明细表要特别注意。我当时第一版只设计了一张订单表,把商品名称、数量、单价直接冗余在订单字段里,结果用户一次买三双不同鞋就傻了。正确做法是订单表和订单明细表分开:订单表存订单编号、用户ID、总金额、下单时间、状态;订单明细表存订单ID、商品ID、商品名称(快照)、单价、数量、小计。订单明细为什么要冗余商品名称和单价?因为商品表的价格和名称随时可能改动,如果用户下单后管理员改了价格,订单里的历史记录就会对不上,这属于典型的“业务数据快照”设计。
活动表和商品表的关联,要支持一个活动包含多个商品、一个商品参加多个活动,所以也要单独设计活动商品关联表。活动字段包括活动名称、类型(满减/折扣/秒杀/抽签)、开始时间、结束时间、状态。抽签活动是运动鞋圈特有的玩法,可以也加一个参与人数限制字段,做起来有特点,答辩时容易出彩。
4. 核心功能模块落地的思路与关键代码
4.1 商品橱窗与分类筛选
首页商品展示是推广网站的门面。运动鞋店的用户需求通常很明确——按品牌看、按跑鞋/篮球鞋/休闲鞋分类、按价格区间筛选、按新品或销量排序。所以商品列表页一定要支持多条件组合查询,而不是简单地把所有商品铺在一个页面上。
我实现的商品列表Servlet接收参数包括:categoryId、keyword、minPrice、maxPrice、sort(默认sale_desc或者new_desc)、pageNum、pageSize。查询逻辑用JDBC拼接动态SQL,注意只能用参数化查询防止SQL注入:
String sql = "SELECT * FROM product WHERE status = 1"; List<Object> params = new ArrayList<>(); if (categoryId != null) { sql += " AND category_id = ?"; params.add(categoryId); } if (keyword != null && !keyword.trim().isEmpty()) { sql += " AND (name LIKE ? OR subtitle LIKE ?)"; params.add("%" + keyword.trim() + "%"); params.add("%" + keyword.trim() + "%"); } if (minPrice != null) { sql += " AND price >= ?"; params.add(minPrice); } if (maxPrice != null) { sql += " AND price <= ?"; params.add(maxPrice); } sql += " ORDER BY " + sortField + " " + sortOrder; sql += " LIMIT ?, ?";这里排序字段不能直接拼参数,我预先定义了一个白名单映射,防止外部传一个恶意字段名进来。分页参数用LIMIT ? ?占位,同样走PreparedStatement。
商品卡片展示的核心是封面图和价格。我封装了一个ProductVO对象,查询时LEFT JOIN图片表拿到封面图URL,避免循环查询。这是新手最容易忽视的性能问题——在JSP里遍历商品,再根据每个商品的ID去数据库查图片,商品一多页面就慢得没法看。
4.2 推广活动模块的三种落地方式
运动鞋店的推广活动和普通电商不一样,常见的有三种玩法,我在系统里都做了支持:
第一种是首页轮播活动图,对应一张banner图,点击跳转到活动专题页或商品列表。这个技术上不复杂,后台维护Banner的图片URL、跳转链接和排序值,前台用JS轮播组件渲染。Banner的图片建议统一压缩到不超过200KB,Web层再开一层缓存,不然首页每次刷新都在读数据库。
第二种是限时折扣/满减活动。用户在商品详情页看到的是计算后的折后价,这个计算可以在SQL里完成,也可以在Service层完成。我选择在Service层处理,因为满减规则的逻辑比较复杂,比如满299减30、满599减80,用SQL条件判断可读性很差。Service层根据活动类型先过滤符合条件的商品,再逐件计算优惠价格,最后封装到VO里返回给页面。
第三种是限量抽签,这是我觉得整个项目最有运动鞋特色的模块。用户在规定时间内点击“参与抽签”,记录参与状态;到开签时间后,系统从所有参与者中随机抽取中签用户,中签用户获得购买资格。技术上本质是用户和活动多对多关系加一个随机函数:
UPDATE activity_enrollment SET status = 1 WHERE activity_id = ? AND user_id = ? AND status = 0 AND create_time < ?开签时用随机数从参与列表取中签名额:
SELECT * FROM activity_enrollment WHERE activity_id = ? AND status = 0 ORDER BY RAND() LIMIT ?注意这里的写法是预期中签人数,比如限量5双就取5条,然后把status更新为1,其余参与记录标记为未中签。数据库加唯一约束(activity_id, user_id)防止同一用户重复参与。
4.3 购物车与订单流程
购物车的实现有两个方向:存Session还是存数据库。推广网站通常允许未登录用户先逛,加入购物车时可能没有登录态,所以种比较稳的做法是先用Cookie存一个临时购物车标识,用户登录后把临时购物车合并到数据库。不过这个方案工作量不小,课程设计里我更推荐简化版:用户必须先登录才能加入购物车,购物车表按user_id维度存储。
购物车表核心字段:id、user_id、product_id、quantity、checked(是否选中用于结算)、create_time、update_time。同一个用户添加同一商品时,先查询是否已有记录,有则update数量+1,没有则insert新记录。
购物车的操作还有一个隐藏坑:用户在购物车改数量时,前端可以传负数或者超大值,后端一定要做二次校验。数量不能小于1,最大不能超过库存。我在这里吃过一次亏,测试时前端改了数量,下单接口没校验,结果订单数据里出现了一个负数,销量统计全乱了。
订单生成流程值得认真捋一下,我是这样设计的:
- 用户从购物车勾选商品,点击结算进入确认页。
- 确认页显示商品快照信息,用户填写收货人、电话、地址。
- 提交时后端开启事务,依次完成:创建订单主表记录、创建订单明细记录、扣减库存、清空购物车对应项。
- 事务回滚条件:库存不足或明细插入失败都整体回滚。
事务管理在纯JDBC里的写法是用Connection开启手动提交,最后commit,异常时rollback。如果把事务放在Servlet层是最不好的做法,控制器的职责是参数接收和页面调度,事务应该收口在Service层方法内部。我当时专门写了一个TransactionManager工具类,用ThreadLocal绑定同一个Connection,保证Service里的DAO操作都在同一个事务里。
4.4 后台管理系统的最小实现
后台是答辩时的“演示重头戏”,但很容易被低估。很多同学觉得后台就是列表查询,用默认的表格显示就行。实际上后台至少包含这几个模块:管理员登录、商品管理(CRUD)、分类管理、活动管理、订单管理、用户管理。
商品管理里的图片上传是后台最常见的卡点。纯Servlet处理文件上传有两种方式,老式的是commons-fileupload组件,新式的是Servlet 3.0的Part接口。建议直接用Part接口,Tomcat自带支持,不需要额外引依赖:
Part part = request.getPart("coverImg"); String fileName = Paths.get(part.getSubmittedFileName()).getFileName().toString(); String ext = fileName.substring(fileName.lastIndexOf(".")); String newName = UUID.randomUUID().toString().replace("-", "") + ext; String basePath = getServletContext().getRealPath("/assets/img/product"); part.write(basePath + File.separator + newName);这个写法有两个重要细节:一是文件名必须用UUID重命名,否则两个用户上传同名图片会互相覆盖;二是getRealPath获取的是项目部署路径,Eclipse或IDEA热部署时路径和源码目录不一定一致,图片可能会被下次重启清掉。更稳的方案是把图片存到服务器固定目录,比如/usr/local/sneaker_shop_images,数据库里存绝对路径或相对访问URL,再把Tomcat的虚拟目录映射配置好。课程设计如果不想折腾部署,用getRealPath也能凑合,但答辩时如果老师问“重启后图片丢没丢”,你得心里有数。
后台页面使用JSP + JSTL + Bootstrap就够了,不需要前端框架。商品列表用分页表格,上下架用switch切换,AJAX提交状态修改;活动列表按时间倒序,状态通过时间自动判断。
5. 实操中的高频问题与排查方法
5.1 请求404、静态资源加载不出来
这是JavaWeb新手最常见的综合症。页面能打开但CSS/JS全部失效,多半是项目访问路径问题。JSP里的静态资源引用要用相对路径,如果页面在/view/product/目录下,你写css/style.css就指向了/view/product/css/style.css,自然加载不出来。推荐的做法是JSP页面头部统一用:
<% String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + request.getContextPath() + "/"; %>所有静态资源地址前面都拼上basePath,这样无论页面在哪个目录层级都能正确引用。Servlet的注解路径也要注意,只要写@WebServlet("/product/list")这种带斜杠的全路径,不要写相对路径。
404还有一个隐蔽来源:页面跳转用了请求转发但没有加/。request.getRequestDispatcher("productList.jsp")和getRequestDispatcher("/productList.jsp")在有前缀时的解析方式完全不同,前者是相对路径,后者是上下文根路径,稍不注意就跳到一个不存在的地址。
5.2 中文乱码问题
乱码问题是老生常谈,但几乎每届都有人栽在上面。乱码的根源只有一个:页面、请求、数据库、连接串四处的字符集不一致。我当时一次性把所有环节都理顺:
- 页面设置:JSP第一行写pageEncoding="UTF-8",并且HTML里也加上。
- 请求设置:写一个Filter统一处理,request和response都setCharacterEncoding("UTF-8"),注意POST请求必须设置,GET请求还需要配合Tomcat的URIEncoding。
- 数据库连接串:在JDBC URL后面加useUnicode=true&characterEncoding=utf8。
- 数据库表结构:建表时统一engine=InnoDB default charset=utf8mb4,注意utf8mb4才能存emoji和特殊字符。
- JDBC驱动:MySQL 8.0以上用com.mysql.cj.jdbc.Driver,老驱动在连接时还会因为SSL报错。
如果前面都设置了还是乱码,检查一下表单提交方式。form表单没有写method="post"时默认是GET,GET请求的参数在Tomcat里默认按ISO-8859-1解析,需要在Servlet里手动isoToUtf8处理,或者直接改Tomcat配置文件里的URIEncoding。我也在这些细节上卡过半天。
5.3 数据库连接池耗尽与SQL报错
没有用连接池而采用DriverManager.getConnection的方式,在并发访问高的时候会频繁创建和关闭数据库连接,Tomcat下很容易报Too many connections异常。课程设计阶段并发量总体可控,但也要养成好习惯。我用的是DBCP连接池:
BasicDataSource dataSource = new BasicDataSource(); dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver"); dataSource.setUrl("jdbc:mysql://localhost:3306/sneaker_shop?useUnicode=true&characterEncoding=utf8"); dataSource.setUsername("root"); dataSource.setPassword("password"); dataSource.setInitialSize(5); dataSource.setMaxTotal(20); dataSource.setMaxWaitMillis(3000);连接池配置放到一个Listener里,在应用启动时初始化,关闭时销毁,DAO层统一从DataSource拿连接。注意DBCP的maxTotal在2.x版本对应的是最大连接数,老版本叫maxActive,网上很多资料混着写,配置不生效时会很难排查。
SQL报错里最常见的两类,一是字段名拼写错误,二是MySQL关键字冲突。字段名我踩过price的坑吗?并没有,但order这个字段名是MySQL的保留字,我第一版订单表用了order_id做字段名没问题,但如果有字段叫order加引号或者改名才行。这个只能靠多写多积累,写SQL时报错信息一定要逐字读,不要把报错截图发给别人问“这是什么”,先自己对着排查。
5.4 上传图片后页面显示不出来
图片上传后,在后台看路径没问题,前台就是显示不出来。当时的情况是:开发时直接以Tomcat部署路径为根目录,图片物理存储在target目录里的临时部署目录,执行一次clean之后图片就被删了。解决方式是我在服务器上建了一个专门的目录存储用户上传的图片,让Tomcat对/assets/upload/路径映射到物理目录,这样重启不丢、多实例部署也支付同一份数据。
在Tomcat的server.xml的Host节点下配置:
<Context path="/assets/upload" docBase="/usr/local/sneaker_shop/upload" reloadable="false"/>这样上传时写物理目录,数据库存访问路径/assets/upload/xxx.jpg,页面直接就能访问。这种方式虽然比getRealPath多一步配置,但更适合实际部署,答辩时提到这点也是加分项。
6. 开发节奏、测试与答辩准备
6.1 开发排期:从需求到验收
如果你完整开发一个运动鞋店推广网站,我按一个人的开发速度,给一个比较真实的时间排期作为参考:
| 阶段 | 内容 | 预计时间 |
|---|---|---|
| 需求梳理与原型设计 | 画页面草图、理清功能边界 | 2-3天 |
| 数据库设计与建表 | 表结构、关系、初始数据 | 2天 |
| 基础框架搭建 | 项目结构、连接池、Filter、监听器 | 1-2天 |
| 前台首页与商品模块 | 分类导航、商品列表、详情页 | 4-5天 |
| 用户模块与购物车 | 注册登录、收藏、购物车 | 3-4天 |
| 订单流程 | 确认页、生成订单、事务 | 3天 |
| 后台管理 | 商品、分类、活动、订单管理 | 4-5天 |
| 推广活动模块 | 轮播图、满减、抽签 | 3-4天 |
| 整体测试与修复 | 功能测试、边界情况 | 3天 |
| 文档撰写与PPT | 设计文档、答辩PPT | 2-3天 |
加起来大约4周到5周,考虑到白天还有其他课程或者实习,实际跨度可能到两个月。我的经验是前端的页面工作量比想象中大很多,JSP页面写起来很琐碎,所以建议先把数据库和核心后端逻辑做完,再做页面美化,否则容易前松后紧。
6.2 答辩时的讲解思路与重点
答辩时间通常只有10-15分钟,很多同学在台上从头到尾把每个功能演示一遍,时间根本不够。更高效的讲法是抓主线:先讲清楚“这是一个面向运动鞋爱好者的推广型电商网站”,再讲系统架构和数据库设计,最后挑2-3个有技术难度的点做演示。
我建议重点准备这几个容易被追问的技术点:
- 动态SQL拼接和防SQL注入的原理:为什么要用PreparedStatement,为什么排序字段要白名单限制。
- 订单事务边界:为什么要在Service层控制事务而不是在Servlet层,事务回滚和连接关闭怎么保证。
- 商品表和图片表为什么要拆分:讲清楚一对多关系和多图展示的灵活性。
- 抽签活动怎么保证公平且不超卖:结合数据库唯一约束和随机取数的说法。
- 连接池的作用:对比每次创建连接和复用连接在性能上的差异。
还有一个加分细节:准备好10条左右的初始数据,包括不同分类的鞋、不同的价格区间、几组不同的用户,以及两张有代表性的轮播图。很多时候你演示的时候页面没数据灰蒙蒙一片,视觉效果打折扣。初始数据里最好有那种“折扣价明明已经低于原价很多”的商品,这样演示满减活动时更有说服力。
6.3 毕业设计/课程设计文档的配套思路
顺带说一下文档。设计文档是这类课题的重要评分项,结构可以参考:选题背景与意义、需求分析、系统设计、数据库设计、功能实现、系统测试、总结与展望。
需求分析里要写清楚角色划分:前台用户和后台管理员,各自有什么权限和操作;系统设计里画清楚总体架构图(如果你用一般绘图工具画,不要用代码工具画,能画清楚即可);数据库设计里要包括E-R图和每张表的字段说明;测试部分按功能模块写测试用例和结果。
文档里不要用夸张的“大数据”“人工智能”这类词,实事求是地写清楚用了什么技术、做了哪些功能,反而更能赢得老师认可。还有一点,文档里的代码不要大段粘贴,核心关键代码配合文字说明就好,贴太多反而显得凑字数。
7. 做完这个项目后的几点真实体会
整个项目做下来,我个人最深刻的感受是:这种看似“普通”的电商网站课题,恰恰是练手价值最高的类型。它不像图书管理系统那样简单,也不像高并发秒杀系统那样超出JavaWeb课程设计应有的范围。运动鞋店的产品推广网站处在刚刚好的中间位置——业务足够丰富,但每个模块的复杂度都可以控制在两周到一个月能完成的范围内。
如果让我重做一遍,我会在开始动手之前多花一天时间做原型设计和数据库评审,而不是急着敲代码。提前把表关系、页面跳转逻辑理清楚,后面代码返工的量会少很多。另外连接池和统一异常处理从一开始就要搭好,不要等到系统写了一半再回头补,否则后面加模块会遇到各种莫名其妙的问题。
最后分享一个实用技巧:开发时在本地建一个独立的测试环境,每次改动后发布到测试环境跑一遍核心流程,不要直接在正式演示环境上做开发。我自己吃过亏——演示前一天在本地改了数据库字段没同步到演示库,结果答辩现场页面直接报错。把这些边边角角提前处理好,答辩的底气就会足很多。这个课题做扎实了,后面你去看Spring Boot、MyBatis这些主流框架,会发现原来很多看起来高级的概念,本质上就是在解决你在Servlet/JSP开发中已经亲手处理过的那些问题。