
简介这是一套基于JSPMySQL的JavaWeb图书销售管理系统网上书店完整项目源码与数据库面向计算机相关专业正在准备期末大作业、课程设计的学生以及需要项目实战练习的JavaWeb学习者。项目经导师指导并认可通过可作为高分课程设计参考帮助读者快速理解网上书店从图书展示、购物车到订单管理的整体业务实现。压缩包共286个文件约6.93MB包含53个java源文件、52个jsp页面、40个js脚本、19个css样式及10个jar依赖另有sql数据库脚本、xml配置与图片资源前端还涉及bootstrap、layui等常用框架结构完整便于二次开发。目前已有233人学习下载。读者可获得可直接运行的完整工程、数据库建表脚本与清晰的目录分层便于对照学习Servlet、JSP与MySQL的整合方式快速完成大作业搭建与调试。1. 从一份能跑通的 JSP 图书销售系统说起它到底解决了谁的燃眉之急如果你正在为 JavaWeb 课程设计或者期末大作业发愁大概率已经翻过不少“图书销售管理系统”的源码包但真正能导入 IDE、连上数据库、点开首页就能看到图书列表的其实没几个。这份基于 JSPMySQL 的网上书店项目核心价值就在于它是一份结构完整、前后台闭环、数据库脚本齐全的 JavaWeb 实战工程。它用最经典的 JSPServletJavaBean 三层架构把图书展示、购物车、订单管理、后台增删改查这些电商基础模块全部串了起来。前端没有用花哨的框架而是 Bootstrap 加 Layui 做响应式布局配合 animate.css 做过渡动效对新手来说改起来不费劲。适合谁一是需要快速搭出课程设计骨架的计算机专业学生二是想拿一个真实项目练手 JavaWeb 连接 MySQL 数据库的初学者。你拿到手的不只是几个 JSP 页面而是一套能让你理解“请求怎么从浏览器走到数据库再走回来”的完整链路。2. 拆开源码包JSP 页面、Servlet 与 MySQL 表结构怎么对上号2.1 目录结构里藏着的分层逻辑把压缩包解压后你会看到典型的 JavaWeb 工程目录。src下面按包名分了dao、service、servlet、entity、util这几层WebContent或者webapp下面放着jsp、css、js、images和WEB-INF。很多人拿到源码直接往 IDE 里一扔就点运行结果 404 或者 500根本原因就是没先看目录和web.xml的映射关系。我一般会先做一件事打开WEB-INF下的web.xml看看 Servlet 的url-pattern是怎么配的再对照 JSP 页面里表单的action地址确认请求路径能不能对上。比如图书列表页bookList.jsp里可能写的是actionBookServlet?methodlist那你就得去BookServlet里找doGet或doPost方法看它怎么根据method参数分发到不同的业务逻辑。这一步走通了后面改代码才不会像无头苍蝇。数据库方面项目一般会附带一个.sql文件里面建了user、book、category、orders、orderitem这几张核心表。book表通常有id、name、author、price、stock、image、category_id这些字段orders表则关联用户和订单状态。你要做的是先在 MySQL 里建一个库比如bookstore然后执行这个 SQL 脚本。注意字符集选utf8mb4不然中文书名或者作者名插进去会变问号。常见做法是用 Navicat 或者命令行source导入命令行更稳不容易因为客户端版本差异出幺蛾子。2.2 数据库连接配置改对这四个参数就能跑项目里连接 MySQL 的代码通常写在src下的util包里类名可能是DBUtil或者BaseDao。它一般用 JDBC 原生方式或者简单的连接池。你要改的就四个地方驱动类、URL、用户名、密码。下面是一段典型的配置代码我把它拆开讲。// DBUtil.java 中获取连接的常见写法 public class DBUtil { // MySQL 8.0 以上驱动类名带 cj5.7 不带写错直接 ClassNotFoundException private static final String DRIVER com.mysql.cj.jdbc.Driver; // useSSLfalse 关掉 SSL 警告serverTimezoneAsia/Shanghai 解决时区差 8 小时问题 private static final String URL jdbc:mysql://localhost:3306/bookstore?useSSLfalseserverTimezoneAsia/ShanghaicharacterEncodingutf8; private static final String USER root; private static final String PASSWORD 你的数据库密码; public static Connection getConnection() throws Exception { Class.forName(DRIVER); return DriverManager.getConnection(URL, USER, PASSWORD); } }逻辑说明Class.forName负责加载驱动MySQL 8 之后驱动类路径变了很多老教程还在写com.mysql.jdbc.Driver你如果照着抄控制台会直接报Loading class com.mysql.jdbc.Driver. This is deprecated然后连接失败。参数方面useSSLfalse不是必须但开着 SSL 而数据库没配证书时会报连接错误本地开发关掉最省事。serverTimezone一定要设否则插入订单时间会比实际时间少 8 小时查订单列表时看着像穿越了。characterEncodingutf8保证中文不乱码配合数据库建库时的utf8mb4使用。改完这四个参数先别急着启动 Tomcat。写一个main方法单独测一下getConnection()能不能返回连接对象能拿到就说明数据库通了再去跑 Web 项目能省掉一半的排查时间。2.3 前端资源引用路径Bootstrap 和 Layui 别打架项目正文里列了一堆 CSS 文件bootstrap.css、bootstrap.min.css、animate.css、layui.css、bootstrap-theme.css、layer.css、layui.mobile.css、bootstrap-table.css。这说明前端混用了 Bootstrap 和 Layui 两套 UI 库。混用不是不行但要注意加载顺序和类名冲突。我一般会把bootstrap.min.css放在最前面然后加载layui.css最后放bootstrap-table.css这种插件样式。JSP 页面里引用路径建议用${pageContext.request.contextPath}开头比如link relstylesheet href${pageContext.request.contextPath}/css/bootstrap.min.css link relstylesheet href${pageContext.request.contextPath}/css/layui.css script src${pageContext.request.contextPath}/js/jquery.min.js/script这样写的好处是不管你项目部署在 Tomcat 的哪个上下文路径下资源都能正确加载。很多人直接把href写成css/bootstrap.min.css本地跑没问题一换环境就全白屏血泪经验就是路径一定要带上下文。另外layer.css是 Layui 的弹层组件样式如果你发现弹窗没样式或者位置错乱先检查这个文件有没有被正确引入以及layer.js是不是在jquery之后加载的。3. 在 IDEA 里跑起来Tomcat 配置、JSP 编译与 MySQL 8 驱动适配3.1 导入项目与 Artifact 设置用 IDEA 打开项目后第一件事是检查Project Structure里的Modules确认src被标记为 Sources RootWebContent或webapp被标记为 Web Resource Directory。然后去Facets里添加 Web Facet指向web.xml的路径。接下来配置 Artifact新建一个Web Application: Exploded把Available Elements里的库全部放到WEB-INF/lib下。这一步不做Tomcat 启动时会报ClassNotFoundException因为 MySQL 驱动和项目依赖的 jar 包没被部署进去。Tomcat 配置在Run/Debug Configurations里新建一个 Tomcat LocalDeployment选项卡里加上刚才的 ArtifactApplication context建议设成/bookstore或者/。如果你设成/那 JSP 里的${pageContext.request.contextPath}会返回空字符串路径拼接依然正确但访问首页直接localhost:8080就行。设成/bookstore的话访问地址就是localhost:8080/bookstore。两种都行看个人习惯。3.2 MySQL 8 驱动版本与 JSP 编译级别MySQL 8 的驱动 jar 包要用mysql-connector-java-8.0.x系列别拿 5.1 的老包硬配否则会报No suitable driver found或者时区错误。把 jar 包放到WEB-INF/lib下IDEA 里右键Add as Library。另外 JSP 编译级别要和 Tomcat 的 JDK 版本匹配如果 Tomcat 跑在 JDK 8 上项目 SDK 也选 8别选 11 或 17不然 JSP 编译时可能报Unsupported class file major version。这个坑很隐蔽因为 Java 代码编译通过了但 JSP 是运行时编译的版本不匹配直接 500。# 查看当前 Tomcat 使用的 Java 版本进入 bin 目录执行 ./version.sh # Linux/Mac version.bat # Windows输出里会显示JVM Version确保和你项目 SDK 一致。如果用的是 IDEA 内置的 Tomcat 配置在Run/Debug Configurations的Server选项卡里能看到JRE选项选对版本就行。3.3 启动后的第一轮验证从首页到后台登录Tomcat 启动没有报错后浏览器访问http://localhost:8080/bookstore/应该能看到网上书店的首页通常有图书分类导航、轮播图或者图书列表。如果首页空白按 F12 看 Network 里 CSS 和 JS 是不是 404大概率是路径问题。如果首页能显示但图片裂了检查images目录下有没有对应的图书封面图数据库book表里的image字段存的是文件名还是完整路径JSP 里拼接的src前缀对不对。后台入口一般是/admin/login.jsp或者/login.jsp默认账号密码在 SQL 脚本的user表里常见的是admin/123456。登录进去后测试一下图书的增删改查重点看新增图书时图片上传功能是否正常。如果上传后图片不显示检查 Tomcat 的server.xml里有没有配Context的docBase或者项目里是不是把上传目录写死在了某个绝对路径上。我一般会把上传路径改成getServletContext().getRealPath(/upload)这样部署到哪都能用。4. 避坑排查JSP 中文乱码、404 与数据库连接池的常见翻车点4.1 现象图书列表页中文书名显示为问号原因JSP 页面顶部没有声明pageEncoding或者 MySQL 连接 URL 里没加characterEncodingutf8再或者数据库建表时字符集是latin1。三个地方只要有一个不对中文就废了。解决JSP 文件第一行加% page languagejava contentTypetext/html; charsetUTF-8 pageEncodingUTF-8%数据库连接 URL 补上characterEncodingutf8如果表已经建好了执行ALTER TABLE book CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;把表和字段的字符集统一改掉。改完重启 Tomcat清一下浏览器缓存再试。4.2 现象访问 Servlet 报 404但 JSP 能打开原因web.xml里 Servlet 的url-pattern和 JSP 表单action里的路径不一致或者 Servlet 类上的WebServlet注解和web.xml同时存在导致冲突。还有一种可能是 IDEA 的 Artifact 里没有把src编译输出到WEB-INF/classes。解决先看 Tomcat 启动日志里有没有Servlet [xxx] marked as unavailable之类的提示。然后检查web.xml的servlet-mapping确认url-pattern是/BookServlet还是/bookServlet大小写敏感。如果用了注解就把web.xml里对应的配置删掉二选一。最后去Project Structure的Artifact里看WEB-INF/classes目录下有没有生成.class文件没有的话在Modules里把src标记为 Sources 并重新 build。4.3 现象数据库连接池报 “Too many connections”原因项目里每次请求都new一个 Connection 且没有关闭或者用了连接池但配置的最大连接数太小而测试时频繁刷新页面。JSPServlet 项目里常见的是在dao层忘了在finally块里close()。解决检查所有Connection、PreparedStatement、ResultSet的使用确保在finally里逆序关闭。如果项目用了 Druid 或者 C3P0去配置文件里把maxActive调到 20 左右同时开启removeAbandoned参数。临时救急可以重启 MySQL但根治还是要改代码。我一般会写一个DBUtil.close(conn, ps, rs)静态方法每个 dao 方法结尾调一下省得漏。4.4 现象订单提交后库存没减或者减成负数原因下单逻辑里先查库存再更新但两步之间没有加事务并发下会出现超卖。或者更新库存的 SQL 写成了update book set stock stock - 1 where id ?但没判断stock 0。解决在 Service 层的方法上加Transactional或者手动conn.setAutoCommit(false)把查库存和减库存放在同一个事务里。SQL 改成update book set stock stock - 1 where id ? and stock 0然后判断executeUpdate()的返回值如果返回 0 说明库存不足回滚事务并给前端提示。这个坑在课程设计答辩时如果被问到能答上来很加分。4.5 现象Layui 弹层不显示或者样式错乱原因layer.js在jquery.js之前加载了或者layui.css和bootstrap.css的z-index冲突导致弹层被遮住。解决确保script srcjquery.js在script srclayer.js之前。如果弹层被遮在layer.open的参数里加zIndex: 99999999或者去layui.css里把.layui-layer的z-index调高。更彻底的办法是只保留一套 UI 库但课程设计一般要求不高能跑就行不用大动干戈。5. 二次开发与答辩加分从现有模块扩展出统计图表和分页优化5.1 用 ECharts 给后台加一个销售统计面板课程设计如果只做了增删改查答辩时容易被问“有没有数据分析”。你可以在后台首页加一个销售统计图表用 ECharts 拉取订单数据。具体做法在dao层写一个getSalesByCategory()方法SQL 用select c.name, sum(oi.price * oi.quantity) as total from orders o join orderitem oi on o.id oi.order_id join book b on oi.book_id b.id join category c on b.category_id c.id group by c.name返回ListMapString, Object。然后在 Servlet 里转成 JSONJSP 页面用 Ajax 请求这个接口ECharts 渲染饼图。// 后台统计页面的 Ajax 请求与 ECharts 初始化 $.ajax({ url: ${pageContext.request.contextPath}/StatServlet?methodsalesByCategory, type: GET, dataType: json, success: function (data) { var chart echarts.init(document.getElementById(salesChart)); chart.setOption({ title: { text: 各分类销售占比 }, tooltip: { trigger: item }, series: [{ type: pie, radius: 60%, data: data // 后端返回 [{name:文学, value:1200}, ...] }] }); } });逻辑说明StatServlet里根据method参数分发salesByCategory方法调用 dao 拿到数据用JSON.toJSONString()或者手动拼 JSON 输出。注意设置response.setContentType(application/json;charsetutf-8)否则前端解析会乱码。ECharts 的data格式是[{name: 分类名, value: 金额}]后端返回的 Map 键名要对应上。这个功能加进去答辩时演示效果直接拉满而且代码量不大一两个小时就能搞定。5.2 手写分页替代 Bootstrap Table 的默认分页项目里如果用了bootstrap-table.css可能表格自带分页但那种分页是前端分页数据量大时性能差。你可以改成后端分页在BookServlet的list方法里接收page和limit参数SQL 用limit ?, ?。计算总记录数用select count(*) from book然后传给 JSP 生成分页条。// BookDao 中的分页查询方法 public ListBook getBooksByPage(int page, int limit) { ListBook list new ArrayList(); String sql select * from book order by id desc limit ?, ?; try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setInt(1, (page - 1) * limit); // 起始偏移量 ps.setInt(2, limit); // 每页条数 ResultSet rs ps.executeQuery(); while (rs.next()) { Book book new Book(); book.setId(rs.getInt(id)); book.setName(rs.getString(name)); // ... 其他字段映射 list.add(book); } } catch (Exception e) { e.printStackTrace(); } return list; }参数说明page是当前页码从 1 开始limit是每页显示条数一般设 10 或 20。(page - 1) * limit算出 SQL 的偏移量比如第 1 页偏移 0第 2 页偏移 10。前端 JSP 里用${totalPages}和${currentPage}渲染上一页、下一页链接。注意limit后面两个参数在 MySQL 里是offset, row_count别写反了。这个改法比前端分页更贴近真实项目答辩时老师问“分页怎么实现的”你能说出limit和count的配合印象分直接上去。5.3 把硬编码的数据库密码挪到 properties 文件项目里DBUtil的密码是写死的交作业时如果被看到不太好而且换环境要改代码。我一般会建一个db.properties放在src下内容如下jdbc.drivercom.mysql.cj.jdbc.Driver jdbc.urljdbc:mysql://localhost:3306/bookstore?useSSLfalseserverTimezoneAsia/ShanghaicharacterEncodingutf8 jdbc.usernameroot jdbc.password你的密码然后在DBUtil的静态代码块里用ClassLoader.getResourceAsStream(db.properties)加载Properties对象读取。这样改配置不用动 Java 代码也显得工程化程度高一点。注意db.properties要放在src根目录下编译后会自动进WEB-INF/classes。如果放在其他包下getResourceAsStream的路径要带包名比如com/xxx/db.properties。从那以后我每次拿到别人的源码包第一件事就是先翻DBUtil和web.xml把数据库连接和 Servlet 映射捋清楚再动手改不然光一个 404 就能耗掉一晚上。希望这份拆解能帮你少走点弯路顺利把课程设计跑起来。本文还有配套的精品资源点击获取