ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

网上购物书城JavaWeb大作业:从数据库SQL设计到部署避坑全攻略

网上购物书城JavaWeb大作业:从数据库SQL设计到部署避坑全攻略 简介面向计算机相关专业学生的JavaWeb数据库课程设计/期末大作业完整方案以网上购物书城为项目场景包含商品展示、购物车、订单管理等典型电商模块附有可运行源代码、文档说明及数据库SQL脚本。代码经测试可正常运作答辩平均分达96分适合课程设计、毕业设计或初期项目演示也便于在此基础上扩展功能。压缩包共140个文件核心为44个JSP页面、12个Java源文件及29个class编译类搭配14个JS、12个HTML、8个CSS前端资源以及XML配置、jar库和支持文件总体2.28MB目录结构清晰便于对照学习前后端交互与业务逻辑。已有391人学习下载若运行中遇到问题可私聊获取远程教学支持适合在校学生、老师和初阶开发者参考练手及二次开发。1. 网上购物书城JavaWeb大作业先弄清它到底值不值得抄很多同学的期末大作业标题都和这个一样网上购物书城JavaWeb带源代码和数据库SQL。它之所以被反复用作课程设计是因为一套“用户—图书—购物车—订单”的业务闭环刚好能覆盖Servlet、Session、数据库增删改查、JDBC这些核心考点。你把它跑通等于把JavaWeb的请求流程和关系型数据库的表设计全部过了一遍。但这个过程里最容易翻车的地方不是代码而是环境配置和SQL脚本文件摆在那导入报错、端口冲突、中文乱码任何一步都能卡住你。这篇文章就按我从拿到源码到最终答辩的路径把这个书城项目从数据库SQL设计到IDEA部署实现拆开讲清楚。适合马上要交期末大作业、又不想只靠复制粘贴糊弄过去的人。2. 从需求到表结构书城项目的数据库SQL设计如何一次成型在导入任何源代码之前先理解书城项目的数据库模型。很多同学拿到一个 .sql 文件就直接双击导入成功了就开始跑一旦答辩老师问“为什么订单要有明细表”就答不上来。数据库SQL设计是整个项目里最值得花时间的地方它不是让你背建表语句而是让你说清楚每张表存在的理由。下面的内容我不讲抽象概念直接照着书城的表结构拆开讲。2.1 功能清单与角色划分先定页面再定表一个最小可用的网上购物书城至少要包含普通用户和后台管理员两种角色。普通用户的动作是注册、登录、浏览图书、加入购物车、生成订单管理员要做的则是维护图书、处理订单状态。把它翻译成数据模型就是用户表、图书表、购物车表、订单表、订单明细表。不要一上来就写SQL先把页面画出来。页面展示什么信息表里就设计什么字段。比如商品列表页要显示书名、作者、价格、库存、销量图书表就应该有这些列订单页要展示总价和创建时间订单表就应该有这两个字段。这就是“先定页面再定表”的落地方式能避免你后期反复用 ALTER TABLE 改结构。另外需要想清楚一个边界问题购物车要不要落库。常见做法是放在 Session 里刷新页面还在但换浏览器就没了。为了体现数据库课程设计要求我会把购物车写成 tb_cart_item 表这样能展示一次完整的数据库增删改查闭环加入购物车是增修改数量是改结算后删除是删列表查询是查。缺点是每次加购都要写一次数据库对课程设计来说完全够用。角色这一层不需要做太重的权限控制。用户表里加一个 is_admin 字段管理员登录后根据这个字段显示隐藏按钮即可。不要把权限模块做得太大否则会给期末大作业增加无意义的复杂度。2.2 核心表设计用户、图书、购物车、订单SQL脚本下面这组建表SQL就是书城项目的核心适合 MySQL 5.7/8.0。可以直接复制到 Navicat、DBX、命令行或者 IDEA Database 工具里执行。注意我刻意把表名统一成 tb_ 前缀避免 order 这类关键字在写SQL时产生歧义。CREATE DATABASE IF NOT EXISTS bookstore DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE bookstore; CREATE TABLE tb_user ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(32) NOT NULL UNIQUE COMMENT 登录名, password VARCHAR(64) NOT NULL COMMENT 保存加密后的密码, nickname VARCHAR(50) DEFAULT , is_admin TINYINT NOT NULL DEFAULT 0 COMMENT 1管理员 0普通用户, create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE tb_book ( id INT PRIMARY KEY AUTO_INCREMENT, title VARCHAR(100) NOT NULL, author VARCHAR(50) NOT NULL, price DECIMAL(10,2) NOT NULL, stock INT NOT NULL DEFAULT 0, sales INT NOT NULL DEFAULT 0, image_url VARCHAR(255) DEFAULT , category VARCHAR(50) DEFAULT , description TEXT, create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE tb_cart_item ( id INT PRIMARY KEY AUTO_INCREMENT, user_id INT NOT NULL, book_id INT NOT NULL, quantity INT NOT NULL DEFAULT 1, UNIQUE KEY uk_user_book(user_id, book_id), CONSTRAINT fk_cart_user FOREIGN KEY (user_id) REFERENCES tb_user(id) ON DELETE CASCADE, CONSTRAINT fk_cart_book FOREIGN KEY (book_id) REFERENCES tb_book(id) ON DELETE CASCADE ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE tb_order ( id INT PRIMARY KEY AUTO_INCREMENT, order_no VARCHAR(32) NOT NULL UNIQUE, user_id INT NOT NULL, total_price DECIMAL(10,2) NOT NULL, status TINYINT NOT NULL DEFAULT 0 COMMENT 0待付款 1已付款 2已发货 3已完成 4已取消, create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, CONSTRAINT fk_order_user FOREIGN KEY (user_id) REFERENCES tb_user(id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE tb_order_item ( id INT PRIMARY KEY AUTO_INCREMENT, order_id INT NOT NULL, book_id INT NOT NULL, book_title VARCHAR(100) NOT NULL COMMENT 冗余标题防止图书被删后订单显示空, price DECIMAL(10,2) NOT NULL COMMENT 下单时的单价, quantity INT NOT NULL, CONSTRAINT fk_item_order FOREIGN KEY (order_id) REFERENCES tb_order(id) ON DELETE CASCADE ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;这段SQL包含了五张表tb_user、tb_book、tb_cart_item、tb_order、tb_order_item。可能让人疑惑的是 tb_cart_item它实际上是个关系表只存“某个用户把某本书加了几本”。主键自增用 (user_id, book_id) 做联合唯一键意思是同一个用户不能对同一本书重复插入两条记录重复加入时走 UPDATE 而不是 INSERT这个逻辑在第4章购物车代码里会继续用到。tb_order_item 里我冗余了 book_title 字段这是刻意的。订单历史要记录“下单时候的名称和价格”如果图书后来改了名或被删除订单明细不能跟着变。很多初学者在这个字段上翻车写完发现删掉图书后订单详情页的书名变成 null原因就是没有冗余。价格类型用 DECIMAL(10,2)不用 float因为浮点数在金额计算时会出现 0.10.2 不等于 0.3 的问题答辩时被问“为什么金额用DECIMAL”这个回答非常加分。外键的取舍也要注意。tb_cart_item 和 tb_order_item 我加了 ON DELETE CASCADE因为购物车条目属于用户临时数据用户注销后可清理订单明细则不能因为订单删除就永远消失所以在 order_item 上没有级联删除只有外键约束。这就是文档说明里经常强调的“按业务决定外键策略”。在导入这段SQL之前最好先执行一次 DROP DATABASE IF EXISTS bookstore避免上次运行的残留老表造成字段冲突。2.3 外键与索引数据库sql的边界与取舍你可能会问既然有外键为什么有些代码里不建外键因为真实项目并发写入时外键会让 InnoDB 对父表加额外的锁性能压力大所以很多生产库只有主键和索引外键约束由业务代码保证。课程设计属于低并发场景保留外键反而方便答辩展示“参照完整性”。索引不要乱加集中在查询条件里。比如 tb_order 的 user_id、tb_order_item 的 order_id、tb_book 的 category这几个字段会频繁出现在 WHERE 中给它们建普通索引就够。如果你在一张只有几十条数据的表上建五个索引老师反而会问你怎么分析慢SQL优化那就自己挖坑了。要说清楚索引解决的是什么问题它让数据库SQL不再全表扫描而是走索引树定位数据。如果要中途扩展字段比如给书加一个出版社 publisherALTER TABLE tb_book ADD COLUMN publisher VARCHAR(50) NOT NULL DEFAULT COMMENT 出版社 AFTER category;这条语句的意思是在 category 字段后面新增 publisher 列默认空字符串避免已有行的 NULL 问题。执行前先确认没有触发器或视图依赖 tb_book否则 ALTER 会被拒绝。如果写错了想删除一列就执行 ALTER TABLE tb_book DROP COLUMN publisher。还有一个很基础但高频的坑SQL脚本里如果写了 SET FOREIGN_KEY_CHECKS0;记得后面要 SET FOREIGN_KEY_CHECKS1;否则在数据库工具里过一段时间会发现删除某张父表时毫不报错而程序却莫名其妙找不到记录。这种问题通常被误认为是数据丢失其实是外键检查被全局关掉了。数据库SQL的边界就是你关闭了什么就要在同一个会话里把它恢复。主键用自增 INT 而不是 UUID也是刻意选择。自增主键写入有顺序性InnoDB 聚簇索引不会频繁分裂联表查询也方便UUID 虽然全局唯一但随机无序会让索引页分裂在小项目里是纯负担。如果答辩老师问“为什么不用 UUID”你回答“当前项目单库部署、访问量低、自增主键足够且能减少索引碎片”这就到位了。这个数据库模型立住后下一步就是把这份SQL倒入本地库让 IDEA 里的源代码真正跑起来。3. 用IDEA把JavaWeb源代码跑起来配置、部署、运行一条龙先提醒一个容易被忽略的兼容性事实网上购物书城这种老项目的源代码大多基于 Java EE 8 规范也就是 javax.servlet 包名。如果你用的是 Tomcat 10 或更高版本包名已经变成 jakarta.servlet代码会直接编译不通过。所以配置环境前先看代码里 import 的是 javax 还是 jakarta。这个检查能帮你省下至少半小时的排查时间。3.1 导入源代码前的环境检查JDK、Tomcat、IDEA版本我一般建议环境组合是JDK 8、Tomcat 8.5 或 9.0、IDEA 2020 以上。不要一上来就用最新版因为课程设计源码的保质期普遍偏老。先打开项目的 lib 目录或 pom.xml确认有没有 servlet-api.jar 和 mysql-connector-java.jar如果是 Maven 项目就检查 pom.xml 里的依赖版本确认 mysql-connector-java 是 5.1.x 还是 8.0.x。在命令行里查两个关键版本java -version mvn -v如果你用的是 IDEA 自带 Tomcat 集成而不是独立安装 Tomcat也要注意IDEA Community 社区版不带应用服务器集成需要 Ultimate 版本。如果只有 Community可以改用 Maven 的 tomcat7-maven-plugin 启动但课程设计一般学校会提供 Ultimate这条最好先确认清楚。还要做一次 jar 包冲突体检。很多源码里 lib 目录已经放了一份 servlet-api.jar然后 Tomcat 的 lib 里又有一份两个版本不一致启动时会出现 NoSuchMethodError 或 ClassCastException。解决方法是把 lib 里的 servlet-api.jar 删掉让容器统一提供。这个现象很隐蔽报错信息又长又抽象经常被当成黑匣子问题处理其实根源就是重复依赖。3.2 配置数据源数据库sql与JDBC连接串运行一个JavaWeb项目绕不开数据库连接。常见做法是把连接配置放在 src 目录下的 db.properties 文件里而不是在代码里硬编码。这样换数据库、改密码都很方便也不会因为某次提交把生产密码带出去。jdbc.drivercom.mysql.cj.jdbc.Driver jdbc.urljdbc:mysql://localhost:3306/bookstore?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/ShanghaiallowPublicKeyRetrievaltrue jdbc.usernameroot jdbc.password123456这套连接串的参数各有用途。characterEncodingutf8 告诉 MySQL 客户端以 UTF-8 编码传输数据配合建库时的 utf8mb4 字符集能解决中文乱码。serverTimezoneAsia/Shanghai 是 MySQL 8.0 驱动强制的参数不加会报时区错误useSSLfalse 用于本地开发避免证书校验allowPublicKeyRetrievaltrue 在 MySQL 8.0 使用 caching_sha2_password 认证时经常需要。如果你的驱动是 5.1.xdriver 要改成 com.mysql.jdbc.Driver并且不需要 serverTimezone 参数。接下来写一个数据库连接工具类所有 DAO 都通过它获取连接。注意静态代码块只在类加载时执行一次加载失败会抛 ExceptionInInitializerError这个异常能让你在项目启动时就发现问题而不是等到页面访问时才报500。public class DBUtil { private static String url, user, pwd; static { try (InputStream in DBUtil.class.getClassLoader().getResourceAsStream(db.properties)) { Properties props new Properties(); props.load(in); Class.forName(props.getProperty(jdbc.driver)); url props.getProperty(jdbc.url); user props.getProperty(jdbc.username); pwd props.getProperty(jdbc.password); } catch (Exception e) { throw new ExceptionInInitializerError(e); } } public static Connection getConnection() throws SQLException { return DriverManager.getConnection(url, user, pwd); } }这段代码的逻辑是从 classpath 读取 db.properties用 Class.forName 注册驱动之后每次调用 getConnection 都从 DriverManager 获取新连接。Java 6 支持 JDBC 4.0 自动注册Class.forName 这行可以省略但保留它兼容老代码。课程设计场景里每次新建连接够用不要引入连接池除非你想给自己增加配置复杂度。3.3 部署到Tomcat并运行最小步骤环境就绪后实际操作路径尽量简洁。先建库导入SQL再配置 db.properties最后配置 Tomcat 运行。步骤是在命令行导入数据库SQL脚本mysql -uroot -p bookstore.sql打开IDEA导入这个JavaWeb项目等待 Maven 或 lib 索引完成。修改 db.properties 中的用户名密码确保能连上自己的本地库。打开 Run/Debug Configurations新增 Tomcat Server - Local在 Deployment 页签添加 Artifact类型选 war explodedApplication context 改成 /。启动 Tomcat访问 http://localhost:8080/看到书城首页即成功。这里最常犯的错是第4步Application context 忘了改成 /导致访问地址变成 http://localhost:8080/bookstore_war_exploded/而代码里所有跳转链接都是根路径开头的页面能打开但登录跳转和静态资源全断。所以我会把 context 固定为 /并用一条 curl 命令确认首页状态码curl -I http://localhost:8080/返回 200 说明部署成功返回 404 就去检查 Deployment 里的 artifact 是否已经构建点 Build - Rebuild Project 后再启动。如果返回 500则需要查看 Tomcat 日志定位到具体异常原因。数据库SQL导入失败、驱动类没有打进 lib、编码不对都会在这个时候暴露。启动成功后先别急着点页面打开日志里的 localhost.log 和 catalina.out 看有没有 WARNING很多隐藏问题都会在日志里露出苗头别把它当黑匣子。4. 书城核心业务代码实现登录、购物车、下单的完整闭环数据库表设计好、项目能跑起来后最后一步是理解源代码里最关键的几个业务点。网上购物书城的代码文件很多想用一个下午全部读完很容易失去耐心我建议只抓三个重点登录、购物车、下单事务。这三个点覆盖了 Session 管理、数据库增删改查和事务控制也正好是答辩老师最常追问的区域。4.1 用户登录与Session管理登录接口是第一个必看的代码。在课程设计源码里最危险的处理方式是把用户输入直接拼进 SQL 字符串这样用户名里出现引号就会让语句结构被拆开形成注入风险。正确写法是使用 PreparedStatement 预编译先占位再传参。下面这段登录 Servlet 是网上购物书城项目的常见形态。protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String username req.getParameter(username); String password req.getParameter(password); String sql SELECT * FROM tb_user WHERE username? AND password?; try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setString(1, username); ps.setString(2, md5(password)); try (ResultSet rs ps.executeQuery()) { if (rs.next()) { req.getSession().setAttribute(currentUser, username); resp.sendRedirect(req.getContextPath() /book/list); } else { req.setAttribute(loginError, 用户名或密码错误); req.getRequestDispatcher(/login.jsp).forward(req, resp); } } } catch (Exception e) { resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); } }这里有个细节登录失败没有重定向而是 forward 回 login.jsp这样原来输入的用户名能回显在输入框里用户不需要全部重打。登录成功用 sendRedirect避免刷新页面时重复提交表单。Session 里存了 currentUser后续页面从 Session 取这个值就能判断用户是否登录。密码处写的是 md5(password)实际开发中请用带盐的 SHA-256 或 BCrypt课程设计里能用 MD5 示意但不要在文档里标榜安全。登录查询一定不要用 Statement 字符串拼接。一旦用户名或密码里出现英文单引号SQL 语句就会在边界处断开轻则报错重则被绕过后台。多写一个 PreparedStatement 并不复杂却能让你从“拼SQL踩坑”变成“预编译防御”这个习惯在工作后非常值钱。4.2 购物车的数据结构设计购物车实现可以看见数据库增删改查中的“增”和“改”。加入购物车的业务判断很简单这个用户加过这本书没有没有就插入一条加过就把数量加一。如果没建 tb_cart_item 表直接在 Session 里放一个 Map 也够用但既然我们建了表就用下面的逻辑public boolean addCart(int userId, int bookId, int quantity) throws SQLException { String selectOne SELECT id FROM tb_cart_item WHERE user_id? AND book_id?; String updateQty UPDATE tb_cart_item SET quantityquantity? WHERE user_id? AND book_id?; String insertOne INSERT INTO tb_cart_item(user_id, book_id, quantity) VALUES (?,?,?); try (Connection conn DBUtil.getConnection()) { try (PreparedStatement psSel conn.prepareStatement(selectOne)) { psSel.setInt(1, userId); psSel.setInt(2, bookId); try (ResultSet rs psSel.executeQuery()) { if (rs.next()) { try (PreparedStatement psUpd conn.prepareStatement(updateQty)) { psUpd.setInt(1, quantity); psUpd.setInt(2, userId); psUpd.setInt(3, bookId); return psUpd.executeUpdate() 1; } } else { try (PreparedStatement psIns conn.prepareStatement(insertOne)) { psIns.setInt(1, userId); psIns.setInt(2, bookId); psIns.setInt(3, quantity); return psIns.executeUpdate() 1; } } } } } }注意这里的关键逻辑是先查询是否已存在存在就 UPDATE不存在就 INSERT。但这段代码有一个并发隐患两个请求同时查到“不存在”再同时执行 INSERT虽然联合唯一键能拦住重复但会抛异常给用户。更稳妥的做法是直接用一条 MySQL 原生 SQL 完成INSERT INTO tb_cart_item(user_id, book_id, quantity) VALUES (?,?,?) ON DUPLICATE KEY UPDATE quantity quantity VALUES(quantity);这是数据库SQL层面上的“后悔药”如果唯一键存在就转成更新不存在就是插入。它没有上一种写法的问题代码还更短。课程设计里能主动写出这条 SQL答辩印象会好很多。购物车列表页也用一个 JOIN 查询把 tb_cart_item 和 tb_book 连起来展示书名、价格、数量和小计。4.3 订单生成与库存扣减事务是关键下单是书城项目里最该较真的部分。整个过程至少涉及三件事扣库存、生成订单、写入订单明细。如果每步各自提交库存扣了但订单没创建成功用户付了钱却看不到记录就是典型的业务数据不一致。要避免这个问题必须把多步 SQL 包进同一个事务。常见做法是拿一个 Connection手动关闭自动提交所有写操作都用同一连接执行最后统一 commit任一步抛异常就 rollback。下面给一段简化版代码结构try (Connection conn DBUtil.getConnection()) { conn.setAutoCommit(false); try { String deduct UPDATE tb_book SET stockstock-?, salessales? WHERE id? AND stock?; PreparedStatement psDeduct conn.prepareStatement(deduct); psDeduct.setInt(1, qty); psDeduct.setInt(2, qty); psDeduct.setInt(3, bookId); psDeduct.setInt(4, qty); if (psDeduct.executeUpdate() ! 1) { conn.rollback(); throw new RuntimeException(库存不足); } String orderSql INSERT INTO tb_order(order_no,user_id,total_price,status) VALUES(?,?,?,0); PreparedStatement psOrder conn.prepareStatement(orderSql, Statement.RETURN_GENERATED_KEYS); // 生成order_no、设置参数、executeUpdate、取回自增id String itemSql INSERT INTO tb_order_item(order_id,book_id,book_title,price,quantity) VALUES(?,?,?,?,?); // 循环订单明细写入单价和数量 String clearCart DELETE FROM tb_cart_item WHERE user_id?; // 清理购物车 conn.commit(); } catch (Exception e) { conn.rollback(); throw e; } finally { conn.setAutoCommit(true); } }上面省略了参数设置但核心逻辑是扣库存的 UPDATE 带上了 stock? 这个条件。如果库存不足受影响行数是 0回滚并直接提示而不是先查出库存再判断。先查再扣在多线程下会超卖“先改行再校验行数”是这里最值得讲的一招。还有一个隐蔽点必须使用同一个 Connection。若扣库存用 conn1、插订单用 conn2事务就失效了整段代码看起来在 try-catch实际根本不在一个事务里。检查方法是在 DAO 之间传 Connection而不是让每个 DAO 自己开连接。课程设计能讲清这一点足够证明你读过源码不是只会 CtrlC。5. JavaWeb项目常见问题避坑从SQL导入到页面404的5个翻车点这部分是血泪经验。第一次跑 JavaWeb 书城项目不要指望一次成功把下面几个高频问题记住能省下一个通宵。每一条我都按“现象 → 原因 → 解决”的顺序写照着做就行。5.1 建表SQL导入后中文乱码现象book 表里插入的中文书名在页面显示成问号数据库里看也是乱码。原因数据库连接串没带 characterEncodingutf8或者建库时用的不是 utf8mb4。另一个容易被忽略的点是 JSP 页面本身没有声明 UTF-8导致响应流的编码和数据库不一致。解决先检查数据库字符集SHOW CREATE DATABASE bookstore; 如果是 latin1就重建数据库如果字符集正常就去 db.properties 的连接串加 characterEncodingutf8。JSP 文件头加上 % page contentTypetext/html;charsetUTF-8 %并在 web.xml 里配置一个编码过滤器。最简单的过滤器代码如下public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { req.setCharacterEncoding(UTF-8); resp.setCharacterEncoding(UTF-8); chain.doFilter(req, resp); }这段代码必须在任何读取请求参数的逻辑之前执行所以要确保它在 web.xml 的 filter-mapping 中处于最前面。配置好后重启 Tomcat再重新插入中文数据乱码会立刻消失。如果还乱打开 .sql 文件确认它是 UTF-8 编码且没有 BOM。5.2 用数据库工具导入SQL报错 near line现象在 Navicat 或 DBX 里执行 .sql 文件报 Err 1064 You have an error in your SQL syntax位置在某个建表语句附近。原因多数情况是 .sql 文件编码不是 UTF-8或者带了 BOM少数情况是这份 SQL 来自另一个数据库方言比如把 SQL Server 的 nvarchar 语法混进来了MySQL 不认识。解决用 VSCode 或 Notepad 打开 .sql右下角确认编码是 UTF-8另存为无 BOM 格式。然后不要用图形工具改用命令行导入mysql -uroot -p bookstore.sql命令行报错信息更精准能看到是第几行出了问题。如果你必须用 Navicat右键目标数据库选择“运行SQL文件”并在导入前把目标库切换到 bookstore而不是在任意库里直接运行。否则建表语句没有 USE bookstore表会建到当前默认库里去启动项目后连不上表报 Table doesnt exist。5.3 Tomcat 配置后访问一直404现象启动成功Tomcat 也起来了但访问 http://localhost:8080/ 返回 404改成 http://localhost:8080/bookstore_war_exploded 又正常。原因IDEA Deployment 里的 Application context 没有改为 /默认把项目名当根路径。解决打开 Run/Debug Configurations选中 Tomcat Server切到 Deployment 页签把 Application context 改成 /再 Rebuild Project。改完还是404就检查 web.xml 里有没有 welcome-file。很多书城代码的入口并不在 index.jsp而是直接转发到 /book/list需要在 web.xml 里加一条welcome-file-list welcome-filebook/list/welcome-file /welcome-file-list如果是通过 Servlet 转发则要看根路径访问时的 url-pattern 是否匹配。这类 404 大多数不是代码 bug而是路径没对。5.4 数据库连接失败Access denied for user现象Tomcat 启动后点登录页面报 500日志里出现 Access denied for user rootlocalhost 或者 Communications link failure。原因db.properties 里的用户名密码和你本地 MySQL 不一致另一个常见原因是 MySQL 8.0 驱动没有加 serverTimezone 参数导致建立连接时直接抛异常。解决先用命令行验证账号能不能进mysql -uroot -p能进就说明代码配置错了改 db.properties 里的 jdbc.username 和 jdbc.password。如果报时区错误按第3章的连接串补全 serverTimezoneAsia/Shanghai 和 allowPublicKeyRetrievaltrue。改完配置文件一定要重启 Tomcat因为连接参数在静态代码块中只加载一次热部署不会重新读取。5.5 登录页被 SQL 拼接写坏现象在用户名输入框里输入带英文单引号的字符串登录页直接 500页面回显堆栈信息。原因源代码用了 Statement 字符串拼接 SQL这是课程设计里最容易出现的老代码问题。拼接后的 SQL 语义被输入内容改变报错像黑匣子一样难定位。解决找到 LoginServlet把所有 Statement 换成 PreparedStatement参数一律用 setString/setInt 传值。不要只在登录里修购物车、订单、后台商品管理里的查询也要排查。举例来说String sql SELECT * FROM tb_user WHERE username username ;这行就是问题源头要改成String sql SELECT * FROM tb_user WHERE username?;看到问号占位就该明白用户的输入不再参与 SQL 结构拼接只是作为参数传给数据库。这是让你以后在面试里少被问倒一回的关键代码习惯也是“数据库SQL安全”这个知识点最直接的落地。临时加一条如果这些排查都做完了还是不通先把 IDEA 里 Tomcat 的 JVM 参数 -Dfile.encodingUTF-8 加上。很多中文乱码和编译问题都源于 IDEA 默认用了系统编码而不是 UTF-8。配置路径Help - Edit Custom VM Options加一行 -Dfile.encodingUTF-8重启 IDEA。这个问题不常被提到但真遇到了会卡一下午。6. 网上购物书城还能怎么改三个可加分的小功能与验证方法如果让我只给一个最具性价比的改进我会做三件事给图书列表加分页给受保护页面加登录过滤器在交付文档里放一段真实SQL验证样例。这三件事都不难但能明显拉开课程设计完成度的差距。分页查询是老师最爱问的功能。常见实现是给列表接口加 page 和 size 两个参数SQL 写成SELECT * FROM tb_book ORDER BY sales DESC LIMIT 0, 10;LIMIT 后面第一个参数是偏移量第二个是每页条数。第一页是从 0 开始所以第 2 页就是 LIMIT 10, 10。配合一个总条数查询 SELECT COUNT(*) FROM tb_book前端就能计算总页数。这一句话就能讲清楚分页原理比背诵 PageHelper 源码有用得多。登录过滤器则保证未登录用户不能直接访问购物车和订单页面。核心代码如下HttpSession session request.getSession(); if (session.getAttribute(currentUser) null) { response.sendRedirect(request.getContextPath() /login.jsp); return; }把它放进 Filter并只对 /orders/、/cart/路径生效。注意 login.jsp 本身必须放行否则会形成死循环。很多同学看到 Filter 就头大其实它就是一段拦在浏览器和 Servlet 之间的代码理解起来并不难。最后说一个我自己的习惯交作业前我会故意破坏业务数据来做验证。比如删掉一张图书后再打开历史订单看详情是否还能显示书名把库存改成 1再下一个数量为 2 的订单看系统会不会提示库存不足。别嫌麻烦答辩老师最喜欢干的事就是点几下页面找 bug。数据库SQL和源代码都能跑不等于业务逻辑经得起追问。这些血泪经验是我从自己做课程设计开始带下来的希望帮到你。本文还有配套的精品资源点击获取
返回列表