ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

JavaWeb网上书店系统部署全攻略:从解压到跑通的避坑指南

JavaWeb网上书店系统部署全攻略:从解压到跑通的避坑指南

简介:JavaWeb网上书店系统是以JSP+Servlet+JavaBean搭建的完整课程设计项目,面向高校计算机专业学生,解决图书展示、购物车结算、订单管理等典型电商业务流程的实现问题。代码已通过导师指导,获得97分高分,适合直接运行或作为课设改造基础。压缩包共645个文件,大小12.47MB。其中212张jpg和26张bmp提供商品图、银行按钮等界面素材,139个js与57个css负责前端交互和样式,41个jsp页面完成动态界面展示,36个java及36个class实现Servlet控制与业务逻辑,17个jar为项目运行依赖库,另有1个sql脚本可一键初始化数据库,各目录按前后端与资源类型划分,便于检索学习。目前已有1183人学习下载。资源内附数据库文件,导入后即可体验完整网上书城流程,可在其基础上扩展搜索、分类、后台管理等模块,对准备JavaWeb课程设计或毕业设计的读者有较高参考价值。

1. 这套 javaweb 网上书店系统,说白了就是让你少写两百个页面

标题这么长一串,拆开看就三样东西:一套基于 JSP + Servlet + MySQL 的网上书店后台、一份能建出整个数据库的 SQL 脚本、和一个打包好的 .zip。市面上这类系统包的目标用户非常明确:JavaWeb 课程设计、毕业设计、以及想看看完整增删改查链路的新手。它不能给你高并发,给不了微服务,它给的是把“浏览器发请求 → Servlet 接住 → Service 处理 → DAO 查库 → JSP 渲染”这整条链路跑成一个能交差的作品。反直觉的一点是:判断这个包值不值得跑,别先翻代码,先打开那份 .sql 脚本看一眼表设计和数据量,数据库能建起来,这系统就活了八成。

2. 解压 zip 之后别急着调配置:认目录、查依赖、定环境

2.1 一套典型 javaweb 工程解压后应该长什么样

下载下来的这个 zip,解压后如果是一个规范打包,你会看到这么几个部分:

  • src:源码根目录,里面按包名分好 entity、dao、service、servlet/controller、util、filter 这类目录。
  • WebRoot 或 WebContent:Web 资源根目录,jsp、css、js、image 都在这里,WEB-INF 下面有 web.xml 和 lib。
  • database 或 sql 目录:网上书城系统的建库脚本,常见的文件名有 bookstore.sql、bookshop.sql、shop.sql。
  • 根目录下的 readme 或 需求文档.doc:说明运行环境、默认账号密码。

我拿到这类包,第一件事不是打开 IDEA,而是先用压缩软件直接看到最上层目录,人工核对一遍:src 里有没有 .java 源码文件。如果只看到 .class 而没有 .java,那这个包多半是从部署目录里扒出来重新压缩的,你看到的源码其实是编译后的字节码,反编译能看个差不多的逻辑,但注释、变量名、部分类型信息会失真。

然后再看 WEB-INF/lib 里有没有 mysql-connector-java 开头的 jar 包,这是数据库驱动,很多翻车案例都是因为这里缺包。常见做法是看这个 jar 的文件名信息判断数据库版本:mysql-connector-java-5.1.47 对应 MySQL 5.x 或兼容模式,而 8.0.x 的驱动类名和连接 URL 写法都有变化。包里驱动缺失的话也不必急着关掉文件夹,后面我会说怎么补救。

2.2 从 zip 到 IDEA 可运行工程:导入方式和两个关键前置

确认目录结构没有大问题之后,我一般用 IDEA 的做法来导入这类非 Maven、非 Gradle 的 war exploded 工程。IDEA 里其实没有特别别扭,步骤就四条:

  1. 菜单 File → New → Module from Existing Sources,选择解压后的根目录。
  2. Import 类型选 Java,不选 Eclipse,除非这个包本身就是 MyEclipse 导出的。
  3. 如果 IDEA 没识别出 Web 方面,右键模块 → Add Framework Support → Web,把 Web 资源目录指定到 WebRoot(或 WebContent)。
  4. 给模块添加一个 Tomcat Local 运行配置,在 Deployment 里选择 Artifact,类型通常选 war exploded,Application context 填/。

