这个项目标题一看就很眼熟,典型的Java Web课程设计或者毕业设计路线:JSP+Servlet+MySQL的汉服电商网站,带完整源码数据库,还要能调试部署跑起来。做过这类项目的同学应该都有体会,真正卡住你的往往不是业务代码怎么写,而是开发环境怎么搭、数据库怎么连、Tomcat怎么部署、改了页面为什么不生效这一类看起来“不是技术”的破事。这篇文章我从头到尾梳理一遍,从项目设计思路、表结构规划、核心功能实现,到最后的调试部署和排错手册,尽量讲透。
1. 项目整体设计与思路拆解
1.1 技术选型:为什么这个年代还有人选JSP
很多人一看到JSP就皱眉,觉得这技术太老,Spring Boot它不香吗?说句实在话,高校的Java Web课程到现在依然大量使用JSP+Servlet作为教学主线,课程设计和毕业设计更是重灾区。这个“福建汉服天下电子商务网站”就是典型的教学级项目,选JSP几点好处很明确:
- JSP本质上是Servlet,它把Java代码嵌进HTML页面里,对初学者来说“改完刷新就能看到效果”的反馈感很强,比前后端分离那套容易建立信心。
- Java EE体系的经典三层架构(表现层-业务层-数据访问层)在这类项目里展示得最直观,答辩的时候框架一讲,老师一听就懂。
- 部署环境要求低,一个Tomcat加一个MySQL就能跑,不用像微服务那样折腾注册中心、配置中心。
- 对毕设答辩来说,JSP项目翻车的概率比Spring Boot全家桶低得多,因为技术栈越复杂,出问题的环节就越多。
这套项目用的是最经典的JSP+Servlet+JDBC组合。JSP负责页面展示,Servlet负责接收请求、调度业务逻辑,JDBC负责和MySQL交互。虽然看着原始,但它完整地把HTTP请求处理的链路走了一遍,这个理解对你后续学任何Web框架都有帮助。
1.2 汉服电商的业务模型拆解
“福建汉服天下”这个名字一看就知道,这是一个区域性汉服文化主题的电商平台。汉服电商和普通服装电商最大的区别在哪儿?品类属性强——汉服往往按形制(襦裙、深衣、袄裙)、朝代风格、面料工艺来区分,客户下单前对尺码、形制、穿着场景有很强的搜索和筛选需求。
从电商网站的通用模型出发,这个项目至少要拆成前台和后台两块:
前台面向普通游客和注册用户,核心链路是:注册登录 → 浏览商品 → 添加购物车 → 提交订单 → 查看订单。辅以商品分类导航、关键词搜索、商品详情展示、个人信息中心这些功能。
后台面向管理员,核心是商品管理(增删改查)、订单处理(发货、取消)、用户管理、分类管理。部分做得细一点的项目还会加公告管理、数据统计。
这个业务模型覆盖了Web开发最常遇到的两类数据操作:一是面向用户的高频查询,二是面向管理员的后台维护。数据库表设计也是围绕这两条线展开的。
1.3 功能模块规划
我在实际拆解这类项目时习惯画一张功能清单,比直接看代码管用。这个汉服电商项目按标准划分应该有如下模块:
| 模块 | 子功能 | 角色 |
|---|---|---|
| 用户模块 | 注册、登录、退出、个人信息查看与修改 | 游客/用户 |
| 商品模块 | 商品列表、分类筛选、关键字搜索、商品详情 | 用户/管理员 |
| 购物车模块 | 加入购物车、修改数量、删除、清空 | 用户 |
| 订单模块 | 提交订单、订单列表、取消订单、订单状态更新 | 用户/管理员 |
| 后台管理 | 商品CRUD、分类管理、用户管理、订单管理 | 管理员 |
这些模块做完,一个课程设计的工作量基本就饱和了。如果还想加亮点,可以补上个人中心页面里展示“最近浏览记录”、用JSP实现数据导出Excel报表、在商品详情页嵌入汉服穿戴讲解的MP4视频——这几个功能正好也是热词里反复出现的,做出来之后答辩加分效果还是很明显的。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 开发环境搭建与核心细节解析
2.1 本地开发环境怎么配
别人发的源码要跑起来,第一步不是看代码,而是把环境对齐。这个项目涉及到的核心环境就是JDK、Tomcat、MySQL和IDEA,再配合Maven做依赖管理。
先说JDK版本。这类老项目通常基于JDK 1.8开发,不要一上来装个JDK 17、JDK 21,Tomcat版本跟JDK版本不匹配,项目直接启动不了。JDK 1.8虽然年头久,但是兼容性最强,一直到Tomcat 9都支持得很好。
Tomcat建议用8.5或者9.0,这两个版本对JSP和Servlet的老写法兼容性都很稳。Tomcat 10以后包名从javax.换成了jakarta.,如果源码里全是javax.servlet开头的导入,放Tomcat 10里直接编译报错。我用这类源码踩过一次这个坑,后来一律默认Tomcat 9,省心。
IDEA的话2022版本完全够用,老项目不要追求最新版IDEA,新版本对老技术的支持反而可能出现一些奇怪的提示问题。装完IDEA之后,把Maven配置好:
xml复制<mirror>
<id>aliyunmaven</id>
<mirrorOf>*</mirrorOf>
<name>阿里云公共仓库</name>
<url>https://maven.aliyun.com/repository/public</url>
</mirror>
Maven的settings.xml里加上阿里云镜像,配好后IDEA里设置Maven home path指向你本地的Maven目录,User settings file指向这个settings.xml。然后项目导入后右键pom.xml → Maven → Reload Project,依赖会自动下载。这一步不配置镜像的话,很多依赖会卡在下载进度条上半天不动,十有八九最后超时失败。
MySQL安装时选择5.7还是8.0,取决于源码里JDBC驱动用的版本。如果代码里连接串是com.mysql.jdbc.Driver,这多半是MySQL 5.x时期的写法,放到MySQL 8.x也能用但需要调整驱动类名。稳妥的做法是看项目lib目录或pom.xml里的mysql-connector版本,如果大于8.0,就把连接串写成这样:
java复制Class.forName("com.mysql.cj.jdbc.Driver");
String url = "jdbc:mysql://localhost:3306/hanfu_db?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&useSSL=false";
serverTimezone=Asia/Shanghai这个参数必须加,不然MySQL 8.0连接时会报时区错误。
2.2 数据库设计与增删改查
数据库是这个项目的核心,拿到源码第一件事应该是把sql文件导进去看表结构。标准的电商表结构长这样——我直接给你们列核心表:
sql复制-- 用户表
CREATE TABLE `tb_user` (
`id` INT PRIMARY KEY AUTO_INCREMENT,
`username` VARCHAR(50) NOT NULL UNIQUE,
`password` VARCHAR(100) NOT NULL,
`nickname` VARCHAR(50),
`phone` VARCHAR(20),
`email` VARCHAR(100),
`avatar` VARCHAR(255),
`create_time` DATETIME DEFAULT CURRENT_TIMESTAMP
);
-- 商品分类表
CREATE TABLE `tb_category` (
`id` INT PRIMARY KEY AUTO_INCREMENT,
`name` VARCHAR(50) NOT NULL,
`parent_id` INT DEFAULT 0,
`sort` INT DEFAULT 0
);
-- 商品表
CREATE TABLE `tb_product` (
`id` INT PRIMARY KEY AUTO_INCREMENT,
`category_id` INT,
`name` VARCHAR(200) NOT NULL,
`subtitle` VARCHAR(500),
`main_image` VARCHAR(255),
`detail` TEXT,
`price` DECIMAL(10,2) NOT NULL,
`stock` INT DEFAULT 0,
`status` TINYINT DEFAULT 1,
`create_time` DATETIME DEFAULT CURRENT_TIMESTAMP
);
-- 购物车表
CREATE TABLE `tb_cart` (
`id` INT PRIMARY KEY AUTO_INCREMENT,
`user_id` INT,
`product_id` INT,
`quantity` INT DEFAULT 1,
`checked` TINYINT DEFAULT 1,
KEY `idx_user_id` (`user_id`)
);
-- 订单表
CREATE TABLE `tb_order` (
`id` INT PRIMARY KEY AUTO_INCREMENT,
`order_no` VARCHAR(50) NOT NULL,
`user_id` INT,
`total_price` DECIMAL(10,2),
`status` TINYINT DEFAULT 0,
`receiver_name` VARCHAR(50),
`receiver_phone` VARCHAR(20),
`receiver_address` VARCHAR(255),
`create_time` DATETIME DEFAULT CURRENT_TIMESTAMP
);
-- 订单明细表
CREATE TABLE `tb_order_item` (
`id` INT PRIMARY KEY AUTO_INCREMENT,
`order_id` INT,
`product_id` INT,
`product_name` VARCHAR(200),
`product_image` VARCHAR(255),
`current_price` DECIMAL(10,2),
`quantity` INT
);
你看这个设计,用户表、商品表、订单表、订单明细表是电商的四大金刚,购物车表属于辅助表。订单为什么要拆主表和明细表?因为一个订单可能包含多件商品,订单表只存订单级别的信息(收货人、总价、状态),明细表存每一件商品的快照信息。注意明细表里的product_name和current_price是冗余字段,这不是浪费,而是刻意为之——万一以后商品改名、价格变动,历史订单里的快照信息不会跟着变,这是电商表设计的常识。
实际增删改查这块,JSP项目里最常用的是自己写一个DBUtil工具类,里面封装了获取连接和关闭资源的代码:
java复制public class DBUtil {
private static final String DRIVER = "com.mysql.cj.jdbc.Driver";
private static final String URL = "jdbc:mysql://localhost:3306/hanfu_db?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&useSSL=false";
private static final String USER = "root";
private static final String PASSWORD = "root";
static {
try {
Class.forName(DRIVER);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection(URL, USER, PASSWORD);
}
public static void close(ResultSet rs, PreparedStatement ps, Connection conn) {
if (rs != null) { try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } }
if (ps != null) { try { ps.close(); } catch (SQLException e) { e.printStackTrace(); } }
if (conn != null) { try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } }
}
}
这个工具类里有几个细节要注意:第一,USER和PASSWORD写死在这里,后期部署到别的机器上如果数据库密码不一样,改这一个文件就行;第二,Connection必须最后关,因为它是从数据库连接池拿的,如果先关Connection再关Statement会报错;第三,PreparedStatement用预编译问号传参,不要用字符串拼接SQL,既防注入又省心。
2.3 关键技术细节:图片上传、视频播放、Excel导出
这几个功能是热词里反复出现的,也是这类项目答辩时的加分项。
商品图片上传,JSP项目里最常见的做法是使用Apache Commons FileUpload组件。图片不要直接存数据库,数据库里只存图片的相对路径/upload/xxx.jpg,图片文件放到项目部署目录下的upload文件夹里。这样做的原因很简单:数据库读写二进制文件性能差,而且数据库体积会膨胀得很快。页面上引用图片时直接用相对路径就能访问,不然你还要专门写一个Servlet去数据库取图片再输出流,麻烦得要命。
JSP里实现MP4视频播放,这其实是前端的问题,HTML5的video标签直接搞定,关键是视频文件放哪、路径对不对。最常见的问题是把MP4文件放在WEB-INF目录下,那肯定访问不到,因为WEB-INF目录受容器保护,对外不可见。正确做法是在webapp根目录下建一个video文件夹,把MP4放进去,然后JSP里这么写:
html复制<video width="640" height="360" controls>
<source src="${pageContext.request.contextPath}/video/hanfu_intro.mp4" type="video/mp4">
您的浏览器不支持HTML5视频播放
</video>
注意${pageContext.request.contextPath}这个表达式,它会把项目上下文路径拼上,不然在部署时如果项目名不是ROOT,视频会404。
数据导出Excel是JSP项目里被问得最多的功能之一,一般用Apache POI 3.x的HSSFWorkbook来生成xls文件:
java复制// 设置响应头,让浏览器弹出下载框
response.setContentType("application/vnd.ms-excel");
response.setHeader("Content-Disposition", "attachment;filename=products.xls");
Workbook workbook = new HSSFWorkbook();
Sheet sheet = workbook.createSheet("商品列表");
// 创建表头行
Row headerRow = sheet.createRow(0);
String[] headers = {"商品ID", "商品名称", "分类", "价格", "库存"};
for (int i = 0; i < headers.length; i++) {
headerRow.createCell(i).setCellValue(headers[i]);
}
// 填充数据行
int rowIndex = 1;
for (Product p : productList) {
Row row = sheet.createRow(rowIndex++);
row.createCell(0).setCellValue(p.getId());
row.createCell(1).setCellValue(p.getName());
row.createCell(2).setCellValue(p.getCategoryName());
row.createCell(3).setCellValue(p.getPrice());
row.createCell(4).setCellValue(p.getStock());
}
workbook.write(response.getOutputStream());
workbook.close();
这段代码里有几个关键点:Content-Type必须设置成application/vnd.ms-excel,浏览器才能识别这是Excel文件;Content-Disposition里指定attachment和文件名,这样才能触发下载而不是在浏览器里打开;HSSFWorkbook只能写xls,如果要写xlsx得用XSSFWorkbook。写完记得调用close(),否则文件锁住不释放,第二次导出就会失败。
3. 核心功能实现与实操过程
3.1 用户登录注册与个人信息展示页面
用户模块是这类项目的基础,登录这层没做好,后面的购物车、订单全都白搭。JSP项目里的登录校验无非就是两种情况:Session存用户信息,Filter做访问控制。
登录Servlet的典型逻辑:
java复制protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String username = request.getParameter("username");
String password = request.getParameter("password");
User user = userDao.findByUsernameAndPassword(username, password);
if (user != null) {
// 登录成功,把用户信息存入Session
HttpSession session = request.getSession();
session.setAttribute("loginUser", user);
// 判断有没有记住密码的Cookie
if ("on".equals(request.getParameter("remember"))) {
Cookie cookie = new Cookie("username", username);
cookie.setMaxAge(7 * 24 * 60 * 60); // 7天有效期
cookie.setPath(request.getContextPath());
response.addCookie(cookie);
}
response.sendRedirect(request.getContextPath() + "/index.jsp");
} else {
// 登录失败,回显错误信息
request.setAttribute("errorMsg", "用户名或密码错误");
request.getRequestDispatcher("/login.jsp").forward(request, response);
}
}
记住密码这个功能别看简单,有两个坑:第一,Cookie里不要存明文密码,存个用户名方便回填输入框就行,真要存登录凭证也得用加密后的token;第二,Cookie设置path的时候要指定为request.getContextPath(),不然项目部署在带上下文路径的应用名下面时光标搜索Cookie的路径匹配不上,登录状态就丢了。
个人信息展示页面是热词里单独提到的,实现起来不复杂,从Session里取出loginUser对象,把昵称、手机号、邮箱、头像展示在页面表格里。改资料时先查Session里的用户ID,再根据ID更新数据库,更新成功后要重新查询用户信息并放回Session,否则页面上展示的还是修改前的旧数据。这个坑我见过好几个人踩,改了数据库,Session里不刷,页面上怎么看都没变。
3.2 商品列表与购物车
商品列表页的核心是分页查询。JSP项目里手写分页是最常见的,核心SQL就是LIMIT关键字:
sql复制SELECT * FROM tb_product
WHERE status = 1
ORDER BY create_time DESC
LIMIT ?, ?
分页查询需要两个参数:起始位置和每页条数。假设当前页是pageNum,每页显示8条,那起始位置就是(pageNum-1)*pageSize。JSP页面底部渲染上一页下一页按钮的时候,注意第一页和最后一页要把按钮置灰或者直接不渲染,否则用户狂点下一页,最后翻到空白页还以为程序出Bug了。
购物车功能我见过两种主流实现:一种是把购物车数据放Session里,另一种是持久化到数据库tb_cart表里。课程设计要求低、实现简单的用Session就够了,但是用户体验差——换个浏览器购物车就没了,而且Session存对象需要序列化,商品信息改动时购物车里的快照不会同步更新。我个人建议既然表结构里已经设计了tb_cart表,就老老实实走数据库。核心处理逻辑放在购物车Service里:
java复制public boolean addToCart(int userId, int productId, int quantity) {
// 先查购物车里有没有这个商品
Cart cart = cartDao.findByUserIdAndProductId(userId, productId);
if (cart != null) {
// 已存在则数量累加
cart.setQuantity(cart.getQuantity() + quantity);
return cartDao.updateQuantity(cart) > 0;
} else {
// 不存在则新增
Cart newCart = new Cart();
newCart.setUserId(userId);
newCart.setProductId(productId);
newCart.setQuantity(quantity);
return cartDao.insert(newCart) > 0;
}
}
这段代码的关键在于“先查后改”,而不是无脑insert。不加判断直接插入的话,同一个商品往购物车里加三次,表里就会出现三条一模一样的记录,用户看到的购物车列表里同一个商品出现三行,体验非常差。
3.3 商品后台管理与数据统计
后台管理界面不需要花哨,能用就行。商品管理无非是列表、新增、编辑、删除四个动作,对应着productDao里的selectAll、insert、updateById、deleteById。我只说两个实操中容易出问题的点。
第一,商品删除是假删除。表结构里有status字段,删除操作实际上是把status从1改成0,查询列表时带上WHERE status = 1条件。这样做的好处是,用户下单后订单明细里引用了商品ID,如果直接把商品记录物理删除,订单详情里的商品信息就成了悬空的死链,对账、统计全部受影响。电商逻辑里很少用物理删除,大部分是逻辑删除。
第二,新增商品时要处理图片上传。前端表单里enctype要设置成multipart/form-data,后端不能用request.getParameter()获取普通表单字段,得用FileUpload组件的ServletFileUpload解析整个请求流。有个参数叫fileSizeMax,是单文件大小限制,默认不设置的话可能被传一个1GB的大文件,直接把Tomcat卡死。建议设置:
java复制// 设置单文件上限 5MB
// 设置总请求上限 10MB
factory.setSizeMax(10 * 1024 * 1024);
factory.setFileSizeMax(5 * 1024 * 1024);
后台还可以加一个简单的数据统计页面,用SQL的聚合函数就能实现:统计用户总数、商品总数、订单总数、今日销售额。SQL不难,但做好后页面看起来很厉害,适合答辩展示。核心就这一句:
sql复制SELECT COUNT(*) AS userCount FROM tb_user;
SELECT COUNT(*) AS productCount FROM tb_product WHERE status = 1;
SELECT COUNT(*) AS orderCount FROM tb_order;
SELECT IFNULL(SUM(total_price), 0) AS todaySales FROM tb_order
WHERE DATE(create_time) = CURDATE() AND status IN (1, 2, 3);
3.4 调试部署全过程实录
环境配好、代码导入、数据库导入之后,真正要跑起来还得过部署这一关。老项目在IDEA里的部署和Spring Boot完全不一样,Spring Boot一键启动,JSP项目得配置Artifact和Tomcat。
在IDEA 2022里跑Tomcat项目的流程大概是:
- File → Project Structure → Artifacts,点加号 → Web Application: Exploded,选择项目。这一步是把项目打包成可以发布到Tomcat的Web应用格式。
- 如果项目是Maven管理的,检查Artifacts里有没有把Maven依赖加进去。没有的话运行时会报ClassNotFoundException:ServletContext资源里找不到某jar包里的类。
- 点击工具栏的Add Configuration → Tomcat Server → Local,在Deployment标签页点加号选择刚才建好的Artifact,Application context填
/hanfu或者其他任意路径。 - 配置好Tomcat后直接点Debug按钮启动,控制台日志里看到
INFO: Server startup in [xxxx] milliseconds就说明启动成功了。 - 浏览器访问
http://localhost:8080/hanfu/index.jsp,看到首页就说明部署成功。
这里最容易被卡住的就是第二步。IDEA在创建Tomcat运行配置后不会自动把外部库打包到Artifact里,必须手动在Artifact的Output Layout里把jar包拖到WEB-INF/lib目录下。不这么干的话,项目里用到的mysql驱动、jstl标签库、POI这些jar在任何页面执行到相关代码时会瞬间抛异常。
如果不用IDEA,也可以用最原始的方式部署:把项目打成war包扔到Tomcat的webapps目录。怎么打war包?在项目根目录执行Maven命令:
bash复制mvn clean package -DskipTests
打包成功后target目录下会生成项目名.war,把这个war复制到Tomcat的webapps目录下,启动Tomcat,它会自动解压war包并部署应用。访问地址就是http://localhost:8080/项目名/。这个方式更接近生产环境部署逻辑,也适合部署到自己买的学生服务器上给答辩老师演示。
4. 常见问题与排查技巧实录
4.1 jsp改了不生效
这是JSP项目里最经典的一个问题,热议词里专门有“jsp改了不生效”和“idea jsp修改后不用重启tomcat”的相关搜索。其实原因很简单:Tomcat自带JSP编译缓存,改动JSP后Tomcat不一定立刻检测到文件变化。
解决办法按优先级排序:
- 检查IDEA的Run/Debug Configurations里On frame deactivation选项是否选择了Update resources。选了之后IDEA窗口失焦时自动把修改过的资源同步到Tomcat的部署目录。
- 手动清理Tomcat缓存:关掉Tomcat,找到Tomcat部署目录下的work/Catalina/localhost/项目名目录,把这个目录删掉,再重启Tomcat。work目录里缓存了JSP编译后的Java文件和class文件,删掉后Tomcat会重新编译所有JSP。
- 如果改了静态资源(CSS、JS、图片)不生效,很可能是浏览器缓存,按Ctrl+F5强制刷新即可,有时候缓存特别顽固,需要用无痕窗口测试。
如果发现JSP改了不生效而且报编译错误,最常见的错误是页面里Java代码的变量名写错了、引入了不存在的类、或者JSTL标签库没有导入。JSP是运行时编译的,这些错误在你改完刷新页面的那一刻才暴露,不像Java代码在编译期就能发现。所以改JSP时一步一测,不要一次改十几处再刷新,错误堆栈会让你眼花缭乱。
4.2 数据库连接问题排查手册
数据库连不上这个问题占这类项目报错的一半以上,报错类型五花八门,我列个速查表,大家直接对照着找答案:
| 报错现象 | 根本原因 | 解决方案 |
|---|---|---|
| Access denied for user 'root'@'localhost' | 数据库用户名或密码错误 | 检查DBUtil里的用户名密码与MySQL实际是否一致 |
| Could not create connection to database server | MySQL服务没启动,或端口不是3306 | 检查MySQL服务状态,或连接串里加端口号 |
| The server time zone value is unrecognized | MySQL连接串缺少时区参数 | 连接串末尾加serverTimezone=Asia/Shanghai |
| Public Key Retrieval is not allowed | MySQL 8.0的认证插件安全策略 | 连接串加allowPublicKeyRetrieval=true |
| Unknown database 'hanfu_db' | 数据库不存在 | 先执行sql脚本导入数据库 |
| Communications link failure | 局域网或服务器上IP/端口不通 | 检查防火墙放行3306,服务端配置bind-address |
数据库这块还有一个很隐蔽的坑,就是MySQL 8.0的密码加密规则。有时你明明密码正确,程序却报认证失败,这是因为MySQL 8默认用caching_sha2_password插件,老版本JDBC驱动不认。解决办法是升级mysql-connector-java到8.0以上版本,或者在MySQL里执行:
sql复制ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '你的密码';
JSP项目普遍用的JDK 1.8搭配的老代码,对MySQL 5.7的支持更平滑,如果是自己搭环境,直接上5.7省去后续加密规则、时区、驱动匹配一堆麻烦。
4.3 Maven依赖下载与构建问题
如果项目里带了Maven的pom.xml,构建时经常会遇到依赖下载失败。默认中央仓库加载慢是主因,在上面已经说过用阿里云镜像解决。除了镜像之外,还有一个常见问题是JDK版本和Maven编译插件不匹配。老项目的pom.xml里maven-compiler-plugin可能写的是1.8(即Java 8),如果本机JDK版本太高,编译时会报不支持发行版本错误。解决办法是在pom.xml里把那几个版本号统一改成你本机的版本:
xml复制<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
IDEA里还要同步设置:Settings → Build Tools → Maven → Importing → JDK for importer,选成JDK 1.8,然后Settings → Java Compiler里把Target bytecode version选成8。这几处版本不一致会导致IDEA编译不报错但运行时报UnsupportedClassVersionError。
4.4 中文乱码问题全家桶
JSP项目里的中文乱码是个绕不开的老大难。乱码的原因本质上就一个:浏览器、JSP、数据库、MySQL连接串这四处的字符集编码不一致。你得把这四层全部统一成UTF-8,缺一环都会乱。
第一层,JSP页面文件本身的编码,在页面顶部声明:
jsp复制<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
第二层,POST请求参数的编码,在每个Servlet的doPost方法最前面加:
java复制request.setCharacterEncoding("UTF-8");
第三层,数据库连接串,在前面说过了,加characterEncoding=UTF-8。
第四层,数据库表本身的字符集,建库时统一指定:
sql复制CREATE DATABASE hanfu_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
这里为什么要用utf8mb4而不用utf8?因为MySQL里的utf8最多只支持3字节的字符,而像生僻字、emoji这些4字节字符根本存不进去。汉服商品的详情描述里偶尔会用到一些特殊符号,表结构如果用的老式utf8,插入就会报Incorrect string value错误。utf8mb4才是真正完整的UTF-8。
验证编码是否统一有个快速办法:直接连MySQL跑一句show variables like 'character_set%',如果Character_set_client、Character_set_connection、Character_set_database都是utf8mb4,那数据库维度基本没问题。剩下的问题就在代码层和HTTP层。
5. 从毕业设计角度说几句大实话
最后分享一点我个人的实操体会。这类JSP电商项目拿到手,最忌讳一上来就盯着代码看。正确顺序应该是:先把数据库导出来看表结构,理解业务数据怎么流转;再把项目跑起来点一遍功能,知道哪个功能对应哪个页面;最后才是结合源码去读核心逻辑。三步走下来,答辩的时候被问到任何问题你都能接住,因为你在脑子里已经有了完整的项目地图。
改代码的时候记得分批验证,不要一口气改完十个文件再启动。JSP项目不像Spring Boot有热部署和详细的错误上下文,它报错信息相对原始,一次改太多地方,出错了你根本定位不到哪个改动引入的Bug。改一个功能点,重启一次,验证通过再进行下一个,虽然看起来慢,实际踩坑最少。
从技术拓展的角度,这个项目往后还有不少进化空间:数据库连接换成Druid连接池、DAO层改成MyBatis、前端引入Vue做前后端分离、部署到云服务器用Nginx做反向代理。这些都是不错的进阶方向,完全可以作为毕业设计后续“展望”部分的素材,让答辩老师觉得你有技术敏感性。尤其是Druid连接池,在答辩时说是“为以后生产环境做准备”,比单纯说“用DriverManager获取连接”要有说服力得多,而且Druid的监控页面本身也是加分展示项。
不过话说回来,把当前这套JSP+Servlet+MySQL的经典组合真正弄懂弄透,本身就是一件含金量不低的事情。HTTP是无状态的,但Session和Cookie可以让它记住你;数据库操作是重复枯燥的,但封装好JDBC工具类后代码会清爽很多;页面展示是繁杂琐碎的,但JSTL标签库引入后JSP也能写得很优雅。这些基本功,不管以后换什么框架,都还是那套底层逻辑。技术的壳一直在变,HTTP协议、关系型数据库、会话管理这些内核是不会变的。
