JSP+Servlet蛋糕店网站毕设实战:环境搭建、数据库设计与订单事务避坑指南
简介这是一套基于JSP与Servlet技术栈实现的JavaWeb蛋糕店售卖网站完整源码面向正在学习JavaWeb开发、需要课程设计或毕业设计参考的初学者与进阶开发者。项目采用IDEA 2017.3.5开发运行于Tomcat 8.5.35数据库为MySQL 5.7并引入c3p0连接池与DBUtil工具类技术选型贴近企业常见实践。压缩包共290个文件约19.11MB其中58个jsp页面承载前后台视图55个java文件实现业务逻辑另有20个xml配置、14个js脚本、12个css样式及82张jpg商品图等静态资源结构完整。功能覆盖前台商品推荐与分类展示、购物车增删改、用户注册登录、个人信息与收货地址维护、下单付款、订单查询及关键字搜索后台支持订单状态流转、用户管理、商品类目与商品信息的增删改查。目前已有127人学习适合对照源码理解MVC分层、数据库连接池配置与前后台交互流程快速搭建可运行的电商练手项目。1. 蛋糕店售卖网站为什么 JSP Servlet 至今仍是毕设最稳的起点如果你正在为毕设选题发愁或者刚学完 JavaWeb 想找一个能跑通全流程的项目练手蛋糕店售卖网站是个被低估的选择。它不像商城那样庞大到无从下手也不像博客那样单薄到撑不起论文。商品展示、分类浏览、购物车、下单、后台管理——电商的核心链路一个不少但业务边界清晰用 JSP Servlet 就能完整实现不需要被 Spring 全家桶的配置淹没。我见过太多人一上来就冲 SpringBoot结果光依赖版本冲突就耗掉一周业务代码一行没写。JSP Servlet 的好处在于请求怎么进来、参数怎么取、页面怎么渲染每一步都是透明的。你写的每一行代码都能在浏览器里看到直接反馈这对建立信心太重要了。这篇文章会从零开始把蛋糕店网站的环境搭建、数据库设计、核心功能实现、部署打包完整走一遍中间踩过的坑也会如实交代。适合有 Java 基础、学过 HTML 和 SQL、想用传统技术栈完成一个完整 Web 项目的同学。2. 环境搭建与项目骨架从 IDEA 新建 JSP 项目到 Tomcat 跑通第一个页面2.1 工具链选型IDEA Tomcat MySQL 的版本搭配工具链的版本搭配是第一个容易翻车的地方。我一般推荐这套组合JDK 8 或 JDK 11、Tomcat 8.5 或 9.0、MySQL 5.7 或 8.0、IntelliJ IDEA 社区版或旗舰版都行。为什么不用 JDK 17因为 Tomcat 9 以下的版本对高版本 JDK 的模块化系统支持不完善容易出ClassNotFoundException或者IllegalAccessError。Tomcat 10 虽然支持 JDK 17但它把javax.servlet包名改成了jakarta.servlet网上绝大多数教程和代码片段还是javax的写法新手照着敲会大面积报错。MySQL 8.0 和 5.7 的区别主要在驱动类名和连接 URL 参数上。8.0 用com.mysql.cj.jdbc.DriverURL 需要加时区参数serverTimezoneAsia/Shanghai5.7 用com.mysql.jdbc.Driver不加时区也能跑。我建议直接用 8.0因为新装环境基本都是 8.0没必要降级。IDEA 新建项目时选 “Java Enterprise” 还是 “Java”如果你用的是旗舰版选 Java Enterprise 可以直接勾选 Web Application 模板自动生成web.xml和目录结构。社区版没有这个模板需要手动创建src/main/webapp/WEB-INF目录和web.xml文件。手动创建也不复杂但要注意目录层级不能错。2.2 目录结构与 web.xml 配置3 个必须检查的路径标准 Maven Web 项目的目录结构是这样的cake-shop/ ├── pom.xml ├── src/ │ ├── main/ │ │ ├── java/ │ │ │ └── com/cake/ │ │ │ ├── dao/ │ │ │ ├── entity/ │ │ │ ├── servlet/ │ │ │ └── util/ │ │ ├── resources/ │ │ └── webapp/ │ │ ├── WEB-INF/ │ │ │ └── web.xml │ │ ├── css/ │ │ ├── js/ │ │ ├── images/ │ │ └── index.jsp三个必须检查的路径第一webapp目录必须在src/main/下不是src/下第二WEB-INF目录名必须全大写里面的web.xml文件名也必须全小写第三JSP 文件放在webapp根目录或子目录下不要放在WEB-INF里面否则浏览器无法直接访问。web.xml的版本声明也要注意。如果用 Servlet 4.0Tomcat 9头部的web-app标签版本写4.0如果用 Servlet 3.1Tomcat 8.5写3.1。写错了 Tomcat 启动时会报解析错误。?xml version1.0 encodingUTF-8? web-app xmlnshttp://xmlns.jcp.org/xml/ns/javaee xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xsi:schemaLocationhttp://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd version4.0 welcome-file-list welcome-fileindex.jsp/welcome-file /welcome-file-list /web-app这段配置只做了一件事指定index.jsp为默认首页。welcome-file-list里的文件名要和webapp根目录下的实际文件名完全一致大小写敏感。2.3 pom.xml 依赖servlet-api 和 mysql-connector 的 scope 陷阱Maven 项目的pom.xml里Servlet API 的依赖必须加providedscope。因为 Tomcat 运行时自带 Servlet API 的 jar 包如果你打成compilescope部署时会和 Tomcat 自带的包冲突报ClassCastException或者NoSuchMethodError。dependencies !-- Servlet APIscope 必须是 provided -- dependency groupIdjavax.servlet/groupId artifactIdjavax.servlet-api/artifactId version4.0.1/version scopeprovided/scope /dependency !-- JSP API同样 provided -- dependency groupIdjavax.servlet.jsp/groupId artifactIdjavax.servlet.jsp-api/artifactId version2.3.3/version scopeprovided/scope /dependency !-- MySQL 驱动scope 是 compile打包时要带上 -- dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId version8.0.33/version /dependency !-- JSTL 标签库用于 JSP 页面循环和判断 -- dependency groupIdjavax.servlet/groupId artifactIdjstl/artifactId version1.2/version /dependency /dependenciesJSTL 的依赖容易被忽略。如果你在 JSP 里用c:forEach标签循环展示蛋糕列表没有这个依赖会报The absolute uri: http://java.sun.com/jsp/jstl/core cannot be resolved。注意 JSTL 1.2 的 groupId 是javax.servlet不是javax.servlet.jsp.jstl后者是旧版本。2.4 在 IDEA 里配置 Tomcat 并跑通第一个 JSP 页面IDEA 旗舰版配置 Tomcat 的入口在 Run → Edit Configurations → 左上角 → Tomcat Server → Local。关键配置项有三个Application server 指向你解压的 Tomcat 目录HTTP port 默认 8080如果被占用改成 8081Deployment 标签页里点 → Artifact选择cake-shop:war exploded。war exploded模式支持热部署改完 JSP 不用重启 Tomcat 就能看到效果。社区版没有 Tomcat Server 选项需要装 Smart Tomcat 插件。装完后在 Run → Edit Configurations 里选 Smart Tomcat配置 Tomcat 路径和 webapp 目录。配置完成后在webapp下新建index.jsp写一行h1蛋糕店首页/h1点运行。浏览器打开http://localhost:8080/cake-shop/看到标题就说明环境通了。如果报 404检查 Deployment 里的 Application context 是不是/cake-shop访问 URL 要带上这个前缀。如果报 500看 IDEA 控制台的异常堆栈大概率是web.xml版本声明和 Tomcat 版本不匹配。3. 数据库设计与实体建模蛋糕品类、订单状态和用户角色的表结构怎么定3.1 五张核心表从蛋糕商品到订单明细的字段设计蛋糕店网站的数据库不需要太复杂五张表就能覆盖核心业务用户表、蛋糕商品表、商品分类表、订单表、订单明细表。我一般会再加一张管理员表但为了简化可以把管理员和普通用户合并到一张表里用role字段区分。CREATE DATABASE cake_shop DEFAULT CHARACTER SET utf8mb4; USE cake_shop; -- 用户表role 字段区分普通用户和管理员 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), role TINYINT DEFAULT 0 COMMENT 0-普通用户 1-管理员, create_time DATETIME DEFAULT CURRENT_TIMESTAMP ); -- 分类表 CREATE TABLE category ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(50) NOT NULL, sort_order INT DEFAULT 0 ); -- 蛋糕商品表 CREATE TABLE cake ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(100) NOT NULL, category_id INT, price DECIMAL(10,2) NOT NULL, stock INT DEFAULT 0, image VARCHAR(200), description TEXT, status TINYINT DEFAULT 1 COMMENT 1-上架 0-下架, FOREIGN KEY (category_id) REFERENCES category(id) ); -- 订单表 CREATE TABLE orders ( id INT PRIMARY KEY AUTO_INCREMENT, user_id INT NOT NULL, total_price DECIMAL(10,2) NOT NULL, status TINYINT DEFAULT 0 COMMENT 0-待付款 1-已付款 2-已发货 3-已完成 4-已取消, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (user_id) REFERENCES user(id) ); -- 订单明细表 CREATE TABLE order_item ( id INT PRIMARY KEY AUTO_INCREMENT, order_id INT NOT NULL, cake_id INT NOT NULL, quantity INT NOT NULL, price DECIMAL(10,2) NOT NULL, FOREIGN KEY (order_id) REFERENCES orders(id), FOREIGN KEY (cake_id) REFERENCES cake(id) );几个字段设计的细节值得展开。price用DECIMAL(10,2)而不是FLOAT或DOUBLE因为浮点数在金额计算时会出现精度丢失0.1 0.2 不等于 0.3 这种问题在订单金额上绝对不能出现。orders表名用复数因为order是 SQL 关键字建表时不报错但查询时容易出问题。订单状态用TINYINT加注释比用字符串存储更省空间查询效率也更高。3.2 实体类与 DAO 层JDBC 工具类的封装与连接池的取舍实体类就是和数据库表一一对应的 Java 类字段名用驼峰命名对应下划线的列名。比如category_id对应categoryIdcreate_time对应createTime。手写 getter/setter 太累IDEA 里 Alt Insert 可以自动生成。DAO 层负责所有数据库操作。最基础的做法是写一个DBUtil工具类封装获取连接、关闭资源的方法public class DBUtil { private static final String URL jdbc:mysql://localhost:3306/cake_shop?useSSLfalseserverTimezoneAsia/ShanghaicharacterEncodingutf8; private static final String USER root; private static final String PASSWORD 你的密码; static { try { Class.forName(com.mysql.cj.jdbc.Driver); } catch (ClassNotFoundException e) { throw new RuntimeException(MySQL 驱动加载失败, e); } } public static Connection getConnection() throws SQLException { return DriverManager.getConnection(URL, USER, PASSWORD); } public static void close(Connection conn, PreparedStatement ps, ResultSet rs) { // 依次关闭每个都 try-catch 防止前面的异常影响后面的关闭 try { if (rs ! null) rs.close(); } catch (SQLException e) { e.printStackTrace(); } try { if (ps ! null) ps.close(); } catch (SQLException e) { e.printStackTrace(); } try { if (conn ! null) conn.close(); } catch (SQLException e) { e.printStackTrace(); } } }这个工具类每次调用getConnection()都会新建一个物理连接性能不高但够用。如果要做连接池可以用 Druid 或 HikariCP但配置复杂度会上升。毕设项目用原生 JDBC 完全够答辩时老师也不会因为没用连接池扣分。注意 URL 里的useSSLfalse和serverTimezoneAsia/ShanghaiMySQL 8.0 不加这两个参数会报连接错误。3.3 分页查询LIMIT 偏移量计算和总数统计的两个 SQL蛋糕列表页需要分页每页显示 8 个蛋糕。分页查询需要两条 SQL一条查当前页的数据一条查总记录数。// 查询当前页数据 public ListCake findByPage(int page, int pageSize) { ListCake list new ArrayList(); String sql SELECT * FROM cake WHERE status 1 ORDER BY id DESC LIMIT ?, ?; try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setInt(1, (page - 1) * pageSize); // 偏移量 ps.setInt(2, pageSize); ResultSet rs ps.executeQuery(); while (rs.next()) { Cake cake new Cake(); cake.setId(rs.getInt(id)); cake.setName(rs.getString(name)); cake.setPrice(rs.getBigDecimal(price)); cake.setImage(rs.getString(image)); list.add(cake); } } catch (SQLException e) { e.printStackTrace(); } return list; } // 查询总记录数 public int countAll() { String sql SELECT COUNT(*) FROM cake WHERE status 1; try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql); ResultSet rs ps.executeQuery()) { if (rs.next()) return rs.getInt(1); } catch (SQLException e) { e.printStackTrace(); } return 0; }LIMIT的第一个参数是偏移量计算公式是(当前页码 - 1) * 每页条数。页码从 1 开始第 1 页偏移量是 0第 2 页偏移量是 8以此类推。总页数用Math.ceil(totalCount * 1.0 / pageSize)计算注意要转成浮点数再除否则整数除法会丢精度。比如总数 17 条每页 8 条17 / 8 2但实际需要 3 页。4. Servlet 请求处理与 JSP 页面渲染从商品列表到购物车的完整链路4.1 Servlet 生命周期init、service、destroy 在蛋糕店项目里的实际体现Servlet 的生命周期分三个阶段init()在 Servlet 第一次被访问时调用一次service()每次请求都调用destroy()在 Tomcat 关闭时调用。在蛋糕店项目里CakeListServlet的init()可以用来加载数据库驱动或者初始化缓存但大多数情况下不需要重写。service()方法会根据请求方式GET/POST分发到doGet()或doPost()我们实际写业务逻辑是在这两个方法里。一个容易踩的坑Servlet 是单例的多个请求共享同一个 Servlet 实例。所以不要在 Servlet 里定义成员变量来存请求相关的数据否则并发时会串数据。所有请求相关的变量都必须在方法内部声明为局部变量。WebServlet(/cake/list) public class CakeListServlet extends HttpServlet { // 不要在这里定义 request 相关的成员变量 Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 1. 获取页码参数默认第 1 页 String pageStr request.getParameter(page); int page (pageStr null || pageStr.isEmpty()) ? 1 : Integer.parseInt(pageStr); int pageSize 8; // 2. 调用 DAO 查询数据 CakeDao cakeDao new CakeDao(); ListCake cakeList cakeDao.findByPage(page, pageSize); int totalCount cakeDao.countAll(); int totalPages (int) Math.ceil(totalCount * 1.0 / pageSize); // 3. 把数据存入 request 域转发给 JSP request.setAttribute(cakeList, cakeList); request.setAttribute(currentPage, page); request.setAttribute(totalPages, totalPages); // 4. 转发到 JSP 页面渲染 request.getRequestDispatcher(/cake_list.jsp).forward(request, response); } }WebServlet(/cake/list)注解代替了web.xml里的servlet和servlet-mapping配置更简洁。访问路径是http://localhost:8080/cake-shop/cake/list?page2。request.setAttribute()存的数据只在当前请求的转发过程中有效转发用forward()浏览器地址栏不变。如果用sendRedirect()重定向request 域的数据会丢失。4.2 JSP 页面用 JSTL 循环渲染蛋糕列表c:forEach 和 c:if 的配合JSP 里尽量避免写 Java 代码块% %用 JSTL 标签和 EL 表达式更清晰。展示蛋糕列表用c:forEach循环% page contentTypetext/html;charsetUTF-8 languagejava % % taglib prefixc urihttp://java.sun.com/jsp/jstl/core % % taglib prefixfmt urihttp://java.sun.com/jsp/jstl/fmt % html headtitle蛋糕列表/title/head body div classcake-grid c:forEach items${cakeList} varcake div classcake-card img src${pageContext.request.contextPath}/images/${cake.image} alt${cake.name} h3${cake.name}/h3 p classprice¥fmt:formatNumber value${cake.price} pattern#.00//p a href${pageContext.request.contextPath}/cake/detail?id${cake.id}查看详情/a /div /c:forEach /div !-- 分页导航 -- div classpagination c:if test${currentPage 1} a href${pageContext.request.contextPath}/cake/list?page${currentPage - 1}上一页/a /c:if span第 ${currentPage} / ${totalPages} 页/span c:if test${currentPage totalPages} a href${pageContext.request.contextPath}/cake/list?page${currentPage 1}下一页/a /c:if /div /body /html${pageContext.request.contextPath}获取的是项目部署的上下文路径也就是/cake-shop。所有链接和静态资源路径都要带上它否则部署到 Tomcat 后路径会 404。fmt:formatNumber用来格式化价格pattern#.00保证显示两位小数。c:if控制上一页/下一页按钮的显示条件第一页不显示上一页最后一页不显示下一页。4.3 购物车实现Session 存储 vs 数据库存储的选型对比购物车有两种实现方式存在 Session 里或者存数据库。Session 方式简单不需要建表但用户换浏览器或清 Cookie 后购物车就丢了。数据库方式持久化好但每次增删改查都要操作数据库代码量大。对于蛋糕店项目我建议用 Session 存购物车。因为毕设演示时不会涉及换设备登录的场景Session 足够用而且代码量少很多。购物车的结构可以用MapInteger, Integerkey 是蛋糕 IDvalue 是数量。WebServlet(/cart/add) public class CartAddServlet extends HttpServlet { Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { int cakeId Integer.parseInt(request.getParameter(cakeId)); int quantity Integer.parseInt(request.getParameter(quantity)); // 从 Session 获取购物车没有就新建 HttpSession session request.getSession(); MapInteger, Integer cart (MapInteger, Integer) session.getAttribute(cart); if (cart null) { cart new HashMap(); } // 如果购物车里已有该商品数量累加否则新增 cart.put(cakeId, cart.getOrDefault(cakeId, 0) quantity); session.setAttribute(cart, cart); response.sendRedirect(request.getContextPath() /cart/list); } }getOrDefault(cakeId, 0)是 Java 8 的方法如果 key 不存在返回默认值 0。sendRedirect()重定向到购物车列表页防止用户刷新页面时重复提交。注意重定向的路径也要带request.getContextPath()。4.4 订单提交事务处理与库存扣减的原子性保证下单是最容易出 bug 的环节。用户点击提交订单后需要做三件事往orders表插一条订单记录、往order_item表插多条明细、扣减cake表的库存。这三步必须在一个事务里完成任何一步失败都要回滚。public boolean createOrder(int userId, MapInteger, Integer cart) { Connection conn null; try { conn DBUtil.getConnection(); conn.setAutoCommit(false); // 开启事务 // 1. 计算总价并插入订单 BigDecimal totalPrice BigDecimal.ZERO; for (Map.EntryInteger, Integer entry : cart.entrySet()) { Cake cake getCakeById(conn, entry.getKey()); totalPrice totalPrice.add(cake.getPrice().multiply(new BigDecimal(entry.getValue()))); } int orderId insertOrder(conn, userId, totalPrice); // 2. 插入订单明细并扣减库存 for (Map.EntryInteger, Integer entry : cart.entrySet()) { int cakeId entry.getKey(); int qty entry.getValue(); // 扣库存时加 stock qty 条件防止超卖 String sql UPDATE cake SET stock stock - ? WHERE id ? AND stock ?; try (PreparedStatement ps conn.prepareStatement(sql)) { ps.setInt(1, qty); ps.setInt(2, cakeId); ps.setInt(3, qty); if (ps.executeUpdate() 0) { conn.rollback(); return false; // 库存不足回滚 } } insertOrderItem(conn, orderId, cakeId, qty); } conn.commit(); return true; } catch (SQLException e) { try { if (conn ! null) conn.rollback(); } catch (SQLException ex) { ex.printStackTrace(); } e.printStackTrace(); return false; } finally { try { if (conn ! null) conn.setAutoCommit(true); conn.close(); } catch (SQLException e) { e.printStackTrace(); } } }关键点在UPDATE cake SET stock stock - ? WHERE id ? AND stock ?。stock ?这个条件保证了库存不会被扣成负数。如果executeUpdate()返回 0说明库存不足立即回滚。所有数据库操作共用同一个Connection不能从DBUtil重新获取连接否则事务不生效。5. 避坑与排查JSP Servlet 项目里最容易翻车的 5 个地方5.1 中文乱码POST 请求和 GET 请求的解决方式不一样现象表单提交后数据库里存的中文变成???或者乱码。原因Tomcat 8 以上默认用 UTF-8 解码 GET 请求但 POST 请求体默认用 ISO-8859-1 解码。JSP 页面如果没声明pageEncoding编译时也会用平台默认编码。解决POST 请求在 Servlet 第一行加request.setCharacterEncoding(UTF-8)GET 请求需要在 Tomcat 的server.xml里给 Connector 加URIEncodingUTF-8。JSP 页面头部必须写% page contentTypetext/html;charsetUTF-8 languagejava %。数据库连接 URL 加characterEncodingutf8建表时用utf8mb4字符集。5.2 404 错误路径拼写、web.xml 版本和 Artifact 配置的三重检查现象浏览器访问某个 Servlet 或 JSP 时返回 404。原因路径拼写错误、web.xml版本声明和 Tomcat 不匹配、IDEA 的 Artifact 没包含新文件。解决先检查 URL 是否带了正确的上下文路径/cake-shop再检查WebServlet注解的路径和访问路径是否一致。如果web.xml头部版本写的是 4.0 但 Tomcat 是 8.5改成 3.1。IDEA 里 File → Project Structure → Artifacts确认war exploded里包含了webapp目录下的所有文件。改完 Artifact 后需要重新部署。5.3 数据库连接失败驱动类名、时区和 SSL 参数的常见错误现象Tomcat 启动时报Communications link failure或Unknown database。原因MySQL 8.0 的驱动类名是com.mysql.cj.jdbc.Driver不是com.mysql.jdbc.Driver连接 URL 没加serverTimezone会报时区错误MySQL 8.0 默认要求 SSL 连接不加useSSLfalse会报 SSL 警告。解决URL 写成jdbc:mysql://localhost:3306/cake_shop?useSSLfalseserverTimezoneAsia/ShanghaicharacterEncodingutf8。确认 MySQL 服务已启动数据库cake_shop已创建用户名密码正确。如果用的是 MySQL 5.7驱动类名用com.mysql.jdbc.DriverURL 可以不加时区参数。5.4 JSTL 标签不解析uri 写错和 jar 包冲突现象JSP 页面直接显示c:forEach标签源码或者报The absolute uri cannot be resolved。原因taglib的 uri 写错或者 JSTL 的 jar 包没引入或者和 Tomcat 自带的 jar 包冲突。解决JSTL 1.2 的 uri 是http://java.sun.com/jsp/jstl/core不是http://java.sun.com/jstl/core。pom.xml里加javax.servlet:jstl:1.2依赖。如果还不行检查WEB-INF/lib下有没有jstl-1.2.jar和standard-1.1.2.jar有的话删掉让 Maven 统一管理。5.5 事务不回滚Connection 不是同一个自动提交没关现象下单时库存扣了但订单没生成或者订单生成了但库存没扣。原因DAO 方法内部各自从DBUtil.getConnection()获取了新连接不在同一个事务里或者忘了conn.setAutoCommit(false)。解决把Connection作为参数传给所有 DAO 方法确保整个事务共用一个连接。setAutoCommit(false)必须在第一条 SQL 执行前调用。commit()之后要setAutoCommit(true)恢复默认状态否则连接归还连接池后会影响后续使用。6. 打包部署与进阶技巧war 包生成、Tomcat 部署和几个提效习惯项目开发完后需要打包成 war 文件部署到 Tomcat。Maven 项目在pom.xml里加packagingwar/packaging然后执行mvn clean packagetarget目录下会生成cake-shop.war。把 war 文件复制到 Tomcat 的webapps目录下启动 Tomcat 会自动解压部署。访问地址是http://localhost:8080/cake-shop/。如果不想每次手动复制可以在 IDEA 的 Maven 面板里双击package然后在 Run/Debug Configurations 的 Tomcat 配置里Deployment 标签页选择war而不是war exploded。这样每次运行都会自动打包部署。几个提效习惯值得养成。第一把常用的数据库操作封装成BaseDao泛型类增删改查的通用逻辑写一次子类只需要提供 SQL 和参数映射。第二JSP 页面里的公共部分头部导航、底部版权抽成header.jsp和footer.jsp用% include fileheader.jsp %引入。第三Servlet 里获取参数的代码抽成工具方法比如getIntParam(request, page, 1)避免到处写Integer.parseInt和空值判断。最后一个血泪教训数据库密码不要硬编码在DBUtil里。我一般会写一个db.properties放在src/main/resources下用Properties类加载。这样换环境时只改配置文件不用重新编译 Java 代码。虽然毕设项目没人会攻击你但这个习惯从开始就养成以后做真实项目会省很多事。希望帮到你。本文还有配套的精品资源点击获取
上一篇/下一篇内容由系统自动关联
返回资讯列表 →