我在这里踩过不止一次:如果 Application context 填了/shop这种带名字的路径,而代码里跳转用的是/xxxServlet或./xxx,就会出现“首页能打开,点登录就 404”的诡异状况。更保险的做法是统一设成/,然后通过 Tomcat 的 8080 直接访问,这样 jsp、Servlet 里的绝对路径都按根路径解析。这一布在 IDEA 里对应的界面选项是 Run → Edit Configurations → Deployment,运行环境选 local Tomcat 时,把它默认的上下文路径去掉即可。

还有一个容易忽略的前置:web.xml 里的 servlet 映射。JSP 页面里通常有href="bookServlet?action=queryAll"这种写法,这套系统的所有功能点都靠一个 Servlet 的 action 参数分发。导入后先打开 web.xml 看一眼,确认它注册了哪些 Servlet、映射了哪些 url-pattern。我发现不少解压包里的 web.xml 是 2.5 版本的,对应 Servlet 2.5 / JSP 2.1,这套组合用 Tomcat 8.5 或 9 都能正常跑;如果 web.xml 头是 3.1,那用 Tomcat 8+ 就很稳妥。

2.3 没有构建脚本的工程,靠什么锁定 Java 和 Tomcat 版本

这种 javaweb 源码包几乎不可能带 pom.xml。锁定运行环境的三条线索,按优先级排:

  1. web.xml 的开头xsi:schemaLocation里写的是哪个版本。2.5 用 Tomcat 8.5/9,3.1 用 Tomcat 9,别装 Tomcat 10。Tomcat 10 的问题我在第 4 章细说。
  2. lib 里 servlet-api 和 jsp-api 的 jar 文件名,比如javax.servlet-api-3.1.0.jar,这跟 web.xml 版本互相验证。
  3. src 里编译级别。如果 IDE 报“compiled by a more recent version of the Java Runtime”,再看代码里有没有用到 JDK 8 的 lambda 或时间 API,最常见的解读是这套代码用 JDK 1.8 编译,跑到高版本 JDK 反而难过编译。

如果没有这些线索,还有一条很实用的兜底路径:把解压目录里的 java 文件全部搜索一遍,看 import 里有没有javax.servlet和org.springframework。干干净净的javax.servlet+ JDBC 代码,就是最经典的老三层结构,环境匹配成功率高。我一般不会一上来就开泛化兼容方案,先拿默认 JDK 8 + Tomcat 9 跑,跑通再说改。

3. 数据库是这套系统的命门:导入脚本与连接配置

3.1 解压包里的 .sql 脚本:先看脚本头,再决定建库方式

网上书店系统的数据库脚本,常见做法有两种写法:第一种是开头就有 CREATE DATABASE 和 USE 语句,你只要给它一个能连接 MySQL 的账号,直接执行就行;第二种是只包含 CREATE TABLE 和 INSERT,库默认不建,需要先手动建库再指定。判断方法是打开 .sql 文件看前十几行,有 USE 的就直接跑,没有的给一个建库语句。MySQL 5.7 到 8.0 的不同版本装法差异很大,如果你的本机是 8.0,而脚本里表结构用到了ENGINE=MyISAM或者DEFAULT CHARSET=utf8,8.0 对这两者都还能兼容,跑完记得把表结构打开看一眼。

我实际执行时用的命令是这样,习惯用命令行而不是可视化工具,因为能直接看到错误在哪一行:

# 进入 mysql 命令行 mysql -u root -p # 如果脚本里没有 CREATE DATABASE,先手动建库 CREATE DATABASE bookstore DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci; # 指定库,并把脚本直接塞进去执行 USE bookstore; SOURCE /path/to/bookstore.sql;

这两个命令拆分说明一下:CREATE DATABASE 这里显式指定字符集 utf8,避免脚本里没写字符集时,建出来的表继承 server 默认的 latin1,之后页面显示中文全是问号。SOURCE 是 mysql 自己的命令,后面写本机脚本的绝对路径,注意路径不能带中文和空格,否则这条命令会直接报错或执行到一半。执行完按SHOW TABLES;检查一下表数量,比如包含用户表、图书表、订单表、订单明细表这几个基础表,这套系统的增删改查就有着落了。

有的包还带测试数据,INSERT 语句里会插入几十本图书记录。这个很有用,开发完看一眼页面有图书渲染,心里踏实得多。如果脚本执行到一半报Unknown column或者Duplicate entry,大概率是脚本本身和 MySQL 版本冲突,常见原因是云环境里导出的脚本带着SET SQL_MODE、DELIMITER这种写法,手工把这行注释掉再执行,或者用 navicat 的“运行 SQL 文件”方式跑,它容错更强一点。

