
简介基于JSP的网上书店系统毕业设计完整交付包包含项目源码、毕业论文和演示视频适合Java Web初学者及需要完成毕业设计的高校学生。系统采用B/S结构与JSP技术覆盖用户登录、管理员管理、图书管理、销售主页、个人资料修改等模块并讨论了Session与Cookie安全、身份验证机制及SQL Server数据库保护完整展示管理信息系统的开发流程。压缩包约23.73MB内含可直接运行的源代码、详细论文文档和视频演示视频演示可直观了解系统操作与页面流转。论文从需求分析、数据库概念结构设计和逻辑结构设计到系统功能模块划分及前端页面开发均有论述能够帮助读者快速理清从设计到实现的完整思路。目前已有122人浏览学习可作为网上书店或同类电商系统的设计参考、代码学习和文档撰写模板毕业设计与论文写作阶段可参考其搭建原型并理解各模块间关系减少从零开发的试错成本。1. 基于JSP的网上书店系统毕业设计到底在做什么值不值得选如果你正在找JavaWeb方向的毕业设计大概率会撞见这个标题基于JSP的网上书店系统。它不算新东西但确实是JavaWeb里最典型的练手项目——用JSP写页面、Servlet处理请求、JDBC访问MySQL把图书展示、用户登录、购物车和订单这一整条电商闭环跑通。这类项目的好处是技术栈单一、代码量可控、论文好写坏处是如果只会照着抄答辩时容易一问三不知。这篇笔记就按我实际做过类似项目的流程把从导入源码到能讲清楚的技术细节拆给你包括环境版本、数据库设计、核心代码和那些不跑一遍根本发现不了的坑。不管是想快速交差还是想借这个题目把JavaWeb基本功补起来都值得往下看。2. JSP网上书店系统的技术架构与核心原理从MVC到数据库设计2.1 为什么这套组合还不过时JSPServletJDBC vs Spring Boot很多同学第一反应是现在外面写JavaWeb都用Spring BootJSP这种老技术还有必要选吗如果是工作项目确实没必要但毕设是另一回事。绝大多数高校的JavaWeb课程还在讲JSPServlet答辩老师对这套流程更熟悉你讲起来也容易对齐。更关键的是JSP项目的代码是“透明”的你写一个login()方法调用关系一眼能看穿不像Spring Boot里一堆注解和自动装配出了错只会看到一个深不见底的堆栈。从依赖上看JSP网上书店系统通常只需要四样东西Servlet API、JSTL标签库、MySQL驱动外加一个Tomcat运行时。Servlet API负责处理HTTP请求JSTL负责在JSP里写c:forEach这类标签来循环展示数据MySQL驱动负责让JDBC能连上数据库。没有Maven的版本冲突没有Bean的生命周期连new对象都是手动来。对新手来说这种“原始”恰恰是好事因为你能在断点里看到每个对象是怎么创建出来的。再说运行原理。浏览器访问index.jsp时Tomcat会把jsp文件翻译成一个Java类这个类本质上是HttpServlet然后经过编译、加载、实例化service方法里根据请求方式分发到doGet或doPost。所以JSP不是一上来就被浏览器直接执行的它最终还是一个Servlet。理解了这一点你就明白为什么JSP页面里可以用request、response、session、application这些内置对象——因为它们就是Servlet方法里的参数和成员。对比Spring BootJSP项目少了Controller层的GetMapping和RequestParam改成在web.xml里写servlet-mapping或者在Servlet类上用WebServlet注解。如果你在源码里看到web.xml配了servlet路径别急着删那是老项目最常用的映射方式。两者没有对错你只要在论文里写清楚自己用的是哪一种就行。2.2 数据模型怎么设计用户、图书、订单三张核心表的关系网上书店系统的核心表一般不会超过六张最少的情况是三张用户表、图书表、订单表。但如果订单和图书是多对多直接一张订单表就存不了多本书所以必须拆订单明细表。很多同学的数据库设计问题就出在这里把“订单表”设计成一行存一本书同一个订单买三本书就插三条相同order_id的记录这样查订单总价和改地址都会非常难受。我习惯先画一个关系简图用户1—N订单订单1—N订单明细图书1—N订单明细。然后每个表按第三范式拆字段。下面是一套常见的建表SQL骨架你可以直接对照源码调整CREATE TABLE user ( user_id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(64) NOT NULL, phone VARCHAR(20), address VARCHAR(255), create_time DATETIME DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE book ( book_id INT PRIMARY KEY AUTO_INCREMENT, book_name VARCHAR(100) NOT NULL, author VARCHAR(50), price DECIMAL(10,2), stock INT DEFAULT 0, cover VARCHAR(255) ); CREATE TABLE orders ( order_id INT PRIMARY KEY AUTO_INCREMENT, user_id INT NOT NULL, total_price DECIMAL(10,2), status TINYINT DEFAULT 0, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, CONSTRAINT fk_order_user FOREIGN KEY (user_id) REFERENCES user(user_id) ); CREATE TABLE order_detail ( detail_id INT PRIMARY KEY AUTO_INCREMENT, order_id INT NOT NULL, book_id INT NOT NULL, quantity INT NOT NULL, price DECIMAL(10,2) NOT NULL, CONSTRAINT fk_detail_order FOREIGN KEY (order_id) REFERENCES orders(order_id), CONSTRAINT fk_detail_book FOREIGN KEY (book_id) REFERENCES book(book_id) );字段这里有几个容易忽视的点。第一价格要选DECIMAL而不是FLOAT否则计算总价时会出现浮点误差答辩现场演示到这一步会很尴尬。第二订单状态的status用TINYINT0代表待付款1代表已付款2代表已发货3代表已完成比写字符串更省空间也方便JSP页面用c:if做状态显示。第三外键要加但也不用加太多订单表关联用户、明细表关联订单和图书就够了多了反而影响插入效率。再说购物车表。如果源码里没有购物车表那它多半是把购物车放在Session里数据结构是MapbookId, 购买数量。这样做的好处是不用为临时数据建表坏处是用户关闭浏览器购物车就没了。如果源码里有一个单独的cart表那就要在表里加user_id和book_id两个外键。两种方案各有取舍论文里挑一种说清楚即可。2.3 MVC分层与请求流转一次登录请求到底经过哪些文件理解JSP项目最快的方式就是挑一个最简单的操作从上到下追一遍。我们拿登录请求开刀。假设你在login.jsp里写了一个表单methodpostactionloginServlet。用户点登录后Tomcat会根据web.xml里的配置把请求交给LoginServlet的doPost方法。doPost里第一步是设置request.setCharacterEncoding(UTF-8)这一步不能省省了中文用户名就乱码第二步getParameter(username)取出表单值第三步调用UserDao.findByUsernameAndPassword这个Dao里用JDBC拼一条select语句把结果集映射成User对象返回第四步Servlet判断User是否为null不为null就把user对象放进session然后response.sendRedirect(index.jsp)为null就把错误消息放进request域再request.getRequestDispatcher(login.jsp).forward(request, response)。这个流转看似简单但里面有四个答辩高频问题。第一个问题是doGet和doPost的区分表单用post就重写doPostGET请求就重写doGet如果两个都用到那就在Servlet里同时重写两个方法并都调用同一个业务方法。第二个问题是sendRedirect和forward的区别前者是302重定向request属性会丢后者是内部转发request属性还在。第三个问题是JSP里怎么拿数据重定向后request里的属性会丢但session里的不会所以登录成功后的用户信息要放session而错误提示只能在forward下用request传递。第四个问题是DAO层为什么要返回User对象而不是boolean因为登录成功后个人信息展示页面还要展示手机号、地址一次性查出来省得再查一次。为了让流程更好讲建议在Servlet里按步骤打日志比如logger.info(login success, user{}, username)。答辩时打开控制台顺着日志讲比对着代码念有说服力得多。后面第6章我还会专门说怎么用断点和日志把整个链路验证一遍这里先记住一条核心心法所有JSP项目问题归根结底都能追溯到“请求在哪一步断了”。3. 从零跑通这个毕设项目环境搭配、导入源码、初始化数据库3.1 环境版本怎么选JDK 8 Tomcat 8.5 MySQL 5.7 的搭配理由先解决版本问题。我见过太多人在环境上折腾两三天最后发现是JDK版本和Tomcat版本不匹配。常见做法是JDK 1.8 Tomcat 8.5 MySQL 5.7这套组合兼容性最高任何教程里的代码都能跑。如果你用Eclipse对应装Java EE版IDEA的话选Community版再装一个Smart Tomcat插件就能本地跑。为什么不用JDK 11或17因为老项目的javax.servlet包名从Jakarta改名后Tomcat 9和Tomcat 10之间不兼容而网上大部分JSP源码还是基于javax写的。JDK 8 Tomcat 8.5能保证javax命名空间没问题这也是网上书店系统最常见的部署组合。MySQL选5.7主要是避开8.0的时区认证坑如果你已经装了MySQL 8.0也没关系后面3.2节会讲怎么配。Tomcat版本不建议低于8.0否则Servlet 3.0的WebServlet注解可能不生效。环境装完后建议先做一个最小验证启动Tomcat浏览器访问http://localhost:8080能出现默认首页再打开一个命令行窗口敲mysql -u root -p能进MySQL。这两个动作能通过再继续导入项目否则后面出了问题很难判定是环境还是代码。3.2 导入项目与初始化数据库SQL脚本和JDBC配置一步步来拿到zip包后先解压用IDEA或Eclipse以Web项目方式导入。常见结构是src目录下放Java代码WebContent/WEB-INF下放web.xml和JSP页面根目录还有bookshop.sql这类数据库脚本。如果你的IDE没识别成Web项目可以手动添加Web Facet再把Tomcat关联到项目上这一步不做运行按钮会一直是灰的。数据库这边打开MySQL客户端创建一个库再执行脚本CREATE DATABASE bookshop DEFAULT CHARACTER SET utf8mb4; USE bookshop; SOURCE /你的路径/bookshop.sql;执行完用show tables;看一下表是否都建出来了。这里有个血泪经验数据库默认字符集如果不对后面页面显示中文全是问号所以创建库时要显式加上DEFAULT CHARACTER SET utf8mb4。脚本里有中文注释的话记得把SQL文件本身转成UTF-8无BOM编码否则第一行注释乱码影响不大但插入中文数据可能报Incorrect string value。然后找到项目里的JDBC配置文件。老项目常见是src/db.properties或者src/jdbc.properties里面写着driver、url、username、password。你需要改成自己的数据库密码jdbc.drivercom.mysql.jdbc.Driver jdbc.urljdbc:mysql://localhost:3306/bookshop?useUnicodetruecharacterEncodingutf8useSSLfalse jdbc.usernameroot jdbc.password你的密码参数说明useUnicodetrue和characterEncodingutf8是保证数据库读写中文不乱码useSSLfalse是关掉MySQL 5.7默认的SSL警告如果是MySQL 8.0还要把driver改成com.mysql.cj.jdbc.Driver并在url后面加serverTimezoneAsia/Shanghai否则会报时区错误。这些参数看着琐碎但每一个都是前人踩过坑换来的。改完后检查一下jar包。老项目的WEB-INF/lib里通常会带mysql-connector-java-5.1.x.jar和jstl.jar。如果lib下没有你就得自己拷进去在IDE里右键项目Build PathAdd Library或者直接把jar扔到WEB-INF/lib目录下。注意Tomcat运行时不会自动加载外部jar所以别放在项目外的目录还指望它能跑。3.3 部署到Tomcat并验证主流程启动参数与访问路径部署这一步把项目右键Run on Server选择之前配好的Tomcat 8.5。如果项目没有自动发布到webappsIDEA会生成一个exploded目录Tomcat启动后去访问http://localhost:8080/项目名/。项目名在Eclipse里默认是工作区里的项目名在IDEA里是Application context里配置的通常叫/bookshop。启动日志是第一个排查入口。如果看到Exception starting filter或ClassNotFoundException多半是lib里的jar冲突或者Servlet版本不对。启动成功后先进首页再走一遍注册、登录、加购、下单流程。这个验证动作至少要做两遍一遍按正常顺序一遍故意不登录就访问订单页看会不会被拦截。另外要特别注意一点如果你看到源码里 import javax.servlet.http.HttpServlet而你的Tomcat版本是10.x那么运行时会直接报ClassNotFoundException。因为Tomcat 10把包名改成了jakarta.servlet老代码必须做全局替换才能跑这个工作比换个Tomcat版本麻烦得多。所以我坚持推荐Tomcat 8.5不是它新而是它最省事。很多JSP网上书店系统的订单页面会在web.xml里配一个简单的过滤器只放行login.jsp、index.jsp和静态资源其他路径都必须有session里的user对象。如果你发现直接访问某个jsp页面也能看到内容说明过滤器配置漏了。这个过滤器属于安全基础论文里建议单独写一节。下一章我会拆核心功能的代码把这些流程对应的代码和你讲清楚。4. 核心功能代码拆解登录会话、图书分页、购物车与订单事务4.1 用户登录与Session一个HttpSession对象管住整个购物链路先看登录Servlet的典型写法WebServlet(/loginServlet) public class LoginServlet extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding(UTF-8); String username request.getParameter(username); String password request.getParameter(password); UserDao dao new UserDao(); User user dao.findByUsernameAndPassword(username, password); if (user ! null) { HttpSession session request.getSession(); session.setAttribute(user, user); response.sendRedirect(request.getContextPath() /index.jsp); } else { request.setAttribute(error, 用户名或密码错误); request.getRequestDispatcher(/login.jsp).forward(request, response); } } }逻辑说明整个类用WebServlet注解完成URL映射省去web.xml配置。doPost里先处理编码再查库最后根据结果决定重定向还是转发。注意response.sendRedirect用request.getContextPath()拼路径这是避免项目部署路径写死的关键。这段代码没有重写doGet所以浏览器直接访问loginServlet会返回405错误要支持GET请求需要再重写doGet方法并调用doPost。写法要点session.setAttribute(user, user)存的是对象JSP里用${sessionScope.user.username}就能取出。登录成功后不要把密码也放在cookie里session已经够用。退出登录则调用session.invalidate()它会把购物车数据一起清掉符合“退出即清空”的预期。如果你要实现“记住我”单纯靠session做不到需要额外用cookie存token这个属于扩展点可加在论文的展望里。4.2 图书列表分页查询DAO层怎么写、JSP怎么渲染图书列表是首页的核心。数据量少的时候可以一次性全查但毕设答辩时老师常会问“如果有一万本书怎么展示”所以分页几乎必做。分页的标准做法是先查总条数再按limit计算偏移量。下面这个DAO方法返回一个PageBean里面封装了list、totalCount、currentPage、pageSizepublic PageBeanBook findPage(int currentPage, int pageSize) { PageBeanBook pb new PageBean(); String countSql SELECT COUNT(*) FROM book; String pageSql SELECT * FROM book LIMIT ?, ?; int totalCount queryForInt(countSql); int totalPages (int) Math.ceil(totalCount * 1.0 / pageSize); ListBook list queryForList(pageSql, (currentPage - 1) * pageSize, pageSize); pb.setList(list); pb.setTotalCount(totalCount); pb.setTotalPages(totalPages); pb.setCurrentPage(currentPage); pb.setPageSize(pageSize); return pb; }逻辑说明LIMIT的第一个参数是偏移量第二个是条数。第1页偏移0第2页偏移pageSize所以公式是(currentPage - 1) * pageSize。分开写两条SQL的原因很简单COUNT查询和分页查询的返回结构不同不能用同一条SQL硬套。这里用的是PreparedStatement参数下标从1开始。JSP页面拿到PageBean后用JSTL渲染核心就这一段c:forEach items${pb.list} varbook div classbook-card img src${pageContext.request.contextPath}${book.cover} / p${book.bookName}/p p${book.price}/p a hrefaddCartServlet?bookId${book.bookId}加入购物车/a /div /c:forEach这里最大的坑是图片路径。book.cover如果存的是“upload/cover1.jpg”这种相对路径浏览器会在当前URL目录下找结果就是404。正确做法是前面加${pageContext.request.contextPath}拼成/项目名/upload/cover1.jpg。很多人遇到“jsp图片如何对坐标定位”的问题、图片显示不出来90%都是这个原因后面避坑章节我会再展开。4.3 购物车加入与订单提交事务边界放在哪里购物车如果不落库可以用Session存一个MapInteger, Integerkey是bookIdvalue是数量。添加购物车的Servlet里先判断session里有没有cart没有就new一个HashMapHttpSession session request.getSession(); MapInteger, Integer cart (MapInteger, Integer) session.getAttribute(cart); if (cart null) { cart new HashMap(); } int bookId Integer.parseInt(request.getParameter(bookId)); cart.put(bookId, cart.getOrDefault(bookId, 0) 1); session.setAttribute(cart, cart);逻辑说明getOrDefault是Java 8的写法取不到值时默认0然后加1。用Map的好处是不需要一张购物车表加减商品都只改value。缺点是重复点击“加入购物车”会连续累加如果用户刷新页面购物车数量会翻倍所以前端可以加个按钮禁用或者跳转时用sendRedirect来避免重复提交。真正的重头戏是下单。下单要同时做三件事往订单表插一条、往明细表插多条、扣减图书库存。任何一个失败前面成功的都要回滚否则会出现财务对不上的脏数据。因此事务必须放在这一条链路的外层Connection conn null; try { conn DBUtil.getConnection(); conn.setAutoCommit(false); // 开启事务 int orderId orderDao.insertOrder(conn, user.getId(), totalPrice); for (Map.EntryInteger, Integer entry : cart.entrySet()) { orderDetailDao.insertDetail(conn, orderId, entry.getKey(), entry.getValue()); bookDao.decreaseStock(conn, entry.getKey(), entry.getValue()); } conn.commit(); // 全部成功才提交 session.removeAttribute(cart); // 清空购物车 } catch (Exception e) { if (conn ! null) conn.rollback(); // 出错回滚 e.printStackTrace(); request.setAttribute(error, 下单失败库存不足); } finally { DBUtil.close(conn); }逻辑说明所有DAO方法都改成接收Connection参数这是为了保证它们用的是同一个数据库连接而不是每次调用都新开连接。setAutoCommit(false)之后必须显式commit才算生效。rollback让前面所有insert和update全部撤销。注意库存不足时要在decreaseStock里用update book set stock stock - ? where book_id ? and stock ?这样的条件SQL来判断如果返回0就需要手动抛异常触发回滚。事务边界是这个项目里最容易讲出深度的部分。答辩时老师问“并发下单库存会不会超卖”你可以回答在where条件里加stock ?让数据库自己判断库存是否够比在Java里先查再减更安全。这句话说出来整个代码的水平就和其他人拉开差距了。5. 避坑指南JSP网上书店系统的5个典型翻车点与排查方法5.1 现象Tomcat启动后访问项目一直404 / 500原因分两种。404是路径没配对500是代码或环境问题。404最常见于项目部署名和访问URL不一致比如项目名是bookshop你访问localhost:8080/却看到Tomcat首页访问/bookshop/才进系统。第二个原因是Servlet的WebServlet(/loginServlet)和表单的action写成loginServlet.do路径里多个后缀就匹配不上。500常见于lib缺少jar包比如ClassNotFoundException: com.mysql.jdbc.Driver说明驱动没进WEB-INF/lib。解决方法是打开Tomcat的catalina.out日志按异常栈第一行定位别被一大段Caused by带偏。我的习惯是先用浏览器直接访问一个静态JSP如果静态页能开而请求Servlet就500那么问题大概率在DAO或JDBC层。再用一个空Servlet测试URL映射通不通逐步缩小范围。5.2 现象登录后中文用户名变成乱码或者页面全是问号这是JSP老项目最常见的病。原因有三层哪一层不对都会乱码第一层表单提交时页面本身要是UTF-8如果你没有在JSP第一行写pageEncodingUTF-8浏览器可能用其他编码提交第二层Servlet没有调用request.setCharacterEncoding(UTF-8)第三层数据库连接URL没加characterEncodingutf8或者数据库表本身是latin1。解决时三个地方一起改所有JSP增加% page contentTypetext/html;charsetUTF-8 %所有Servlet的doPost第一行先setCharacterEncodingdb.properties连接串补上useUnicodetruecharacterEncodingutf8。检查技巧是在Servlet里取到username后先打印一遍如果打印出来乱码说明提交或取值环节有问题如果打印正常而数据库里乱码说明连接串或表字符集有问题。打印是黑匣子最好的突破口。5.3 现象启动时提示Access denied for user rootlocalhost密码不对是最初级的问题但经常被忽略db.properties里写的密码是别人的你需要改成自己的。第二个原因是MySQL 8.0的认证插件升级成caching_sha2_password而项目里的老驱动还是5.1.x两者不兼容报错是Public Key Retrieval is not allowed。解决方法是换用mysql-connector-java 8.0.x驱动URL里加allowPublicKeyRetrievaltrue。第三种情况是Tomcat能启动但第一次查库就报CommunicationsException通常是MySQL服务没启动或者URL里的端口改过检查3306是否被占用。如果你不确定驱动最简单的方式是写一个Java主方法直接main里测试Connection绕过Tomcat能把问题定位到驱动层面还是项目层面。5.4 现象页面能开但图书封面、CSS、JS全都不显示这是JSP图片路径问题的集中体现。网上书店的图书信息展示页面经常要显示封面图而封面图一般存在项目根目录的upload文件夹下。如果JSP里写的是那么当当前URL是/bookshop/bookList.jsp时浏览器会解析成/bookshop/upload/cover1.jpg没问题但如果你通过/bookshop/indexServlet跳转过来的页面URL变成/bookshop/indexServlet相对路径就会拼到/bookshop/indexServlet/upload/cover1.jpg结果404。解决方法是所有图片路径都写绝对路径前面加${pageContext.request.contextPath}即${pageContext.request.contextPath}/upload/cover1.jpg。至于“jsp图片如何对坐标定位”那属于CSS问题一般用width/height或者background-position注意不要用table布局硬调像素响应式会崩。还有一个隐藏坑Tomcat默认不允许直接访问WEB-INF目录下的文件所以图片不要放WEB-INF/upload要放在WebContent/upload下。5.5 现象下单成功但订单状态不变或者反复刷新生成重复订单先说重复订单用户在orderConfirm.jsp提交后如果按F5刷新浏览器会重新提交上一次POST请求导致同一个购物车内容生成两笔订单。解决方式是在下单成功后sendRedirect到订单完成页而不是forward。这个做法叫Post/Redirect/Get模式论文里值得提。订单状态不变通常是写库失败但没给用户提示。我见过一个源码在业务方法里catch异常然后continue导致部分明细插入成功订单主表却回滚最后状态还是0。正确做法是要么全部成功要么全部回滚。如果你发现代码里没有conn.setAutoCommit(false)那这个项目十有八九没有做事务你需要自己补上。这个坑特别隐蔽我建议下单流程走一遍后去数据库看一眼orders和order_detail表的数据别只看页面出现“下单成功”就放心。5.6 现象IDEA/Eclipse里能跑但导出war部署到独立Tomcat就404这是很多毕设项目的最后一关。原因通常是IDE运行时把Tomcat的conf/context.xml配了数据源而导出的war包没有这个配置。另外项目里如果有绝对路径如“/Users/xxx/...”那换一台电脑必挂。处理方法是把db.properties里的配置全部改成相对Web应用的写法或者在数据源初始化时用classpath资源路径。如果war包部署后JSP页面能开但Servlet请求404检查web.xml的servlet-mapping里有没有把jsp扩展名拦截或者看Tomcat manager里的Application Context路径是不是/。建议直接用ROOT.war方式部署访问localhost:8080/就能进少一级路径也少一个坑。6. 答辩与扩展从「能跑」到「能讲清楚」的三个验证技巧6.1 用日志和断点把核心链路串起来答辩前我建议你挑三个流程登录、加购、下单分别打上日志或者用断点走一遍。具体做法是在Servlet入口打一个断点再在DAO的查询方法打一个断点F8单步追看request参数是否完整、SQL参数是否正确、返回结果是否为空。这一步走完你对这个项目的熟悉程度会立刻超越班上一半照着抄的人。如果项目没接日志框架可以用System.out.println临时打但记得答辩前删掉或者保留成logger不然老师看到控制台满屏System.out会皱眉。6.2 画一张请求时序图作为答辩开场不需要用复杂工具就用ProcessOn或手画一张登录时序图浏览器→LoginServlet→UserDao→MySQL→返回User→写入Session→跳转index.jsp。这张图放在PPT第二页讲三分钟老师能快速看到你懂不懂这个系统比贴满屏代码强。6.3 三个低成本加分扩展模糊搜索、库存扣减、个人信息展示页第一个扩展首页加一个按书名模糊搜索的输入框DAO里一处select where book_name like ?就够。第二个扩展修改库存的SQL用update book set stock stock - ? where book_id ? and stock ?并在业务层判断返回值。第三个扩展做一个jsp个人信息展示页面显示用户基本信息、历史订单列表这就用到了登录时存的user对象和历史订单查询。这三个改动都不大但答辩时你可以说“我考虑了并发扣库存、实现了个人信息聚合展示”这就是亮点。最后说一个我自己的教训不要到答辩前一晚才第一次导出war也不要只在IDE里跑通就觉得自己会了。我见过同学在演示时换个浏览器就白屏最后发现是浏览器缓存了旧版CSS。正确做法是答辩前一周用干净的Tomcat从头到尾不接触IDE纯命令行启动把流程走两遍。这事看着麻烦但能帮你把环境里的玄学问题提前消掉。希望帮到你。本文还有配套的精品资源点击获取