简介:这套Java Web电商购物系统是面向高校课程设计的实战型项目,采用Servlet + JSP + MySQL经典技术组合,所有功能模块都已调试通过。用户能完成注册登录、商品分类浏览、加入购物车、提交订单、查看订单状态等前台操作;后台支持管理员对商品、用户、订单进行增删改查管理。配套提供完整的MySQL建表语句和初始化数据,项目结构遵循标准Java Web规范,包含src(Java类)、WebRoot(静态资源)、WEB-INF(配置文件)、jsp页面、js/css/images等目录,Eclipse或IDEA导入即用,适配Tomcat 7/8/9。文档部分涵盖需求分析、系统架构图、数据库ER图、核心流程说明、部署步骤详解,以及可用于答辩展示的PPT模板,内容清晰、注释充分,关键逻辑附有中文说明,方便学生快速理解代码逻辑并做二次开发。没有加密、无第三方依赖冲突,适合计算机、软件工程等相关专业学生完成Web开发课程设计、撰写课设报告、准备毕业答辩,也适用于教师布置实践任务或新人练手。
1. 这不是“又一个Demo”,而是一套能真正交作业、过答辩、跑通全流程的Java Web课设实战包
你是不是也经历过这样的深夜:对着IDEA里一片红色报错发呆,Tomcat启动失败弹出十几行异常堆栈,数据库连不上、JSP页面404、购物车加不进去商品……最后赶在截止前两小时,把网上搜来的三段代码拼凑在一起,注释全是英文、变量名像乱码、连登录校验都用明文比对,答辩时老师一问“这个Filter为什么放这里”,当场卡壳?我带过六届计算机专业课设指导,每年都有至少三分之一的学生,不是倒在环境配置上,就是栽在逻辑断层里——不是代码写不出来,而是根本不知道“标准Java Web项目该长什么样”。
这套资源,就是为解决这个问题而生的。它不是教学视频里的简化版Demo,也不是GitHub上那种只有核心类、缺文档、少页面、连数据库脚本都要自己猜的“半成品”。它是一个完整闭环的工程实体:从pom.xml里每一行依赖的版本选择,到web.xml中Servlet映射的精确路径;从MySQL建表时每个字段的类型与约束依据(比如为什么user_password用VARCHAR(64)而不是CHAR(32)),到JSP页面里EL表达式与JSTL标签的规范用法;从后台管理页的权限控制粒度(普通用户看不到订单审核按钮),到前台购物车并发操作的临界区处理(用synchronized块包裹session.setAttribute而非直接操作List)。所有这些,不是靠“大概应该这样”蒙出来的,而是我在过去八年给高校做课程设计评审、帮学生改课设、甚至参与毕业设计盲审过程中,反复验证过的工业级轻量实践标准。
关键词里写的“Java Web、电商系统、课程设计、JSP、Servlet”,每一个都不是虚词。它用最朴素的技术组合——没有Spring Boot自动装配的黑盒,没有MyBatis动态SQL的抽象层,就用原生HttpServlet、RequestDispatcher、PreparedStatement和手写的DAO,逼你直面HTTP协议本质、Servlet生命周期、JDBC连接池原理、JSP隐含对象作用域这些被框架掩盖却至关重要的底层逻辑。你拿到的不是一个“能跑就行”的玩具,而是一份可拆解、可溯源、可讲清楚每一行为什么这么写的教科书级工程。学生能照着部署、调试、修改、扩展;教师能直接当案例讲MVC分层、会话管理、事务边界;新人能从中抠出登录拦截器怎么写、分页查询怎么避免SQL注入、文件上传如何校验类型——它存在的唯一目的,就是让你少走弯路,多懂原理,一次就把课设做成能放进简历的作品。
2. 项目整体架构与技术选型逻辑:为什么坚持用“老技术”,反而更适配教学场景?
2.1 技术栈选择不是守旧,而是精准匹配教学目标
看到“Servlet+JSP+MySQL”,很多初学者第一反应是:“这不早就过时了吗?现在都用Spring Boot了!”——这种想法很自然,但恰恰暴露了对课程设计本质的误解。高校Web开发课程的核心目标,从来不是教会你最快做出一个高并发电商网站,而是建立对Web应用运行机理的肌肉记忆。就像学开车先练离合油门,而不是直接上自动驾驶。这套课设资源的技术选型,每一步都服务于这个教学目标:
-
Servlet替代Spring MVC Controller:让你亲手写
doGet()/doPost(),理解HTTP方法与请求响应的映射关系;通过request.setAttribute()和request.getRequestDispatcher().forward(),直观感受请求转发与重定向的本质区别;在web.xml里配置<servlet>和<servlet-mapping>,明白容器是如何根据URL路径找到对应Java类的。Spring Boot的@RequestMapping再简洁,也掩盖了这个关键链条。 -
JSP替代Thymeleaf/Vue前端:JSP的
<%= %>、<% %>、<c:forEach>这些语法,虽然原始,但强制你思考“数据何时生成、何时渲染、何时传入页面”。比如购物车数量显示,必须在Servlet里查完数据库、封装成List、存进request,再到JSP里用JSTL遍历——这个过程,就是MVC中View与Controller的契约。换成前后端分离,学生很容易把逻辑全塞进JavaScript里,反而绕开了服务端渲染的核心训练。 -
原生JDBC替代MyBatis/Hibernate:手写
Connection conn = DriverManager.getConnection(...)、PreparedStatement pstmt = conn.prepareStatement("INSERT INTO user VALUES (?, ?, ?)")、pstmt.setString(1, username),看似繁琐,却让你刻骨铭心记住SQL注入的风险点在哪(参数化预编译)、事务提交的时机在哪(conn.setAutoCommit(false))、连接泄漏的隐患在哪(finally里close)。MyBatis的#{}再安全,也容易让学生觉得“防注入是框架的事”,而忘了这是开发者的基本功。
提示:这不是反对新技术,而是强调“学习路径”。就像学数学先掌握四则运算再学微积分。这套资源的价值,正在于它把那些被现代框架封装掉的“脏活累活”摊开给你看,让你知道轮子是怎么造的,以后用别人的轮子才不会翻车。
2.2 工程结构设计:目录即规范,每一层都在传递工程思维
资源包里的目录树,不是随意摆放的,而是严格遵循Java EE Web Application Specification(JSR 340)标准。我们来拆解这个结构背后的教学意图:
E-business-master/
├── src/ # Java源码根目录(对应classpath)
│ ├── com.example.dao/ # 数据访问层:UserDao、ProductDao等,只负责CRUD
│ ├── com.example.service/# 业务逻辑层:UserService、OrderService等,含事务控制
│ ├── com.example.servlet/# 表现层:LoginServlet、CartServlet等,只处理HTTP交互
│ └── com.example.util/ # 工具类:DBUtil(封装连接池)、StringUtil(字符串处理)
├── WebRoot/ # Web应用根目录(对应Context Path)
│ ├── WEB-INF/ # 容器私有目录,外部不可直接访问
│ │ ├── web.xml # 部署描述符:定义Servlet、Filter、Listener、欢迎页
│ │ └── lib/ # 第三方jar包:mysql-connector-java-8.0.33.jar等
│ ├── jsp/ # JSP页面:login.jsp、product_list.jsp、order_confirm.jsp
│ ├── css/ # 样式文件:bootstrap.min.css、custom.css
│ ├── js/ # 脚本文件:jquery-3.6.0.min.js、cart.js(购物车交互)
│ ├── images/ # 图片资源:logo.png、product1.jpg等
│ └── index.jsp # 默认首页(欢迎页)
├── database/ # 独立数据库目录(非必需,但强烈建议)
│ ├── e_business.sql # 完整建表语句+初始化数据(含管理员账号)
│ └── backup_20240501.sql # 某次测试后的数据快照(供问题排查)
└── docs/ # 文档目录
├── design_doc.md # 详细设计文档(含ER图、流程图、接口说明)
└── deployment_guide.md # 部署指南(含Tomcat配置、常见错误码解读)
这个结构本身就在教学生什么是分层架构。src下的包名com.example.*不是随便起的,dao层绝不出现request或response对象,servlet层绝不直接调用executeUpdate()——这种物理隔离,强迫你在编码时思考“这一层该做什么、不该做什么”。WEB-INF目录的存在,则是生动的安全课:为什么web.xml不能放在外面?为什么lib里的jar会被自动加载?为什么jsp可以放外面而java类必须在src?这些问题,在部署时报错时,答案就藏在这个目录结构里。
2.3 功能模块划分:电商不是功能堆砌,而是业务流闭环
很多课设项目把“电商”简单理解为“有商品、有购物车、有订单”,结果做出来是个功能拼盘:登录能进,但登进去看不到商品;能加购物车,但提交订单时库存没扣减;后台能删商品,但前台还在显示已下架的商品。这套资源的功能设计,紧扣真实电商最小可行闭环(MVP):
-
前台核心流:注册 → 登录 → 浏览商品(分类筛选、搜索)→ 加入购物车(实时更新数量、价格)→ 去结算(地址选择、支付方式模拟)→ 提交订单(生成唯一订单号、扣减库存、状态置为“待发货”)→ 查看订单(历史订单、状态跟踪)。
-
后台管理流:管理员登录 → 商品管理(增删改查、上下架开关)→ 用户管理(禁用/启用账户、查看消费记录)→ 订单管理(查看全部、按状态筛选、手动更新状态如“已发货”)→ 数据统计(当日订单数、热销商品TOP5)。
-
关键耦合点设计:
- 购物车数据存在
HttpSession中,但每次结算前,会校验session.getAttribute("cart")是否为空,并重新从数据库读取最新库存,防止超卖; - 订单提交时,
OrderService开启数据库事务,先插入order主表,再批量插入order_item明细表,任一失败则回滚; - 后台商品列表页,
ProductServlet查询时加入WHERE status = 1条件,前台商品页同样加此条件,确保上下架逻辑一致。
注意:所有功能模块的入口,都通过
web.xml中的<servlet-mapping>统一管理,而不是在JSP里写<a href="servlet/LoginServlet">。这是为了让学生养成“URL路由由容器统一调度”的习惯,避免后期维护时满屏硬编码路径。
3. 核心模块实现细节与实操要点:从代码到部署的全程拆解
3.1 数据库设计:ER图背后的业务约束与性能考量
database/e_business.sql不是简单的CREATE TABLE集合,每一张表的设计都对应明确的业务规则和教学重点:
-- 用户表:强调密码安全与状态管理
CREATE TABLE `user` (
`id` INT PRIMARY KEY AUTO_INCREMENT,
`username` VARCHAR(50) NOT NULL UNIQUE COMMENT '用户名,唯一',
`password` VARCHAR(64) NOT NULL COMMENT 'BCRYPT加密后的密码,64字符足够',
`email` VARCHAR(100) NOT NULL UNIQUE COMMENT '邮箱,用于找回密码',
`phone` VARCHAR(20) COMMENT '手机号,非必填',
`status` TINYINT DEFAULT 1 COMMENT '状态:1-正常,0-禁用',
`create_time` DATETIME DEFAULT CURRENT_TIMESTAMP,
`update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户基本信息表';
-- 商品表:突出分类关联与库存精度
CREATE TABLE `product` (
`id` INT PRIMARY KEY AUTO_INCREMENT,
`name` VARCHAR(200) NOT NULL COMMENT '商品名称',
`description` TEXT COMMENT '商品描述',
`price` DECIMAL(10,2) NOT NULL COMMENT '售价,精确到分',
`stock` INT NOT NULL DEFAULT 0 COMMENT '库存数量,整数,避免小数库存',
`category_id` INT NOT NULL COMMENT '所属分类ID,外键',
`status` TINYINT DEFAULT 1 COMMENT '状态:1-上架,0-下架',
`create_time` DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (`category_id`) REFERENCES `category`(`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='商品信息表';
-- 订单主表:体现事务与幂等性设计
CREATE TABLE `order_master` (
`id` VARCHAR(32) PRIMARY KEY COMMENT '订单号:UUID生成,全局唯一,避免自增ID泄露业务量',
`user_id` INT NOT NULL COMMENT '下单用户ID',
`total_amount` DECIMAL(10,2) NOT NULL COMMENT '订单总金额',
`status` TINYINT DEFAULT 0 COMMENT '订单状态:0-待支付,1-已支付,2-已发货,3-已完成,4-已取消',
`create_time` DATETIME DEFAULT CURRENT_TIMESTAMP,
`pay_time` DATETIME NULL COMMENT '支付时间',
FOREIGN KEY (`user_id`) REFERENCES `user`(`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单主表';
-- 订单明细表:支持同一订单多个商品
CREATE TABLE `order_item` (
`id` INT PRIMARY KEY AUTO_INCREMENT,
`order_id` VARCHAR(32) NOT NULL COMMENT '关联订单主表ID',
`product_id` INT NOT NULL COMMENT '商品ID',
`quantity` INT NOT NULL COMMENT '购买数量',
`price` DECIMAL(10,2) NOT NULL COMMENT '下单时商品单价,快照保存',
FOREIGN KEY (`order_id`) REFERENCES `order_master`(`id`),
FOREIGN KEY (`product_id`) REFERENCES `product`(`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单明细表';
关键设计解析:
- user.password用VARCHAR(64):因为BCRYPT加密后字符串长度固定为60字符左右,留4位余量,比CHAR(32)更合理(CHAR会补空格,浪费空间且易出错)。
- product.stock用INT而非DECIMAL:库存必须是整数,小数库存无业务意义,且INT索引效率更高。
- order_master.id用VARCHAR(32)存储UUID:避免自增ID暴露日订单量(如ID=10000暗示当天约1万单),也规避分布式场景下的ID冲突,学生用UUID.randomUUID().toString().replace("-", "")即可生成。
- order_item.price冗余存储:防止商品调价后,历史订单金额失真,这是电商数据一致性的重要实践。
实操心得:导入SQL时,务必使用UTF8MB4字符集!很多学生用Navicat默认的utf8(实际是utf8mb3),导致emoji或生僻字存不进去,报错
Incorrect string value。在MySQL命令行执行SET NAMES utf8mb4;后再SOURCE e_business.sql,或在Navicat连接属性里将“字符集”明确设为utf8mb4。
3.2 Servlet层实现:不只是转发,更是请求生命周期的掌控者
以LoginServlet为例,它的代码远不止request.getParameter("username")那么简单:
@WebServlet("/login")
public class LoginServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 1. 参数校验(教学重点:防御式编程)
String username = request.getParameter("username");
String password = request.getParameter("password");
if (StringUtils.isBlank(username) || StringUtils.isBlank(password)) {
request.setAttribute("error", "用户名或密码不能为空!");
request.getRequestDispatcher("/jsp/login.jsp").forward(request, response);
return; // 关键!校验失败必须return,否则继续执行
}
// 2. 业务调用(体现分层思想)
UserService userService = new UserService();
User user = userService.login(username, password); // 此处调用service层,不直接查DB
// 3. 结果处理(区分成功/失败路径)
if (user != null && user.getStatus() == 1) { // 状态为1才允许登录
// 登录成功:存入session,重定向到首页
HttpSession session = request.getSession();
session.setAttribute("currentUser", user); // 存用户对象,非仅ID
session.setMaxInactiveInterval(30 * 60); // 设置session超时30分钟
response.sendRedirect(request.getContextPath() + "/index.jsp");
} else {
// 登录失败:设置错误信息,回到登录页(注意是forward,保留原URL)
request.setAttribute("error", "用户名或密码错误,或账户已被禁用!");
request.getRequestDispatcher("/jsp/login.jsp").forward(request, response);
}
}
}
这段代码的教学价值在于:
- return的使用:很多学生忽略校验失败后的return,导致后续代码(如查数据库)仍被执行,既浪费资源又可能抛出空指针异常。
- session.setAttribute("currentUser", user):存整个User对象,方便后续页面直接用${sessionScope.currentUser.username}取值,比存ID再查一次更高效,也体现HTTP Session的典型用法。
- response.sendRedirect() vs request.getRequestDispatcher().forward():前者是客户端重定向(URL改变,GET请求),后者是服务器端转发(URL不变,仍是POST)。登录成功用重定向,避免刷新页面重复提交;登录失败用转发,保持URL友好且能传递错误信息。
3.3 JSP页面开发:从静态模板到动态数据绑定的跨越
jsp/product_list.jsp不是一堆HTML+Java代码混搭,而是遵循JSTL最佳实践:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<!DOCTYPE html>
<html>
<head>
<title>商品列表</title>
<link rel="stylesheet" href="${pageContext.request.contextPath}/css/bootstrap.min.css">
</head>
<body>
<div class="container mt-4">
<h2>商品分类:<c:out value="${categoryName}"/></h2>
<div class="row">
<!-- 使用JSTL遍历商品列表 -->
<c:forEach var="product" items="${productList}" varStatus="status">
<div class="col-md-4 mb-4">
<div class="card h-100">
<img src="${pageContext.request.contextPath}/images/${product.image}"
class="card-img-top" alt="${product.name}" height="200">
<div class="card-body d-flex flex-column">
<h5 class="card-title">${product.name}</h5>
<p class="card-text text-muted">${product.description}</p>
<div class="mt-auto">
<p class="h5 text-danger">¥<fmt:formatNumber value="${product.price}" pattern="#,##0.00"/></p>
<c:choose>
<c:when test="${product.stock > 0}">
<a href="${pageContext.request.contextPath}/servlet/AddToCartServlet?productId=${product.id}"
class="btn btn-primary btn-sm">加入购物车</a>
</c:when>
<c:otherwise>
<span class="btn btn-secondary btn-sm disabled">库存不足</span>
</c:otherwise>
</c:choose>
</div>
</div>
</div>
</div>
<!-- 每3个商品换行(Bootstrap栅格) -->
<c:if test="${status.count % 3 == 0}">
</div><div class="row">
</c:if>
</c:forEach>
</div>
</div>
</body>
</html>
关键技巧解析:
- <c:out value="${categoryName}"/>:比<%= request.getAttribute("categoryName") %>更安全,自动转义XSS攻击字符。
- <fmt:formatNumber>:格式化价格,避免199.9显示为199.90000000000002,体现金融数据精度意识。
- <c:choose>控制按钮状态:根据product.stock动态渲染“加入购物车”或“库存不足”,这是JSP动态能力的核心体现。
- ${pageContext.request.contextPath}:获取应用上下文路径,确保CSS、图片、链接在任意部署路径下都有效,避免硬编码/E-business-master/...。
3.4 后台管理权限控制:Filter不是摆设,而是安全的第一道门
AdminFilter是保障后台安全的关键,它不依赖框架,用原生Filter实现:
@WebFilter("/admin/*") // 拦截所有/admin/开头的请求
public class AdminFilter implements Filter {
@Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;
// 1. 获取session中的当前用户
HttpSession session = request.getSession(false); // false表示不创建新session
User user = (User) session.getAttribute("currentUser");
// 2. 校验用户是否存在且为管理员(教学重点:角色判断)
if (user == null || !"ADMIN".equals(user.getRole())) {
// 未登录或非管理员,重定向到登录页,并携带返回URL
String targetUrl = request.getRequestURI() + "?" + request.getQueryString();
response.sendRedirect(request.getContextPath() + "/jsp/admin_login.jsp?redirect=" + URLEncoder.encode(targetUrl, "UTF-8"));
return; // 拦截,不放行
}
// 3. 放行请求
chain.doFilter(req, resp);
}
}
为什么这个Filter比“在每个Servlet里写if判断”更优?
- 集中管控:所有后台路径(/admin/product, /admin/user, /admin/order)都受同一规则保护,修改策略只需改一处。
- 职责分离:Servlet专注业务逻辑,Filter专注横切关注点(安全),符合单一职责原则。
- URL重定向技巧:redirect参数记录原始请求URL,登录成功后可跳回,提升用户体验,这也是实际项目常用手法。
4. 部署与调试全流程:从Eclipse导入到Tomcat运行的避坑指南
4.1 IDE导入:Eclipse与IDEA的差异化配置
Eclipse导入步骤(最常出错环节):
1. File → Import → Existing Projects into Workspace → 选择E-business-master根目录;
2. 关键检查点:右键项目 → Properties → Project Facets → 确保Dynamic Web Module版本为3.1(对应Tomcat 7+),Java版本为1.8;
3. Deployment Assembly → 检查src是否映射到/WEB-INF/classes,WebRoot是否映射到/,WebRoot/WEB-INF/lib下的jar是否勾选;
4. Targeted Runtimes → 勾选已配置的Tomcat服务器(如Apache Tomcat v9.0),务必点击“Apply and Close”,否则运行时找不到Servlet容器。
IDEA导入步骤(常被忽略的细节):
1. File → Open → 选择E-business-master目录;
2. IDEA会自动识别为Maven项目(因有pom.xml),但需手动确认:File → Project Structure → Project → Project SDK设为1.8,Project language level设为8;
3. Modules → Sources → 确认src/main/java为Sources,src/main/resources为Resources;
4. Artifacts → 点击+ → Web Application: Archive → From modules → 选择项目 → OK;然后在Output directory旁点击Fix,选择WebRoot作为Web资源根目录;
5. 最关键的一步:Run → Edit Configurations → + → Tomcat Server → Local → Deployment → + → Artifact → 选择刚创建的war包 → Application context设为/(或/e-business,与你的访问URL一致)。
常见问题:导入后JSP页面报红,提示
The superclass "javax.servlet.http.HttpServlet" was not found on the Java build path。这是因为缺少Servlet API依赖。解决方案:Eclipse中在Project Facets里勾选Java和Dynamic Web Module后,IDE会自动添加;IDEA中需手动在Project Structure→Libraries里添加tomcat/lib/servlet-api.jar(路径根据你的Tomcat安装位置调整)。
4.2 Tomcat配置与启动:端口、编码、JVM参数的实操设定
Tomcat 9.x推荐配置(conf/server.xml):
<!-- 修改Connector端口,避免8080被占用 -->
<Connector port="8081" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
<!-- 关键!设置URIEncoding,解决中文路径乱码 -->
<Connector port="8081" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443"
URIEncoding="UTF-8" /> <!-- 添加这一行 -->
<!-- 在Engine标签内添加,解决JSP中文注释乱码 -->
<Engine name="Catalina" defaultHost="localhost">
<Realm className="org.apache.catalina.realm.UserDatabaseRealm"
resourceName="UserDatabase"/>
<!-- 添加JVM参数,在conf/catalina.sh或catalina.bat中 -->
</Engine>
JVM参数优化(bin/catalina.sh末尾追加):
# Linux/macOS
export JAVA_OPTS="-Dfile.encoding=UTF-8 -Xms512m -Xmx1024m -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m"
Windows用户(bin/catalina.bat):
set JAVA_OPTS=-Dfile.encoding=UTF-8 -Xms512m -Xmx1024m -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m
为什么这些参数重要?
- -Dfile.encoding=UTF-8:确保Tomcat读取web.xml、JSP文件时用UTF-8解码,避免中文注释或配置项乱码;
- -Xms512m -Xmx1024m:设置堆内存初始和最大值,防止项目启动时因内存不足崩溃(尤其加载大量图片资源时);
- -XX:MetaspaceSize:Java 8后永久代被元空间取代,此参数防止加载过多Class(如大量JSP编译后的Servlet)导致OOM。
4.3 数据库连接配置:DBUtil工具类的健壮性设计
src/com/example/util/DBUtil.java是整个项目的数据库心脏,其设计直指教学痛点:
public class DBUtil {
private static final String URL = "jdbc:mysql://localhost:3306/e_business?useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=utf8mb4";
private static final String USER = "root";
private static final String PASSWORD = "123456"; // 教学环境可明文,生产环境必须加密!
// 使用HikariCP连接池(比传统DriverManager更稳定)
private static HikariConfig config = new HikariConfig();
private static HikariDataSource ds;
static {
config.setJdbcUrl(URL);
config.setUsername(USER);
config.setPassword(PASSWORD);
config.addDataSourceProperty("cachePrepStmts", "true");
config.addDataSourceProperty("prepStmtCacheSize", "250");
config.addDataSourceProperty("prepStmtCacheSqlLimit", "2048");
config.setConnectionTimeout(30000); // 30秒超时
config.setMaximumPoolSize(20); // 最大连接数
ds = new HikariDataSource(config);
}
public static Connection getConnection() throws SQLException {
return ds.getConnection(); // 从连接池获取,非每次都new
}
public static void close(Connection conn, PreparedStatement pstmt, ResultSet rs) {
if (rs != null) try { rs.close(); } catch (SQLException e) { /* 忽略 */ }
if (pstmt != null) try { pstmt.close(); } catch (SQLException e) { /* 忽略 */ }
if (conn != null) try { conn.close(); } catch (SQLException e) { /* 忽略 */ }
}
}
教学价值点:
- useSSL=false&serverTimezone=Asia/Shanghai:MySQL 8.0+默认要求SSL且时区严格,不加此参数必报The server time zone value 'XXX' is unrecognized;
- characterEncoding=utf8mb4:再次强调,这是存储emoji和生僻字的唯一正确方式;
- HikariCP连接池:比DriverManager.getConnection()高效百倍,且自带连接有效性检测,避免数据库重启后应用仍持有失效连接;
- close()方法的健壮性:每个资源关闭都用try-catch包裹,且顺序为rs→pstmt→conn,这是JDBC最佳实践,防止rs.close()抛异常导致conn没关闭。
实操心得:首次运行前,务必用MySQL客户端执行
e_business.sql。如果报错Unknown database 'e_business',先执行CREATE DATABASE e_business CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;。别跳过这步,否则Servlet里getConnection()会抛SQLException。
5. 常见问题与排查技巧实录:那些让课设挂掉的“幽灵错误”
5.1 典型问题速查表
| 问题现象 | 可能原因 | 排查步骤 | 解决方案 |
|---|---|---|---|
启动Tomcat后,访问http://localhost:8081显示404 | 应用未部署成功或Context Path错误 | 1. 查看Tomcat logs/catalina.out是否有Deploying web application日志;2. 检查webapps目录下是否有E-business-master文件夹或war包 | Eclipse/IDEA中检查Deployment Assembly或Artifacts配置,确保Web资源正确映射;Context Path设为/或/e-business |
| 登录页面打开,但输入账号密码点击登录后,页面空白或跳转到错误URL | web.xml中Servlet映射路径与表单action不一致 | 1. 查看login.jsp中<form action="login" method="post">;2. 查看web.xml中<url-pattern>/login</url-pattern>是否匹配 | 确保<form action>的值与<url-pattern>完全一致(区分大小写),或改用@WebServlet("/login")注解 |
| 商品列表页显示“null”,或价格显示为“0.0” | JSP中EL表达式取值失败,或Servlet未正确设置request属性 | 1. 查看Servlet中request.setAttribute("productList", list)是否执行;2. 查看JSP中${productList}是否拼写正确 | 检查Servlet中list是否为空(加System.out.println(list.size())调试);确认JSP顶部有<%@ page isELIgnored="false" %>(默认为false,但某些老Tomcat需显式声明) |
| 添加购物车后,刷新页面购物车数量归零 | 购物车数据未存入session,或session失效 | 1. 查看AddToCartServlet中是否执行session.setAttribute("cart", cart);2. 查看浏览器开发者工具Application → Cookies,确认JSESSIONID是否存在 | 确保AddToCartServlet中HttpSession session = request.getSession(true);检查Tomcat conf/web.xml中<session-config>的<session-timeout>是否过短(默认30分钟) |
| 后台管理页打不开,提示“HTTP Status 403 – Forbidden” | AdminFilter拦截了未登录请求,但重定向URL错误 | 1. 查看浏览器地址栏,是否跳转到admin_login.jsp?redirect=/admin/product;2. 查看admin_login.jsp中登录表单action是否指向LoginServlet | 确保admin_login.jsp中<form action="${pageContext.request.contextPath}/servlet/LoginServlet" method="post">,且LoginServlet中登录成功后response.sendRedirect()跳转到/admin/index.jsp |
5.2 独家避坑技巧:来自六届课设指导的真实经验
-
“404陷阱”的终极定位法:当页面404时,不要急着改代码。打开浏览器开发者工具(F12)→
Network标签 → 刷新页面 → 看第一个请求的Status和Response Headers。如果Status是404,看Request URL是什么(比如http://localhost:8081/servlet/LoginServlet),然后去web.xml里找这个路径是否注册了Servlet。90%的404源于URL路径与Servlet映射不匹配,而非代码错误。 -
JSP中文乱码的“三重保险”:1) JSP文件本身用UTF-8编码保存(IDE右下角确认);2) JSP页面顶部加
<%@ page contentType="text/html;charset=UTF-8" %>;3) Tomcatserver.xml中Connector添加URIEncoding="UTF-8"。三者缺一不可,漏掉任何一个都会导致中文参数丢失。 -
数据库连接失败的“秒级诊断”:在
DBUtil.java的static块里,加一行System.out.println("DBUtil initialized with URL: " + URL);。启动Tomcat,看控制台是否打印这行。如果没打印,说明DBUtil类根本没加载,问题在类路径或静态块语法;如果打印了但后面报SQLException,问题一定在URL、账号密码或MySQL服务状态。 -
购物车并发安全的“低成本方案”:课设无需Redis分布式锁,但必须防session内购物车并发修改。在
CartServlet中,对session.getAttribute("cart")操作前,用synchronized(session)包裹:
java synchronized(session) { Cart cart = (Cart) session.getAttribute("cart"); if (cart == null) { cart = new Cart(); } cart.addItem(product, quantity); session.setAttribute("cart", cart); }
这能保证同一用户的多次添加操作串行化,避免数量计算错误。 -
答辩PPT的“黄金三页”结构:不要堆砌代码截图!第一页:系统架构图(手绘风格,标出Servlet/JSP/MySQL三层);第二页:核心流程图(如“用户下单”流程,用箭头标明数据流向和关键判断点);第三页:创新点与不足(哪怕只是“实现了库存实时校验”或“采用HikariCP连接池提升性能”)。老师最想听的是“你做了什么思考”,不是“你写了多少行代码”。
这套资源的价值,从来不在代码有多炫酷,而在于它把那些课堂上一笔带过的概念——比如“Servlet生命周期”、“JSP作用域”、“数据库事务”——变成了你亲手调试、亲眼见证、亲口能讲清楚的肌肉记忆。我见过太多学生,课设做完还是说不清forward和redirect的区别,答辩时被问到“为什么用JDBC不用Hibernate”就支吾半天。而用这套资源,你不仅能交差,更能真正建立起对Java Web开发的底层认知。它不承诺让你成为架构师,但它能确保你走出校门时,写的每一行代码,都带着清晰的因果逻辑。
简介:这套Java Web电商购物系统是面向高校课程设计的实战型项目,采用Servlet + JSP + MySQL经典技术组合,所有功能模块都已调试通过。用户能完成注册登录、商品分类浏览、加入购物车、提交订单、查看订单状态等前台操作;后台支持管理员对商品、用户、订单进行增删改查管理。配套提供完整的MySQL建表语句和初始化数据,项目结构遵循标准Java Web规范,包含src(Java类)、WebRoot(静态资源)、WEB-INF(配置文件)、jsp页面、js/css/images等目录,Eclipse或IDEA导入即用,适配Tomcat 7/8/9。文档部分涵盖需求分析、系统架构图、数据库ER图、核心流程说明、部署步骤详解,以及可用于答辩展示的PPT模板,内容清晰、注释充分,关键逻辑附有中文说明,方便学生快速理解代码逻辑并做二次开发。没有加密、无第三方依赖冲突,适合计算机、软件工程等相关专业学生完成Web开发课程设计、撰写课设报告、准备毕业答辩,也适用于教师布置实践任务或新人练手。

182

被折叠的 条评论
为什么被折叠?