3.2 改了 db.properties 还是连不上?逐项排查这五个值

连接配置是这个系统仅次于数据库脚本的第二命门。这类项目里常见的配置文件名叫 db.properties、dbconfig.properties 或者 jdbc.properties,位置通常在 src 的 util 包下。网上书城系统的连接池在这个包里有多种实现,常见的是 DBCP、C3P0、或者最朴素的 DriverManager 单例,我先按最常用的 properties 格式整理一套默认配置:

jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/bookstore?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai jdbc.username=root jdbc.password=123456

逐行说:driver 这一行,如果 lib 里放的是 mysql-connector-java-5.1.x,那么com.mysql.jdbc.Driver是对的;如果换成 8.0.x 驱动,驱动类名要改成com.mysql.cj.jdbc.Driver。url 里四个参数里,useUnicode 和 characterEncoding 是保证 Java 往数据库写中文不乱码的基础配置;useSSL=false 是为了避免 MySQL 8.0 默认开启 SSL 握手导致的数据库大量警告;serverTimezone 这一项更关键——MySQL 8.0 驱动如果不在 url 里指定时区,启动时大概率会直接抛异常。username 和 password 注意不要带任何空格,写在 properties 文件里末尾也不能有看不见的空格,我吃过这个暗亏。

如果你看到配置文件里根本不是 properties,而是一段 XML,那多半是 C3P0 的写法。C3P0 在 MySQL 8 下有个容易翻车的地方:property 里driverClass同样要换成com.mysql.cj.jdbc.Driver,JdbcUrl 里同样要带上时区参数。判断当前项目用哪种连接池,看 lib 里有c3p0-x.x.x.jar、commons-dbcp-x.x.x.jar还是都没有,没有第三方 jar 的,多半是原生的DriverManager.getConnection。我一般改完配置后先写一个小 main 方法直接调用 DBUtil 的 getConnection,能拿到 Connection 再启动 Tomcat,这一步能排除掉大半莫名其妙连不上库的问题。

3.3 网上书店的增删改查链路:从 DAO 到 JSP 的一条龙套路

书店系统的业务表,典型设计是这样:t_user 用户表、t_book 图书表、t_order 订单表、t_order_item 订单明细表。插句题外话,我遇到的很多课设 demo 里订单表和图书表之间没有外键约束,订单明细表里就一个 bookId 字段。这种表设计跟生产系统相比很糙,但跑流程没问题,也容易理解。写网上书店增删改查,最容易让新手断链子的是这三个地方:图书的模糊查询、订单的状态流转、以及页面上的中文参数在 GET 请求里被浏览器和 Tomcat 用不同字符集解码的问题。

先看 DAO 层里最常见的一段查询代码:

public List<Book> queryBook(String keyword) { Connection conn = null; PreparedStatement ps = null; ResultSet rs = null; List<Book> list = new ArrayList<>(); try { conn = DBUtil.getConnection(); String sql = "SELECT book_id, book_name, author, price FROM t_book WHERE book_name LIKE ? OR author LIKE ?"; ps = conn.prepareStatement(sql); String likePattern = "%" + keyword + "%"; ps.setString(1, likePattern); ps.setString(2, likePattern); rs = ps.executeQuery(); while (rs.next()) { Book b = new Book(); b.setBookId(rs.getInt("book_id")); b.setBookName(rs.getString("book_name")); b.setAuthor(rs.getString("author")); b.setPrice(rs.getDouble("price")); list.add(b); } return list; } catch (SQLException e) { throw new RuntimeException("查询图书失败", e); } finally { DBUtil.close(conn, ps, rs); } }

这段代码的意图很简单:拼接%keyword%然后用占位符传参,而不是字符串硬拼 SQL。很多人喜欢写"SELECT ... WHERE book_name LIKE '%" + keyword + "%'",keyword 一旦带上单引号或者中英文符号,SQL 就组装得不成样子,这就是网上书店系统里查询功能最经典的坑。PreparedStatement 的 setString 会帮你处理转义,性能差距虽说不明显,但至少不会注入。DBUtil.getConnection() 就是连接池或 DriverManager 的封装,finally 里关连接,做了一个标准的 JDBC 流程。

链路走到 Servlet 层,常见的写法是让前端表单 action 指向一个 BookServlet,BookServlet 里通过 getParameter 拿出 action,再用 if/else 或 switch 分发。网上书店系统里的常见 action 有 queryAll、queryByKeyword、toAdd、add、toEdit、edit、delete,基本就是一套增删改查。比较需要注意的是,查询结果放到 request 域后 forward 到 jsp,在 jsp 里用 JSTL 或传统脚本片段循环渲染;这套系统如果只有 scriptlet 而没有 JSTL,渲染时风格就很复古。这些都无伤大雅,能跑通本身就是价值。

