☰
SSM宠物管理系统课设全解析:Action类、数据库设计与Layui对接
2026/10/9 7:07:08 网站建设 项目流程

简介:基于Java的宠物管理系统是一套面向高校课程设计场景的完整项目源码与配套资料,适合正在学习SSM框架、Java Web开发或准备课程设计的读者。系统采用Spring、SpringMVC、MyBatis搭建后端,结合Layui构建交互界面,以MySQL存储宠物、用户、领养记录等核心数据,覆盖了需求分析、数据库设计、编码实现、测试与文档撰写等主要环节。包内共有1442个文件,压缩包约136.6MB,以js、jsp、java前后端源码为主,同时包含png、gif页面素材、css样式表、jar依赖包,以及sql数据库脚本和doc文档,目录结构清晰,便于按模块查阅学习。该资源已有2171人学习下载。通过研读源码与配套文档,可以直观理解SSM框架如何分层整合、后台管理功能如何具体实现,以及如何借助Layui制作数据表格和表单交互;sql脚本和测试文档还能帮助快速还原系统环境,验证核心流程,对提升Java Web课程设计与实战能力很有帮助。

1. 宠物管理系统下载后第一件事:先看懂这九个 Action 类

刚拿到这份「基于 Java 的宠物管理系统」源码包时,如果直接往 MyEclipse 里导入,大概率会被一堆 .class 编译文件晃花眼。把 DingdanAction、HuiyuanAction、ShangpinAction、XinxiAction、UsersAction、SupplyorAction、SitenavAction、AlbumAction、LeavewordAction 这些类名按业务含义翻译一遍,你其实已经拿到了这个系统的完整功能地图:订单、会员、商品、资讯信息、用户、供应商、站点导航、相册、留言。这九个模块组合起来,就是一个典型的「商品 + 会员 + 订单 + 内容展示」后台管理结构,只是业务对象换成了宠物。它用 SSM 做后端分层,用 Layui 渲染管理界面,以 MySQL 存数据,MyEclipse 做开发环境。对正在做 Java 课设的人而言,这份资源的价值不只是能跑,而是能让你看到 SSM 项目的 Action 层、DAO 基类、前端框架是怎么拧成一股绳的。下面我按「先认骨架、再建表、再对前端、最后排坑」的顺序,把它拆开讲透。

2. 从 .class 文件反推项目骨架:Action 类拆分和 SSM 三层架构的映射

2.1 Action 类名就是最好的模块说明书

不用看文档,光是项目正文里那串类名,就能画出这个系统的业务模块。

DingdanAction 对应订单管理,HuiyuanAction 对应会员管理,ShangpinAction 对应商品管理,SupplyorAction 对应供应商管理,XinxiAction 对应资讯信息管理,AlbumAction 对应相册管理,LeavewordAction 对应留言管理,SitenavAction 对应站点导航管理,UsersAction 对应后台用户管理。这种命名习惯在早期的 Java Web 课设里很常见,类名直接暴露业务领域,比那些国际化命名容易懂得多。

那 DALBase.class 是干什么的?从名字看,DAL 是 Data Access Layer 的缩写,它应该是数据访问层的公共基类,负责封装数据库连接的获取、释放、通用增删改查,子类只需要声明自己的表名和字段就能继承能力。你可以把它理解成整个系统的数据访问底座,业务模块的数据操作能力都从这里继承。这种设计虽然不如 MyBatis Generator 生成的接口那么规范,但对只有几周时间的课设来说,反而更直观,调试的时候打断点也更容易定位。

从这些类还能看出一个关键决策:它是用「每个业务模块一个 Action」的方式组织请求入口,而不是像 SpringMVC 那样一个 Controller 里塞多个方法。这说明原项目作者可能用的是类似 Struts2 的 Action 命名习惯,或者是一个自定义的 MVC 框架。这正好是课程设计的经典取值范围:老师不会要求你上微服务,你只要把 SSM 框架的职责讲清楚,把请求转发、参数注入、业务调用、页面跳转这条链路走通,评分就能拿到。

2.2 SSM 框架在这个项目里各管哪一段

先把 SSM 三层和这个项目里的实际文件对应起来:

Spring 管的是容器和依赖注入。在这个系统里,Spring 负责创建 Service 层对象和 DAO 层对象,并把它们注入到 Action 里。如果你打开 applicationContext.xml,会看到<context:component-scan>、<bean id="dataSource">、<bean id="sqlSessionFactory">这些配置,它们的职责就是让 Action 类不用自己 new Service,而是从 Spring 容器里拿。

SpringMVC 管的是请求路由。它把/dingdan_add、/dingdan_list这种 URL 映射到 DingdanAction 的对应方法上。虽然这个项目的 Action 命名带点老式风格,但 SpringMVC 的@RequestMapping注解依然有效,只是注解里的值可能写在方法上而不是类上。

MyBatis 管的是 SQL 和 Java 对象的映射。这个项目里用 MyBatis 时,有两条路线可走:一是用 XML 文件写 SQL,然后在 DAO 接口里声明方法;二是直接用注解写 SQL。老课设项目大多选 XML,因为 SQL 和 Java 代码分离之后,调整一条查询语句时不用重新编译 Java,改完 XML 重启 Tomcat 就生效,这在赶工调试时特别实用。

下面用一段伪代码来说明这个项目里一个 Action 类的基本结构,这能帮你理解老式 SSM 项目的方法签名风格:

public class ShangpinAction extends BaseAction { private ShangpinService shangpinService; private String id; private List<Shangpin> list; // Spring 容器通过 setter 注入 Service 对象 public void setShangpinService(ShangpinService shangpinService) { this.shangpinService = shangpinService; } // 对应 /shangpin_list 请求 public String list() { list = shangpinService.findAll(); return "list"; // 返回逻辑视图名,由视图解析器转到对应 JSP } // 对应 /shangpin_add 请求 public String add() { shangpinService.insert(buildShangpinFromParams()); return "success"; } }

这段代码的逻辑说明:Action 类不直接 new Service,而是通过 setter 注入方式从 Spring 容器获取,这是 Spring 的依赖注入在 Java Web 项目里的最常见落地方式。方法返回值是逻辑视图名,SpringMVC 的视图解析器会把list解析到/WEB-INF/pages/shangpin/list.jsp这个物理路径。参数说明:如果项目里用的是注解,入口类上会有@Controller,方法上会有@RequestMapping("shangpin_list"),返回值则是 ModelAndView 或 String 加Model参数,具体取决于项目用的 SpringMVC 版本,3.x 以下版本用 ModelAndView 居多。

2.3 MyEclipse 导入时先认准目录结构

这个资源是用 MyEclipse 开发的,但 MyEclipse 的工程目录和 IDEA 的 Maven 工程目录差别很大。它通常长这样:

PetManager/ ├── src/ # Java 源码根目录 │ ├── com/ │ │ └── pet/ │ │ ├── action/ # Action 层 │ │ ├── service/ # Service 接口 │ │ ├── service/impl/ # Service 实现 │ │ ├── dao/ # DAO 接口 │ │ ├── model/ # 实体类 │ │ └── util/ # 工具类 │ └── applicationContext.xml # Spring 配置文件 ├── WebRoot/ # Web 根目录 │ ├── WEB-INF/ │ │ ├── web.xml # Web 部署描述符 │ │ ├── lib/ # 依赖 jar 包 │ │ └── pages/ # JSP 页面 │ ├── css/ js/ images/ # 静态资源 │ └── index.jsp # 首页 └── .classpath .project # MyEclipse 工程文件

这个结构的重点在WebRoot/WEB-INF/lib——MyEclipse 时代的 Java Web 项目,依赖包全部手放在这个目录里,不像 Maven 从中央仓库拉取。所以导入时最稳妥的做法是直接打开.project文件,让 MyEclipse 按原工程配置加载,否则你手动新建 Dynamic Web Project 后,还要重新配置 Tomcat 运行时,很容易翻车。常见做法是:导入后先确认 Build Path 里的 JRE 版本,再确认 Tomcat 版本和你本机一致,然后右键项目 → Properties → Targeted Runtimes,勾选你的 Tomcat。这一步不做,项目会一直红叉,但不是代码问题。

3. 把数据库表设计出来:宠物、会员、订单和留言的关系

3.1 宠物信息表:课设验收时的门面

宠物信息是这个系统最核心的数据对象,它至少需要一张 pet 表来承载。根据这个系统的功能描述和 Action 类名推断,核心表的构建思路如下:

CREATE TABLE `pet_info` ( `id` INT(11) NOT NULL AUTO_INCREMENT COMMENT '宠物ID', `name` VARCHAR(50) NOT NULL COMMENT '宠物名称', `category` VARCHAR(20) DEFAULT '猫' COMMENT '类别:猫/狗/兔/其他', `variety` VARCHAR(50) DEFAULT NULL COMMENT '品种,如英短、金毛', `age` INT(11) DEFAULT NULL COMMENT '年龄(月)', `color` VARCHAR(20) DEFAULT NULL COMMENT '毛色', `gender` TINYINT(1) DEFAULT '1' COMMENT '性别:1公 0母', `price` DECIMAL(10,2) DEFAULT '0.00' COMMENT '价格', `status` TINYINT(1) DEFAULT '0' COMMENT '状态:0在售 1已售 2预定', `photo` VARCHAR(255) DEFAULT NULL COMMENT '封面图路径', `album_ids` VARCHAR(255) DEFAULT NULL COMMENT '关联相册ID,逗号分隔', `description` TEXT COMMENT '详细描述', `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4;

逻辑说明:album_ids字段用逗号分隔多个相册 ID,是一个偷懒但也够用的做法,省去一张关联表。status字段管理宠物在售状态,这是宠物店业务的关键,也是演示时最容易出效果的地方——把一条记录从「在售」改成「已售」,前端列表刷新就变化。参数说明:price用 DECIMAL(10,2) 而不用 FLOAT,是因为 Java 对浮点金额计算会有精度问题,比如 0.1 加 0.2 不是 0.3,这在 MySQL 里同样存在,所以金额字段一律用 DECIMAL。age用 INT 存月数,也是为了避免「2岁零3个月」这种不好查询的字符串。

3.2 会员表和订单表:一对多的关系

再建两张维护业务主线的表。一张是huiyuan,对应 HuiyuanAction,一张是dingdan,对应 DingdanAction:

CREATE TABLE `huiyuan` ( `id` INT(11) NOT NULL AUTO_INCREMENT, `username` VARCHAR(50) NOT NULL COMMENT '登录名', `password` VARCHAR(64) NOT NULL COMMENT 'MD5后的密码', `real_name` VARCHAR(30) DEFAULT NULL COMMENT '姓名', `phone` VARCHAR(11) DEFAULT NULL COMMENT '手机号', `address` VARCHAR(200) DEFAULT NULL COMMENT '地址', `points` INT(11) DEFAULT '0' COMMENT '积分', `status` TINYINT(1) DEFAULT '0' COMMENT '状态:0正常 1禁用', `reg_time` DATETIME DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `uk_username` (`username`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE `dingdan` ( `id` INT(11) NOT NULL AUTO_INCREMENT, `order_no` VARCHAR(32) NOT NULL COMMENT '订单编号,如DD+时间戳', `huiyuan_id` INT(11) NOT NULL COMMENT '会员ID', `total_price` DECIMAL(10,2) DEFAULT '0.00', `pay_type` TINYINT(1) DEFAULT '0' COMMENT '支付方式', `status` TINYINT(1) DEFAULT '0' COMMENT '状态:0待付款 1已付款 2已发货 3已完成', `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), KEY `idx_huiyuan_id` (`huiyuan_id`), CONSTRAINT `fk_order_member` FOREIGN KEY (`huiyuan_id`) REFERENCES `huiyuan` (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

逻辑说明:这两张表是典型的主外键关联,一个会员可以下多笔订单,订单表里的huiyuan_id外键指向会员表主键。要注意的是,课设里如果不建外键约束,只靠 Java 代码保证数据一致,也是常见做法,但答辩时能说出外键设计,印象分会明显更高。参数说明:订单号order_no字段建议在 Java 端生成,格式用DD加当前时间的毫秒值,例如DD20240615143022123,这样既能避免主键冲突,又能直观看出订单产生时间。如果你想要更精确的查询,把create_time字段加上索引。

3.3 供应商和留言:围绕主业务的两条辅助线

供应商表对应 SupplyorAction 里的 Supplier,它服务的是采购环节:宠物店要进货,就得知道哪个供应商供了什么宠物、什么价格。留言表对应 LeavewordAction,它是网站的访客留言,一般挂在宠物详情页或首页反馈入口。

CREATE TABLE `supplier` ( `id` INT(11) NOT NULL AUTO_INCREMENT, `name` VARCHAR(100) NOT NULL COMMENT '供应商名称', `contact` VARCHAR(30) DEFAULT NULL COMMENT '联系人', `phone` VARCHAR(20) DEFAULT NULL COMMENT '联系电话', `address` VARCHAR(200) DEFAULT NULL, `supply_type` VARCHAR(50) DEFAULT NULL COMMENT '供应宠物种类', `remark` VARCHAR(500) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE `leaveword` ( `id` INT(11) NOT NULL AUTO_INCREMENT, `pet_id` INT(11) DEFAULT NULL COMMENT '关联宠物ID', `username` VARCHAR(50) NOT NULL COMMENT '留言人', `content` VARCHAR(500) NOT NULL COMMENT '留言内容', `reply` VARCHAR(500) DEFAULT NULL COMMENT '管理员回复', `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

参数说明:leaveword.content用 VARCHAR(500),课设里这个长度够用,但是如果你希望能插入大段文字,之后可以改成 TEXT。supplier.supply_type这个字段设计成 VARCHAR 存字符串,查询时如果用 LIKE 匹配,就够用了,不必拆表。这里有个经验:课设阶段的表数量控制在 6 张以内最好演示,多了反而不好圆场,因为每张表都要你解释用途。

建完表之后,下一步是把 MyBatis 的 Mapper 文件写好。一个简单的宠物查询 Mapper 配置长这样:

<mapper namespace="com.pet.dao.PetInfoDao"> <select id="findByCondition" parameterType="map" resultType="com.pet.model.PetInfo"> SELECT * FROM pet_info <where> <if test="category != null and category != ''"> AND category = #{category} </if> <if test="status != null"> AND status = #{status} </if> <if test="keyword != null and keyword != ''"> AND (name LIKE CONCAT('%', #{keyword}, '%') OR variety LIKE CONCAT('%', #{keyword}, '%')) </if> </where> ORDER BY create_time DESC </select> </mapper>

逻辑说明:<where>标签是 MyBatis 动态 SQL 的核心,它会在第一个条件成立时自动拼上 WHERE 关键字,后续条件自动加 AND,这样就不用担心「WHERE 后面直接跟 AND」这种低级错误。参数说明:parameterType="map"表示这个方法接收一个 Map 参数,里面可以放 category、status、keyword 这些可选的查询条件,Controller 层接收到前端传来的参数后,只需放入 Map 传给 DAO 层即可。

4. 用 Layui 搭后台界面:表格渲染和表单交互的对接套路

4.1 表格数据接口的 JSON 格式是硬指标

Layui 的 table 组件对后端数据格式有硬性要求,这个格式不对,哪怕 SQL 查出了数据,表格里依然是空白。核心格式如下:

{ "code": 0, "msg": "", "count": 15, "data": [ { "id": 1, "name": "英短", "category": "猫", "price": 1200.00 }, { "id": 2, "name": "金毛", "category": "狗", "price": 1500.00 } ] }

这里的code必须是 0,count是总记录数,data是当前页的数据数组。如果你的后端 Action 返回的不是这个结构,表格默认加载不出来,而且控制台只有一段很隐晦的 JSON 解析报错。

layui.use(['table', 'layer'], function () { var table = layui.table; table.render({ elem: '#petTable', url: '/pet/list', // 后端接口地址 method: 'post', // 请求方式,注意和后端一致 page: true, // 开启分页 cols: [[ { field: 'id', title: 'ID', width: 60, sort: true }, { field: 'name', title: '宠物名' }, { field: 'category', title: '类别' }, { field: 'price', title: '价格' } ]], parseData: function (res) { // 兜底解析:即使后端返回的字段名不同也能转成 Layui 格式 return { "code": res.code, "msg": res.msg, "count": res.count, "data": res.data }; } }); });

逻辑说明:这段代码在页面初始化时注册表格渲染,elem是表格容器的选择器,url是接口地址,page: true启用了服务端分页。Layui 会自动向服务端传page和limit两个参数,后端需要根据这两个参数做 limit 查询。参数说明:如果项目里的后端没有实现分页,一次性返回所有数据,那么page: false,去掉count也可以工作,但数据量大时页面会卡。parseData是兜底函数,我一般都会写上,因为后端同事改个字段名是常有的事,有这个函数,前端不用跟着改。

4.2 表单提交:弹窗里新增,表格里刷新

后台管理系统的常规操作是「点新增 → 弹窗 → 填表 → 提交 → 刷新列表」,Layui 的 form 模块配合 layer.open 正好能完成这条链路。

layui.use(['form', 'layer', 'table'], function () { var form = layui.form; var layer = layui.layer; var table = layui.table; // 监听表单提交 form.on('submit(petSubmit)', function (data) { var field = data.field; $.ajax({ url: '/pet/save', type: 'post', dataType: 'json', data: field, success: function (res) { if (res.code === 0) { layer.msg('保存成功'); table.reload('petTable'); // 刷新表格 layer.closeAll('page'); // 关闭弹窗 } else { layer.msg('保存失败:' + res.msg); } } }); return false; // 阻止表单默认提交 }); });

逻辑说明:form.on('submit(petSubmit)')监听的是<button lay-submit lay-filter="petSubmit">这类按钮的提交行为。data.field可以一次性拿到表单里所有带name属性的字段值,组装成 POST 请求的 body,省去手动拼接数据结构的麻烦。return false很重要,它阻止了浏览器的默认表单跳转行为,否则页面会刷新,体验很糟糕。参数说明:如果新增和编辑共用同一个弹窗,需要在打开弹窗前把编辑行的数据塞进表单,一般通过form.val('petForm', rowData)方法,这是 Layui 提供的数据回填手法,比一个一个$('#name').val()赋值快得多。

4.3 后端 Action 返回 JSON 的规范写法

既然 Layui 表格要求固定 JSON 结构,后端就必须有统一的数据返回格式。这个项目里每个业务 Action 都有类似的方法:

public class ShangpinAction extends BaseAction { public void list() { int page = Integer.parseInt(request.getParameter("page")); int limit = Integer.parseInt(request.getParameter("limit")); Map<String, Object> params = new HashMap<String, Object>(); params.put("page", (page - 1) * limit); params.put("limit", limit); List<Shangpin> list = shangpinService.queryPage(params); int total = shangpinService.count(params); Map<String, Object> result = new HashMap<String, Object>(); result.put("code", 0); result.put("msg", ""); result.put("count", total); result.put("data", list); ObjectMapper mapper = new ObjectMapper(); try { mapper.writeValue(response.getWriter(), result); } catch (IOException e) { e.printStackTrace(); } } }

逻辑说明:这个方法先把 Layui 传过来的page和limit参数取出来,page是第几页,limit是每页多少条。MyBatis 的分页写法是LIMIT offset, limit,所以这里算出(page - 1) * limit作为偏移量。参数说明:ObjectMapper来自 FasterXML Jackson 库,如果项目里没有引入这个依赖,可以换成Gson,效果一样。这个项目的 .class 清单里没有出现 Service 层类名,很可能 Service 层是公共基类处理,但 Action 里调用的方式大同小异。

5. 避坑与排查:SSM 项目从导入到跑起来的六个高频问题

5.1 MyEclipse 导入项目后 Java 文件全部报红

现象:项目导入后,src 目录下大量 Java 文件显示红叉,javax.servlet.*一系列类无法解析。

原因:三个可能。一是 Tomcat 运行时没有配置到项目里,导致 servlet-api.jar 不在编译路径上;二是 JDK 版本和项目要求的版本不一致,MyEclipse 默认编译级别过低;三是 lib 目录的依赖没有加入 Build Path。

解决:右键项目 → Properties → Targeted Runtimes,勾选本机 Tomcat。然后到 Java Build Path 里把 JRE System Library 改成你本机装的 JDK 1.8,再把 WebRoot/WEB-INF/lib 下的所有 jar 包全选,添加到 Build Path。最后 Project → Clean 重新编译一遍。这个流程我每次导入课设项目都会强制走一遍,顺序不能反。

5.2 启动 Tomcat 报数据库连接失败,控制台出现 Access denied 或 Communications link failure

现象:页面打开后第一个列表接口报 500,控制台里 MySQL 异常,有时报「Access denied for user」,有时报「Communications link failure」。

原因:「Access denied」是用户名或密码错误,或者 MySQL 用户没有远程访问权限;「Communications link failure」则是连接串写错了,最常见的是 MySQL 8.x 时区问题和驱动版本过旧。现在的 MySQL 8 默认用com.mysql.cj.jdbc.Driver,连接串必须带serverTimezone=Asia/Shanghai,课设项目里写的是老驱动com.mysql.jdbc.Driver又不带时区参数,必然报错。

解决:打开applicationContext.xml或db.properties,把驱动换成com.mysql.cj.jdbc.Driver,URL 改成:

jdbc:mysql://localhost:3306/petdb?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&useSSL=false&allowPublicKeyRetrieval=true

参数说明:useSSL=false是因为本地开发不需要 SSL 加密,allowPublicKeyRetrieval=true是 MySQL 8 连接时的一个特殊要求,不加会报 Public Key Retrieval is not allowed,这是血泪经验,漏一个都连不上。

5.3 Layui 表格打开后一片空白,控制台报 Cannot read property 'length' of undefined

现象:页面不报 500,后端接口直接访问也能看到 JSON 数据,但 Layui 表格就是不渲染。

原因:JSON 返回的data字段不是数组,或者接口响应类型不是 application/json。有的项目里,Action 直接通过response.getWriter().write(jsonString)输出,而 response 的 Content-Type 没有设置,浏览器收到的是 text/html,Layui 内部解析失败。

解决:在后端输出之前强制设置响应头:

response.setContentType("application/json;charset=utf-8"); response.setCharacterEncoding("utf-8");

另外在 Layui 里加上parseData兜底函数,把后端字段兼容一下。这个问题在课设里尤其常见,因为大家很容易照着网上的例子抄,抄的时候没抄 content-type 设置。

5.4 保存中文出现问号「???」或乱码

现象:从表单提交到数据库的中文变成问号,页面上显示乱码。

原因:口径不一致的三处编码没有对齐。JSP 页面是 UTF-8,但 MySQL 连接串没有characterEncoding=utf8,数据库表字符集是 latin1,或者web.xml里没配置 CharacterEncodingFilter。三个环节只要有一个不对,中文就挂。

解决:第一,确认每条 SQL 建表语句里ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;第二,连接串必须带characterEncoding=utf8;第三,确认web.xml里有这段配置:

<filter> <filter-name>characterEncodingFilter</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> <init-param> <param-name>forceEncoding</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>characterEncodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>

forceEncoding设为 true 很关键,它会把 request 和 response 的编码都强制设置成 UTF-8,而不是只在 request 有编码时才生效。这个过滤器配置在你接手课设项目时,如果遇到任何中文乱码,优先检查它。

5.5 登录后跳转首页,但静态资源 CSS/JS 全部 404

现象:页面能打开,但页面是全裸的,CSS 和 JS 全部加载不到,控制台一堆 404。

原因:SpringMVC 的前端控制器DispatcherServlet拦截了/所有请求,包括.css、.js这些静态资源文件,导致静态文件没有走默认的 Servlet 处理。老的 Tomcat 配置里还没加默认 servlet 映射。

解决:在 SpringMVC 配置文件里加静态资源放行规则:

<mvc:default-servlet-handler/> <mvc:annotation-driven/>

逻辑说明:default-servlet-handler会把请求转交给 Tomcat 默认的 DefaultServlet 处理,这样.js、.css就能正常加载,同时annotation-driven保证@RequestMapping的注解驱动还能正常生效,两者搭配,后端接口和静态资源各走各的路。如果项目用了web.xml里旧配置,还要检查DispatcherServlet的 url-pattern 是不是写的/而不是*.do,如果是后者,其实不会拦截静态资源,问题反而在别处。

5.6 分页点第二页时报错 limit 参数为空

现象:第一页能显示数据,点下一页时后端报 NullPointerException 或 SQL 语法错误。

原因:Layui 的table.render开启了分页,但后端在接收page和limit时可能没有做空值处理。第一页加载时 Layui 会传 page=1、limit=10,但某些网络环境下二次请求可能参数名变了,比如page变成了pages,导致取值失败。

解决:后端强制做默认值兜底:

int page = 1; int limit = 10; String pageStr = request.getParameter("page"); String limitStr = request.getParameter("limit"); if (pageStr != null && !pageStr.isEmpty()) { page = Integer.parseInt(pageStr); } if (limitStr != null && !limitStr.isEmpty()) { limit = Integer.parseInt(limitStr); }

这是最基础的防御式编程,课设里写了这层判断,答辩时能少挨很多问。

6. 把课设做成能现场演示的系统:初始化数据与验收演示路径

6.1 初始化数据的最优顺序

拿到源码后,别急着往里填一堆假数据。先按依赖顺序初始化,否则外键关联会乱套。我一般按这个顺序:

第一,建立数据库和表结构,执行 3.1 到 3.3 里那些建表语句。先建pet_info、huiyuan,再建dingdan,最后建supplier和leaveword,因为订单依赖会员。第二,插入后台管理用户,也就是users表的管理员账号,这是登录系统的入口。第三,插入供应商和宠物数据,供应商最好插 3 到 5 家,宠物插 10 到 20 条,种类涵盖猫、狗、兔。第四,插入会员和订单数据,会员 3 到 5 个,每个会员下 1 到 2 笔订单,注意订单状态要覆盖待付款、已付款、已完成。第五,插入留言数据,数量随意,但至少要有 2 条带管理员的回复,这样演示回复功能时有东西可看。

这种初始化顺序到现在我还在用,它避免了你删按主键顺序插入时而产生的关联报错。

6.2 演示时走一遍主线流程,别跳步骤

课程设计现场演示时最怕手感生疏,被老师一打断就不知道点哪里。我的习惯是按这条路径走:先拿管理员账号登录后台,打开宠物列表,展示分页和按类别搜索,点新增一条宠物,上传一张图片,保存后回到列表刷新,展示刚新增的数据出现在第一行。然后切换到一个会员账号,模拟下单流程:从宠物列表里挑一只在售宠物,加入购物车或直接提交订单,在订单列表里看到这单,状态为待付款,然后模拟付款,把状态改为已付款。最后回到供应商页面,展示供应商列表,点新增供应商,保存成功。这条路径覆盖了商品、会员、订单、留言四个主要模块,供应商和相册作为辅助,用时控制在五分钟以内。

6.3 把调试输出当成评审加分项

演示过程里还有一个小技巧:在关键 Action 方法上加上一段日志输出,用 Log4j 打印关键参数。比如保存宠物时打印入库的宠物名、价格、状态,下单时打印订单号、会员 ID、金额。这有几个作用:一是演示过程中一旦报错,你能马上看到日志定位问题,不至于当场翻车;二是老师看见你代码里有日志埋点,会认为你有生产环境开发的习惯,这种细节往往是打分时的隐形加分项。

// 日志示例:保存订单时输出关键业务数据 log.info("创建订单 - 订单号: {}, 会员ID: {}, 金额: {}", orderNo, memberId, totalPrice);

这个习惯是我当年做课设翻车之后养成的。那次演示时新增宠物一直保存失败,因为参数里混进了一个 null,但页面只给了个笼统的提示,我翻了几分钟才找到问题。从那以后,我每次做这种 SSM 课设项目,都会先在所有 Action 的 insert/update 方法入口加一行日志,这行日志用不上最好,一旦用上,它就是你的后悔药。希望这个习惯也能帮到你。

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询