ARTICLE DETAIL

资讯详情

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

JavaWeb课设经典:基于JSP+Servlet+Tomcat的农产品销售管理系统全解析

JavaWeb课设经典:基于JSP+Servlet+Tomcat的农产品销售管理系统全解析 简介一套基于Java与JSP技术栈的农产品销售管理系统开发源码包面向高校计算机相关专业学生、毕业设计选题者以及希望熟悉B/S开发模式的初级开发者。系统运行于Tomcat容器搭配MySQL数据库实现农产品信息展示、购物车管理、订单处理、后台数据维护等功能模块可作为课程设计、项目实训或毕业设计的完整参考方案。压缩包约95.93MB资源内部以项目源码和完整文档为主要文件类型其中源码涵盖Java类、JSP页面及配置文件文档包含设计说明、使用说明等资料能够直接支撑二次开发与报告撰写。资料已经过测试运行部署配置后可正常使用目前已有1440人浏览学习适合需要借鉴项目结构、学习JSPServlet开发流程或快速搭建同类系统的人员。整体目录组织清晰便于按模块查阅借助源码注释和配套文档可有效提高项目实践能力。1. 农产品销售管理系统这个基于JavaJSPTomcat的BS项目到底能干什么如果你正在学校作业、课设或毕设里翻来覆去找一个能跑的Java Web项目这个基于JavaJSPTomcatMySQL的农产品销售管理系统是一个典型到不能再典型的选择。它没有Spring全家桶没有前后端分离就是一个最正统的B/S架构项目浏览器访问JSP页面Tomcat运行Servlet和JSPMySQL负责持久化。系统分成前台和后台两块前台给消费者看农产品列表、按分类筛选、加购物车、下单后台给管理员维护分类、商品上下架、处理订单、管理用户。它的价值不在功能炫而在于把Java Web的知识点串成了一条完整的线Session登录态怎么维持、JDBC怎么连库、SQL怎么做分页、事务怎么控制。适合两类人刚学完Java基础却想看看真实项目长什么样的新手以及需要一套源码加文档作为课设或毕设底子的在校生。下面我从项目结构讲到部署再拆核心代码和踩坑点按可复现的方式写。2. 项目结构复盘一个ServletJSP三层架构数据库里最少有这五张表2.1 源码包的目录结构从src到webapp每个文件夹不是摆设拿到源码解压后第一件事不是急着导入开发工具而是先按目录结构把项目的骨架看明白。最常见的JSPServlet工程分两大部分src目录放Java源码web目录有的叫WebContent或webapp放JSP、静态资源和WEB-INF。src/ ├─ com/agriculture/ │ ├─ entity/ # 实体类 User.java Goods.java Category.java Order.java OrderItem.java │ ├─ dao/ # 数据库访问类 UserDAO.java GoodsDAO.java OrderDAO.java │ ├─ service/ # 业务逻辑层可选 │ ├─ servlet/ # 控制层 LoginServlet.java GoodsServlet.java CartServlet.java │ └─ util/ # DBUtil.java 数据库连接工具类 webapp/ ├─ index.jsp # 前台首页 / 商品列表 ├─ login.jsp # 用户登录 ├─ register.jsp # 用户注册 ├─ goods_detail.jsp # 商品详情 ├─ cart.jsp # 购物车 ├─ order_confirm.jsp # 下单确认 ├─ admin/ │ ├─ goods_manage.jsp │ ├─ order_manage.jsp │ └─ ... └─ WEB-INF/ ├─ web.xml └─ lib/ # mysql-connector-java jar 包这个结构里entity、dao、servlet、util四层基本就是MVC的落地方案。JSP属于ViewServlet是Controllerentity和dao承担Modelutil里的DBUtil统一管数据库连接。很多课设项目不单独拆service层业务逻辑直接写在Servlet里这样更容易理解如果你想练习分层把订单、购物车这类复杂逻辑抽到service包会更舒服。实际项目里有人把JSP全部放在WEB-INF下目的就是不让人通过URL直接访问JSP所有入口都必须走Servlet转发也有项目直接在根目录放JSP简单但安全性差一点。读源码时先看web.xml里面配置了哪些Servlet和映射路径就知道了首页入口在哪里。看代码的顺序我一般建议这样走先在web.xml里找到登录和商品列表两个Servlet映射再到util里看DBUtil的getConnection方法接着挑一个最简单的Servlet看它怎么调用DAO最后打开对应JSP看EL表达式怎么取数据。这一条线走通了整个项目的基本套路也就全通了。切忌打开项目就全局搜索页面又杂很容易被管理员后台那堆代码带偏。2.2 数据库设计五张核心表与一个典型建表SQL农产品销售系统的数据量不大但表之间关系是标准的电商结构用户、商品、分类、订单、订单明细。少了任何一张表核心流程都无法闭环。下面的建表SQL是多数同类源码的相同骨架区别只在字段名和是否允许NULL。CREATE DATABASE IF NOT EXISTS farm_shop DEFAULT CHARACTER SET utf8mb4; USE farm_shop; CREATE TABLE user ( id INT AUTO_INCREMENT PRIMARY KEY, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(50) NOT NULL, nickname VARCHAR(50), phone VARCHAR(20), address VARCHAR(255), create_time DATETIME DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE category ( id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(30) NOT NULL ); CREATE TABLE goods ( id INT AUTO_INCREMENT PRIMARY KEY, category_id INT, name VARCHAR(100) NOT NULL, price DECIMAL(10,2) NOT NULL, stock INT NOT NULL DEFAULT 0, image VARCHAR(255), description TEXT, status TINYINT DEFAULT 1, -- 1上架 0下架 FOREIGN KEY (category_id) REFERENCES category(id) ); CREATE TABLE orders ( id INT AUTO_INCREMENT PRIMARY KEY, user_id INT NOT NULL, order_no VARCHAR(32) NOT NULL, total_price DECIMAL(10,2) NOT NULL, status TINYINT DEFAULT 0, -- 0待处理 1已发货 2已完成 3已取消 create_time DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (user_id) REFERENCES user(id) ); CREATE TABLE order_item ( id INT AUTO_INCREMENT PRIMARY KEY, order_id INT NOT NULL, goods_id INT NOT NULL, price DECIMAL(10,2) NOT NULL, quantity INT NOT NULL, FOREIGN KEY (order_id) REFERENCES orders(id), FOREIGN KEY (goods_id) REFERENCES goods(id) );几个关键设计细节值得你读源码时对照检查。decimal(10,2)存价格不要用float否则算总价会出现0.30000000000000004这种闹心问题。goods表的status字段相当于软下架开关商品下架不是物理删除订单历史里的旧商品信息才能一直保留。orders和order_item分开是因为一个订单可以包含多种商品订单表只存合计金额、订单号和用户状态明细表存每种商品的数量和单价这样订单列表不需要反复join商品表。有一个容易被忽略的设计订单明细表里的price字段应当保存下单那一刻的商品单价而不是去引用goods表的当前价。否则以后商品调价历史订单的金额就跟着变了。这个字段叫“价格快照”电商系统里都有。很多课设源码没做快照但你要是在文档里把这个点讲出来答辩老师会认为你真的看懂了表结构设计。外键约束在课设里很常见但生产环境其实很多人会故意去掉外键改在程序层校验。因为外键会导致插入订单明细时先查父表性能上多一次约束检查。复现这个项目时我一般会先按原样导入如果后续要加大数据量再去掉外键。2.3 技术栈选型JSPServlet做课设不是落后是打基础现在市面上主流是Spring BootMyBatis但这个项目坚持用JavaJSPTomcat这个选择对入行学基础其实更友好。原因有三第一Servlet和JSP是Java Web的原生规范Spring Boot里的DispatcherServlet底层还是在执行Servlet逻辑面试时被问“一个HTTP请求怎么到Controller”你没有这个项目打底会答得很空。第二JSP可以直接在页面里用EL表达式和JSTL标签也可以手写Java片段调试所见即所得适合理解数据是怎么从数据库流转到浏览器的。第三部署简单一个Tomcat扔一个war包就能跑不需要Maven仓库不需要一堆依赖对只装了JDK和MySQL的电脑非常友好。对比Spring Boot项目哪怕一个小demo也要带几十MB的依赖而这个项目通常只有一个mysql-connector-java的jar。如果你是冲着面试去的我建议你把这个系统读透之后再去看Spring Boot怎么把这一套东西封装掉了。JSP里的%user.getUsername()%对应Spring Boot就是ModelAndViewsession.getAttribute(user)对应session域到Session和Cookie的区别转发和重定向的区别在这个系统的登录注册代码里都能找到实例。很多java面试题喜欢问这两组概念而这个系统正好提供了活场景。2.4 一次完整请求的流转从JSP到Servlet到DAO到数据库理解了项目结构后还差一条主线把各个包串起来。以用户登录为例一次请求的实际流转是这样的浏览器填写表单点击提交把username和password以POST方式发给TomcatTomcat根据web.xml里的servlet-mapping找到LoginServletLoginServlet从request参数取值调用UserDAOUserDAO在getConnection里拿到数据库连接执行SELECT * FROM user WHERE username? AND password?查询结果封装成User对象返回给Servlet最后Servlet决定是sendRedirect跳首页还是forward回登录页。servlet servlet-nameLoginServlet/servlet-name servlet-classcom.agriculture.servlet.LoginServlet/servlet-class /servlet servlet-mapping servlet-nameLoginServlet/servlet-name url-pattern/login/url-pattern /servlet-mapping这段web.xml配置里servlet-class是全限定类名Tomcat通过反射加载这个类url-pattern是浏览器访问的路径表单action写/login就能匹配到它。值得多说一句的是url-pattern的写法/login是精确匹配*.do是后缀匹配同一个Servlet可以配置多条url-pattern读源码时别看到多个映射就以为有多个Servlet。请求流转清楚了后面第4章拆核心功能时你会很快知道每个方法该去哪儿找。3. 环境部署全流程JDKTomcatMySQL 8.0的版本组合与四步上线3.1 版本怎么配JDK1.8Tomcat8.5MySQL5.7最常见值得关注MySQL 8.0部署之前先把环境版本确认清楚。这个系统的年资比较老很多课设源码用JDK8和Tomcat8编写现在新电脑装JDK17甚至更高版本会碰到两个问题一是Tomcat9以下版本对高版本JDK支持不完整启动可能直接报找不到类二是旧版MySQL驱动连不上MySQL8。实际复现下来最稳的组合是JDK8、Tomcat8.5、MySQL5.7或8.0均可IDE用IDEA 2020以上版本。如果你只有JDK11Tomcat用9.0以上JSP和Servlet是标准规范没有太花哨的语法照样能跑。关键是在装Tomcat之前把JAVA_HOME配好。很多人Tomcat启动闪一下屏幕就没了原因十之八九是环境变量没配对。# 验证环境三个命令都返回正常才能继续 java -version echo $JAVA_HOME $CATALINA_HOME/bin/startup.sh # Windows下是 %CATALINA_HOME%\bin\startup.bat第一行确认JDK可用第二行确认JAVA_HOME指向了JDK而不是JRE第三行启动Tomcat后访问http://localhost:8080/能看到默认猫脸页面说明Tomcat环境成立。如果之前装过老版本JDK注意把Path里指向C:\Program Files\Java\jre的项删掉否则java -version显示的版本跟JAVA_HOME不一致。这种“明明装对了却在报错”的玄学问题基本都出在Path顺序上。版本对应关系我整理了一张表照着选就行不需要纠结其他组合。组件推荐版本注意点JDK1.8 或 11必须配置JAVA_HOME不要指向JRETomcat8.5 或 9.0JDK11配9.0JDK8配8.5MySQL5.7 或 8.08.0需要换cj驱动连接URL加参数mysql-connector-java5.1.49 或 8.0.33驱动和MySQL版本要匹配IDEA2020以上内置Tomcat集成省去手动部署3.2 数据库初始化导入SQL脚本并改对连接参数源码包里一般会有db_farm_shop.sql或farm_shop.sql这就是数据库的“后悔药”。打开命令行或Navicat执行导入mysql -u root -p farm_shop db_farm_shop.sql如果还没建库就先登录MySQL执行source db_farm_shop.sql脚本里通常自带CREATE DATABASE。导入后打开项目里的db.properties或DBUtil.java检查和修改这几项jdbc.drivercom.mysql.cj.jdbc.Driver jdbc.urljdbc:mysql://localhost:3306/farm_shop?useSSLfalseserverTimezoneAsia/ShanghaicharacterEncodingutf8 jdbc.usernameroot jdbc.password123456这里最容易翻车的是driver和url。MySQL5.7及以下用com.mysql.jdbc.DriverMySQL8.0必须用com.mysql.cj.jdbc.Driver。url里的useSSLfalse和serverTimezoneAsia/Shanghai在MySQL8.0下不加启动项目第一次访问数据库时就会报“SSL connection error”和“The server time zone value”的异常。另一个容易忽略的是characterEncodingutf8不加它中文数据写入和读取都可能变乱码。如果你用纯JDBC封装DBUtil类里加载驱动的代码必须与上面properties保持一致Class.forName(com.mysql.cj.jdbc.Driver); con DriverManager.getConnection(url, username, password);注意Class.forName的字符串不能写错写错会在第一次访问数据库时抛ClassNotFoundException。但Tomcat能正常启动因为数据库连接是懒加载的——只有第一次查询时才会真的去连库。很多同学看到Tomcat启动没报错就以为万事大吉结果一打开登录页就白屏再看控制台才找到这个异常这类问题在项目里尤其隐蔽排查时先去看Tomcat的localhost日志。3.3 导入IDEA并配置Tomcat两步搞定IDEA导入这个项目的正确姿势是Import Project选择源码目录然后选New Project from existing sources一路next。导入后重点检查Project Structure里的Libraries有没有把WEB-INF/lib下的mysql驱动加载进来以及Artifacts是不是Web Application Exploded。如果没有Tomcat启动会直接报找不到数据库驱动但页面和类都能编译又是一个隐蔽坑。# 手动打war包的话在项目根目录执行 jar -cvf farm_shop.war --directory webapp .这是把webapp目录打成war包的命令-c表示新建-v输出过程-f指定war文件名。打出war包后丢到Tomcat的webapps目录启动Tomcat项目会自动解压部署访问路径通常是http://localhost:8080/farm_shop/。用IDEA开发时更推荐在Tomcat配置页的Deployment标签加一个war exploded artifact然后设置Application context为/farm_shop这样修改JSP不用重启热部署对调页面友好得多。访问路径不对时先看Tomcat日志里的“Deploying web application directory”那一行它会直接告诉你实际上下文路径。这里再补充一个Eclipse场景如果你是Eclipse Dynamic Web Project导入后需要配置Targeted Runtimes选Tomcat然后在Servers视图里Add and Remove模块。Eclipse打war包是右键Export WAR file比IDEA更直观。不过IDEA和Eclipse在部署时有个共同坑项目的编译输出目录如果没包含web.xmlTomcat会认为这不是一个标准的Web应用启动日志里出现“No web.xml found”之类信息多半是Artifact输出目录配置错了。3.4 部署方式取舍war包、exploded和直接拷贝webapp三种部署方式各有利弊选一种适合你当前阶段的就行。war包最接近生产环境Tomcat会自动解压但每次改代码都要重新打war调试效率低。IDEA里的war exploded是开发模式代码改了热更新快缺点是一旦IDEA缓存异常部署目录会被清空。还有更简单的方式直接把webapp目录复制到Tomcat的webapps下改个名就能跑适合临时验证但缺点是没有IDE提示编译期错误发现不了。我的建议是第一天部署用IDEA的exploded模式跑通全流程答辩前用war包再验证一次确保换个干净环境也能运行。war包能不能跑直接决定了你交给老师的项目能不能换一台电脑正常演示。很多同学在自己电脑上跑得欢到答辩机器上就白屏通常不是代码问题而是war包没打对。4. 核心功能拆解登录、分页、购物车三块代码里的关键参数4.1 登录与Session管理不只是查一次数据库登录是所有电商项目的地基。这个系统最常规的写法是login.jsp里一个表单提交到LoginServletServlet调UserDAO根据用户名密码查user表查到就写Session查不到就转发回登录页并带错误信息。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(loginError, 用户名或密码错误); request.getRequestDispatcher(login.jsp).forward(request, response); } }两个关键参数行为值得说。request.setCharacterEncoding(UTF-8)必须写在读取参数之前否则中文用户名会变成问号response.sendRedirect用的是302重定向填的地址要带request.getContextPath()否则会跳错地方。而forward是服务端跳转地址栏不变所以把错误信息放在request域中在login.jsp里用EL表达式${loginError}取即可。Session默认存活时间在Tomcat里是30分钟想改2小时需要在web.xml里配置session-config但课设一般不需要浏览器一关Session就没了。这段代码有两点值得思考第一实际项目中密码绝不能明文存储在数据库至少要做MD5加盐或BCrypt哈希课设里为了演示简单才用明文。第二判断登录成功时用的是对象是否为空这里有一个潜在问题如果数据库里存在两个相同用户名密码的记录DAO返回的是第一个正常业务逻辑应该在用户名上建唯一索引第2章的表结构里username字段已经设置了UNIQUE这是对的。4.2 商品列表分页SQL里的LIMIT顺序与参数校验农产品商品数量一多列表页就必须分页。这个系统的分页通常用pageNo和pageSize两个参数实现对应的SQL很简单SELECT * FROM goods WHERE status1 ORDER BY id LIMIT 0, 8;LIMIT后面的第一个数字是偏移量第二个是每页大小。当前页为第1页时偏移是0第2页是8第3页是16换算逻辑是(当前页-1)*每页大小。Java代码里注意这几个值的来源和校验int pageNo 1; String pageNoStr request.getParameter(pageNo); if (pageNoStr ! null !pageNoStr.isEmpty()) { try { pageNo Integer.parseInt(pageNoStr); } catch (NumberFormatException e) { pageNo 1; } } int pageSize 8; int offset (pageNo - 1) * pageSize; ListGoods goodsList goodsDAO.findByPage(offset, pageSize); int total goodsDAO.countAll(); int totalPages (int) Math.ceil(total * 1.0 / pageSize);这里有两个隐藏风险。一是pageNoStr若是一个非数字如abcInteger.parseInt会抛NumberFormatException所以必须做try-catch并默认pageNo1否则输入非法参数页面就500。二是countAll和列表查询必须用同一个筛选条件。刚才SQL里带了status1只查上架商品那么count查询也要加同样的条件否则最后一页可能出现空白。很多同学count用的是SELECT COUNT(*) FROM goods结果列表少了几个商品总页数却还是按全部商品算的最后一页永远显示空。排序也是商品列表里的常见需求。MySQL排序可以用ORDER BY price ASC或ORDER BY sales DESC但如果排序字段来自前端参数千万别把字段名拼进SQL否则可能被SQL注入。稳妥的做法是后端写死几个白名单排序字段比如只允许传price或sales再对应拼接。4.3 购物车与订单Session里攒商品数据库里开事务购物车是这个系统里最值得花时间读的模块。常见实现是不用购物车表直接把HttpSession里的一个Map存“商品ID-数量”用户未登录也能加购物车。MapInteger, Integer cart (MapInteger, Integer) session.getAttribute(cart); if (cart null) { cart new HashMapInteger, Integer(); } Integer goodsId Integer.parseInt(request.getParameter(goodsId)); Integer num cart.get(goodsId); if (num null) { cart.put(goodsId, 1); } else { cart.put(goodsId, num 1); } session.setAttribute(cart, cart);这种写法的好处是零数据库压力缺点是切换浏览器或Session过期后购物车清空。部分项目会把购物车持久化到cart表并绑定user_id做法更严谨但代码量大。读这个模块时重点不是Map操作而是下单时的事务处理。下单时系统要同时做三件事插入订单主表、插入订单明细、扣减商品库存。这三件事必须放在一个数据库事务里否则可能出现“订单已生成但库存没减”或是“钱扣了订单没了”的隐患。JDBC的写法是Connection con null; try { con DBUtil.getConnection(); con.setAutoCommit(false); // 1 插入订单主表 String sqlOrder INSERT INTO orders (user_id, order_no, total_price, status) VALUES (?, ?, ?, 0); PreparedStatement psOrder con.prepareStatement(sqlOrder, Statement.RETURN_GENERATED_KEYS); psOrder.setInt(1, userId); psOrder.setString(2, orderNo); psOrder.setBigDecimal(3, totalPrice); psOrder.executeUpdate(); // 2 插入订单明细 String sqlItem INSERT INTO order_item (order_id, goods_id, price, quantity) VALUES (?, ?, ?, ?); PreparedStatement psItem con.prepareStatement(sqlItem); for (CartItem item : cartItems) { psItem.setInt(1, orderId); psItem.setInt(2, item.getGoodsId()); psItem.setBigDecimal(3, item.getPrice()); psItem.setInt(4, item.getQuantity()); psItem.addBatch(); } psItem.executeBatch(); // 3 扣减库存 String sqlStock UPDATE goods SET stock stock - ? WHERE id ? AND stock ?; PreparedStatement psStock con.prepareStatement(sqlStock); psStock.setInt(1, quantity); psStock.setInt(2, goodsId); psStock.setInt(3, quantity - 1); int rows psStock.executeUpdate(); if (rows 0) { throw new SQLException(库存不足); } con.commit(); } catch (SQLException e) { con.rollback(); e.printStackTrace(); } finally { con.setAutoCommit(true); con.close(); }这段代码里setAutoCommit(false)是开启事务的开关commit和rollback是两条分叉路。特别要强调的是扣库存的UPDATE语句它必须带条件更新。WHERE id? AND stock ?这句是关键如果库存小于本次购买数量executeUpdate返回值是0代码主动抛异常触发回滚这就避免了库存被扣成负数。如果你先用SELECT查库存再在Java里判断够不够在高并发下仍然可能超卖因为两个请求同时读到库存1都执行了扣减库存变成-1。课设里虽然没有并发测试但这个点写在文档里答辩老师通常会高看你一眼java面试题里的“数据一致性”也爱考这个。4.4 注册功能里的重复用户名检查先查一遍还是靠唯一索引注册逻辑通常比登录简单但有个经典问题值得单独说怎么判断用户名已经被注册。常见的做法是在UserDAO里写一个findByUsername注册时先查一次没查到就插入。这种写法在并发下有个漏洞两个请求同时查不到就会插入两条相同用户名数据库会报Duplicate entry异常。所以更稳的做法是依赖第2章表结构里的UNIQUE约束直接尝试INSERT捕获SQLIntegrityConstraintViolationException再提示用户。课设源码里往往是“先查后插”你能把唯一的唯一约束兜底写出来已经比很多商用代码当初的实现要严谨了。5. 避坑部署运行中五个翻车点每条都有现象、原因和解决5.1 现象Tomcat能启动但点登录时页面白屏日志报ClassNotFoundException原因mysql-connector-java.jar没有打进WEB-INF/lib或者IDEA的Artifacts没有包含该jar。Tomcat启动时不会验证驱动类是否存在只有首次执行Class.forName时才找类所以日志里看似“项目启动正常”实际一调数据库就炸。解决把jar放在WEB-INF/lib目录下并右键Add as LibraryIDEA中进入Project Structure-Artifacts在Output Layout列表确认lib已加入如果还不行在Deployment标签里把jar包重新部署一次。5.2 现象登录页输入中文用户名和密码查询结果为空或者页面显示乱码原因三层编码不一致。JSP页面没写pageEncodingTomcat请求参数解码默认用ISO-8859-1MySQL连接URL没带characterEncodingutf8。解决JSP第一行写% page contentTypetext/html;charsetUTF-8 pageEncodingUTF-8%Servlet里在读取参数前加request.setCharacterEncoding(UTF-8)JDBC连接URL加characterEncodingutf8。MySQL5.7以下有时还需要改my.ini里的default-character-setutf8改完重启MySQL服务。修改顺序是先容器再连接最后数据库字符集按这个顺序排查基本一轮就能解决。提示修改编码后一定要清空浏览器缓存再测。很多“改了没效果”其实是浏览器缓存了旧页面。5.3 现象Tomcat双击startup.bat黑窗口一闪而过原因JAVA_HOME环境变量没设置或指向到了JRE而不是JDK。startup.bat脚本需要用到JAVA_HOME/bin/java.exe找不到就直接退出。解决检查环境变量JAVA_HOME是否指向JDK安装目录比如C:\Program Files\Java\jdk1.8.0_131。然后在cmd里手动运行startup.bat窗口会保留报错信息比双击更能看到原因。我当年第一次配环境时把JAVA_HOME指向了jre8Tomcat虽然能显示猫脸但控制台一直在告警JDK相关类不存在换到JDK目录后很多怪问题瞬间消失。5.4 现象部署项目后访问首页CSS样式、图片全部丢失控制台一堆404原因JSP页面里引用静态资源时用了绝对路径比如/css/style.css而项目部署到Tomcat后有上下文路径/farm_shop实际路径应该是/farm_shop/css/style.css少一个项目名前缀就404。解决所有静态资源引用改成动态拼接上下文路径推荐用${pageContext.request.contextPath}link relstylesheet href${pageContext.request.contextPath}/css/style.css同时表单action和页面跳转链接也要带上contextPath否则提交表单后405或404。这类路径问题属于Web项目最常见的“路径门”新手常在这里卡几小时最后发现只是少了前缀。查这个问题最直接的方式是打开浏览器开发者工具看你刷新后哪个请求返回404再对比请求URL和项目实际路径。5.5 现象订单提交显示成功但数据库orders表里没有记录原因最常见的是事务没生效。一种情况是Service层异常被catch后没有rollback事务虽然开了但没回滚另一种是源码里commit()放错了位置在INSERT语句执行前就提交了还有一种是连接池配置了自动提交而con.setAutoCommit(false)没有真正执行。解决在catch块里明确调con.rollback()在finally里检查Connection是否null同时用debug看执行到哪一步。验证事务是否真的关闭自动提交很简单在commit前后各打印一行日志如果commit还没执行数据库里就有数据说明自动提交还开着。这个坑我帮别人改过好几次多数源码里把setAutoCommit(false)写在了try外面导致事务开关没生效。5.6 排查套路三层日志法快速定位是Tomcat、MySQL还是代码问题部署这类系统遇到问题不要乱翻代码先按下面三层定位。第一层看Tomcat启动日志访问logs/catalina.out重点搜SEVERE、Exception这一步解决90%的部署问题。第二层看MySQL端确认能登录、能看到farm_shop库、能执行简单的SELECT必要时开启SHOW PROCESSLIST看是否有连接卡住。第三层看浏览器控制台404看路径500看Tomcat日志堆栈白屏看控制台是否有报错。这三层走一遍问题基本能圈定在一个小范围内。注意如果Tomcat日志里看到的是“Communications link failure”先ping一下MySQL所在主机IP再检查MySQL是否允许远程连接。很多坑其实是MySQL服务没启动被误判成代码问题。6. 进阶用法给后台订单列表加一个CSV导出顺带处理库存预警这篇最后分享一个实用扩展很多农产品销售系统后台只有订单表格没有导出功能。借这个项目练手时我建议给它补一个简单的CSV导出和库存预警列表对答辩和实际使用都有加分。CSV导出的思路不用引入POI直接用Java输出文本流每行字段用逗号分隔生成.csv文件即可。在OrderServlet里加一个actionexportresponse.setContentType(text/csv;charsetUTF-8); response.setHeader(Content-Disposition, attachment;filenameorders.csv); PrintWriter out response.getWriter(); out.println(\uFEFF订单号,用户,总价,状态,时间); // BOM头避免Excel打开中文乱码 for (Order order : orderList) { out.println(order.getOrderNo() , order.getUserName() , order.getTotalPrice() , order.getStatus() , order.getCreateTime()); } out.flush(); out.close();注意三处关键参数setContentType必须是text/csv而不是text/html否则浏览器把结果当网页打开Content-Disposition里的attachment告诉浏览器下载而不是显示输出流写第一行时加上UTF-8的BOM头\uFEFF否则Excel打开后中文全乱码。导入这条代码后访问OrderServlet?actionexport就能直接落一个CSV到本地比在数据库里翻查快得多。库存预警的SQL更简单在后台首页加一个统计查询SELECT name, stock FROM goods WHERE stock 10 AND status1 ORDER BY stock ASC;设置一个10的警戒线把低于这条线的商品在后台Dashboard列表里用红色标出。你可以在GoodsDAO里加一个getWarningList方法然后把结果set到request域在admin/index.jsp里遍历输出。这个扩展成本很低但让系统的“管理”属性体现出来了对答辩老师来说是一个加分项。最后说一个我的血泪经验之前帮一个同学改这个系统数据量测试时只有几百条商品导出订单功能做得挺顺后来他导了一万条订单浏览器直接卡死。原因是我把全表查出来再遍历输出没有做分批查询。从那以后我每次给这类项目加导出功能都会强制检查一遍SQL是否带分页或分批条件即使是导出全量数据也会限制每批数量防止内存炸掉。希望这个习惯能帮到你完整源码、数据库脚本和配套文档就在资源包里下载后按第3章的部署流程走一遍十分钟内应该能跑起来。本文还有配套的精品资源点击获取
返回列表