数据链路没问题之后,剩下的问题基本都围绕编码:数据库里存进去的是 utf8,Tomcat 默认解码 GET 请求的 URI 用的还是 ISO-8859-1。这就是为什么你看到网上书店系统页面里搜索中文图书名返回空、或者页面乱码,但是直接看着数据库里的记录完好无损。解决办法我在第 4 章直接给出来。

4. 避坑集中营:从 zip 变运行项目的五条血泪排查记录

4.1 MySQL 8 时区异常:报了乱码时间区,全局蒙圈

现象:启动 Tomcat 后,点任意一个涉及数据库的按钮,控制台抛java.sql.SQLException: The server time zone value '�й���ʱ��' is unrecognized,页面 500。

原因:MySQL 8.0 的驱动在建立连接时会去读数据库时区,读出来的是中文编码的上海时区,而驱动用的字符集把它解码成了乱码,识别失败。MySQL 5.7 及以下常年用着jdbc:mysql://localhost:3306/bookstore这种极简 url 不报错,到了 8.x 就翻车。

解决:在 jdbc.url 后面补上serverTimezone=Asia/Shanghai,并顺带加useSSL=false。改了 properties 后不要只重启 Tomcat,要重新编译让配置文件进到 classes 目录。我用 IDEA 时经常因为改了 src 下的 properties 但没触发 build,页面一直报同一个错,那真是典型的黑匣子时间。确认配置生效的验证方法是:在 DBUtil 里写一个 main 方法直接测试连接,成功后再碰 Tomcat。

4.2 页面全是方块字,中文乱码的五处控制点

现象:图书名称、作者、订单备注全是带问号的方块字,或者向数据库里写中文后读出来变成了问号。

原因:系统从浏览器到数据库这条链路上,字符集在三个地方分别被设置成了不同值。联调后发现数据在浏览器是 utf8、进入 Java 程序是乱码、写库又是乱码,问号常出现在最末端。另一个问题是 jsp 页面本身的 pageEncoding 和 contentType 没配统一。

解决:按以下清单逐一核对:

  • jsp 页头改成<%@ page contentType="text/html; charset=utf-8" pageEncoding="utf-8" %>
  • jdbc.url 里加useUnicode=true&characterEncoding=utf8
  • Tomcat 的 conf/server.xml 里给 8080 的 Connector 加URIEncoding="UTF-8"
  • 建表时统一用 utf8
  • 在 web.xml 里注册一个 CharacterEncodingFilter,对 request 和 response 都设置 utf8

乱码这问题,最省心的做法是在 web.xml 直接开一个过滤器和 jsp 头同步:全链路都往 utf8 指,不要混合 latin1 和 gbk 测试,不然你永远说不清是哪一段弄乱的。

4.3 都能打开首页了,点登录却 404

现象:进入书店首页正常,点“登录”或“注册”链接后,地址栏变成/bookstore/loginServlet,页面却 404。数据库和连接池已经确认没问题,Servlet 编译也没报错。

原因:这事儿的元凶,几乎都是因为 IDEA 部署的 Application context 默认为带上下文名称的 web 路径,而代码里的跳转用的是绝对路径/loginServlet。这属于老式 javaweb 项目和 IDEA 现代部署方式之间的经典错位。更隐蔽的变体是:在 jsp 里用了../BookServlet这种相对路径,从不同层级的 jsp 跳转时,指向的目录不同导致 404。

解决:打开 Run → Edit Configurations → Deployment,将 Application context 改成/,确保 Artifact 映射到根路径。代码里已经包含跳转链接的,尽量统一用${pageContext.request.contextPath}/BookServlet来拼路径。改动后记得重新部署,必要时把 Tomcat 的工作目录清理掉。

4.4 Tomcat 10 运行 Servlet:直接 NoClassDefFoundError

现象:解压这个 zip 后用 IDEA 默认的 Tomcat 版本(比如 Tomcat 10.x)跑,控制台报NoClassDefFoundError: javax/servlet/http/HttpServlet,但是明明 lib 里面有 servlet-api。

原因:Tomcat 10 把 Jakarta EE 的包名从javax.servlet改成了jakarta.servlet。旧式 javaweb 项目代码里 import 的全是 javax 包,跟 Tomcat 10 的 servlet 实现完全不匹配。这个 zip 若是老项目,所有结构都按 Tomcat 8/9 写的,驱动这一类是改不过来的。

解决:给项目配 Tomcat 9 或 8.5,版本对应最稳。如果你因为系统限制只能用 Tomcat 10,那就必须把代码里所有javax.servlet改成jakarta.servlet,但这不是明智的决定,因为 lib 里旧的 jsp-api、servlet-api 也一样要替换。所以最省事的决定是绕开 Tomcat 10,老项目还是配老 Tomcat,是我用过的组合里最省心的一套。

4.5 lib 里有驱动 jar,运行却报 ClassNotFoundException

现象:连接数据库时抛ClassNotFoundException: com.mysql.jdbc.Driver,但打开项目的 WEB-INF/lib 一眼看得到 mysql-connector-java 的 jar 文件。

原因:这是 IDEA 项目依赖和最终部署产物的差别。你看到的 lib 是磁盘上的 jar,但在 IDEA 的模块依赖里可能没有被标记为库;更常见的是 Artifact 打包时没有把 lib 目录放进输出,war exploded 输出目录里没有 WEB-INF/lib。于是编译能过因为 IDE 编译期能找到类,运行期启动失败因为类没有进到部署产物路径。

解决:把该 jar 挂上依赖:右键工程 → Open Module Settings → Libraries,添加 mysql 连接器作为项目库。然后检查 Artifact 的输出结构,确认有 WEB-INF/lib 这样的文件夹。一个保险动作是不给这个包创新打法,直接把 lib 目录下所有 jar 全选加入 Library,然后在 Artifact 里勾选 “Include in project build”。这样重新部署后 ClassNotFoundException 基本消失。

这几条坑我全部用“现象 → 原因 → 解决”的格式给你串了一遍,如果只记一道后悔药,那就是:任意 javaweb 老包,落地之前先确认 Tomcat 版本和 MySQL 驱动版本,这两个版本决定你后续一小时还是一次过。

5. 跑通之后,怎么把网上书店项目做出简历级完成度

跑通不等于能写进简历,否则满大街的课设都成了作品。我会给这套系统加三个验证方向,加完它才算一个经得起问的练手作品。

第一个方向,走通完整的购买闭环:登录 → 图书列表 → 图书详情 → 加入购物车 → 提交订单 → 查看订单列表。很多书店项目只实现了订单表插入,漏掉了购物车和订单明细的联动。把这两个表通过订单 id 和图书 id 串起来,在 MySQL 里用 JOIN 查一次,证明你的数据设计是连贯的。

第二个方向,下单防重放。书店系统最怕点两次提交生成两笔一模一样的订单。老式 javaweb 项目没有现成框架组件,我加一个一次性 token:详情页提交订单时先在 session 里放一个随机 token,表单里带同一个 token,后端校验通过后立即移除。下面这段是我在 Servlet 里做的骨架写法:

// 生成 token 并放入 session String token = UUID.randomUUID().toString(); request.getSession().setAttribute("order_token", token); request.setAttribute("order_token", token); // 提交时校验 HttpSession session = request.getSession(); String sessionToken = (String) session.getAttribute("order_token"); String requestToken = request.getParameter("order_token"); if (sessionToken == null || !sessionToken.equals(requestToken)) { throw new RuntimeException("请勿重复提交订单"); } session.removeAttribute("order_token");

这段跟业务代码无关,但面试问“你怎么防止重复下单”时,这套逻辑能直接拿出来说。核心是保证 token 是服务端生成的、且只能用一次,比对成功后从 session 里删掉,第二次提交拿不到旧 token 就被拦了。

第三个方向,把数据库脚本做成可重复初始化。我建议在项目根目录放一个 init.sql 和 init.bat/sh,脚本里用 DROP TABLE IF EXISTS 开头再接建表,这样你任何时候重置环境都能一键回到干净状态,不用手动删表。这是很多课设包没有做的事,做完了整个系统的可复现性就上一个台阶。

最后一个我自己的血的教训:我最早接触这类系统时,卡在 MySQL 8 时区上整整一晚,后来被逼出一个习惯——拿到任何 javaweb 源码包,第一件事永远是先看 web.xml 和 .sql 脚本头,确认版本和服务端环境,然后再碰 IDE。这个顺序反过来,你会在配置和报错之间狼狈地来回踱步。希望帮到你。

本文还有配套的精品资源,点击获取

返回列表