ARTICLE DETAIL

资讯详情

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

SSM古诗词平台毕业设计:从建库建表到核心代码避坑指南

SSM古诗词平台毕业设计:从建库建表到核心代码避坑指南 简介面向Java毕业设计及课程设计场景这套古诗词数字化平台源码基于SSM框架SpringSpringMVCMyBatis与MySQL数据库开发前台与后台功能均已实现包含主页、个人中心、用户管理、诗词信息管理、分类管理、诗人信息管理、个人分享管理和系统管理等模块支持诗词录入、编辑分类、用户分享、管理员维护等操作适合需要完整毕业设计项目参考的开发者学习。压缩包共1288个文件约22.59MB以Java源码、JSP页面、JavaScript脚本、CSS样式、PNG图片及XML配置为主同时包含项目说明文档和LW论文相关文件目录结构清晰可导入Eclipse或IDEA配合Tomcat运行。目前已有72人学习浏览。这份源码除可直接运行外还附带详细文档能帮助读者从头理解SSM项目的环境搭建、数据库脚本设计、后端接口编写和前端页面交互通过阅读真实完整的业务模块也能积累用户权限管理、诗词分类检索、个人分享等常见功能的编码经验是一份兼具实用性与教学价值的毕设参考资源。1. 古诗词数字化平台是什么一个 SSM 毕业设计能覆盖哪些真实需求马上要交毕业设计了老师指了个方向“古诗词数字化平台”技术栈限定 ssm mysql附带说明文档和 LW毕业设计论文部分。这个题目听起来偏文科本质却是一个标准的信息管理类 Web 应用把散落的古诗词结构化支撑用户浏览、按作者朝代检索、查看详情管理员后台维护数据。做它的最大价值不在“诗词”选题本身而在于把 JavaWeb 核心知识点——Spring 容器、SpringMVC 路由、MyBatis 数据访问、MySQL 表设计——串成一条完整链路。这篇按我的实操方式写从功能拆解、建库建表、环境跑通到核心代码和踩坑你可以跟着复现并在答辩前真正把项目跑起来。2. 从功能清单到数据库设计先搞清楚这个平台要存什么、查什么很多同学拿到源码先急着 IDE 里 Run结果被各种缺表、缺字段的异常打断。我的习惯是反过来先读说明文档里的需求分析把角色、页面、数据关系画一遍再动数据库。数据库设计决定了这个 SSM 项目后面所有代码的写法表没设计好Service 层一定写得拧巴。2.1 需求拆解用户、管理员和检索三条主线这个平台按角色能分成两条操作线外加一条所有访客都在用的检索线。普通用户线注册、登录、浏览诗词列表、按标题/作者/朝代搜索、进入详情页收藏诗词、在个人中心看到收藏列表。管理员线登录后台、维护诗词增删改、维护作者信息、管理公告或用户状态。检索线最核心它直接决定 MyBatis 动态 SQL 怎么写。技术上的权限不用做 Spring Security一张表的 role 字段就够。角色在 Java 代码里体现为拦截器判断session 里的 user.role 等于 1 才放行 /admin 开头的路径。这样既符合课程设计“能演示权限控制”的要求又不会把复杂度抬到答辩讲不清的地步。2.2 四张核心表作者、诗词、用户、收藏的关系我用四张表支撑全部功能。作者表和诗词表是一对多用户表和诗词表通过收藏表做多对多。以下是字段设计时容易出问题的地方直接给出我的最终版本。表名职责关键字段t_user登录与角色区分id, username, password, role, create_timet_author作者档案id, name, dynasty, introductiont_poem诗词正文与元数据id, title, content, author_id, dynasty, type, image, favorite_count, status, create_timet_favorite用户收藏关系id, user_id, poem_id, create_timet_poem 里我单独存dynasty而不是通过作者表关联朝代因为同一作者的作品可能跨朝代整理而且检索时“按朝代筛选”是最高频的查询条件冗余一个字段可以减少一次 JOIN。favorite_count是冗余计数每次收藏加一避免统计收藏数时去 favorite 表 COUNT这在 MyBatis 里少写一段嵌套查询。status字段用来做上下架默认值为 0显示管理员可以置为 1 下架。这个字段在答辩时很有用能解释“为什么列表页不直接 select *”。image存的是相对路径而不是 Base64数据库只存/upload/xxx.jpg图片文件落在服务器磁盘这样表体积不会爆炸。2.3 建库建表 SQL可以直接改名的初始化脚本拿到源码后第一件事就是执行这个脚本然后再考虑改密码。我一般先建库再建表每张表的字符集都单独指定utf8mb4防止 MySQL 默认的utf8mb3在存生僻字时翻车。CREATE DATABASE IF NOT EXISTS poetry_db DEFAULT CHARACTER SET utf8mb4; USE poetry_db; DROP TABLE IF EXISTS t_user; CREATE TABLE t_user ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE COMMENT 登录名, password VARCHAR(64) NOT NULL COMMENT MD5哈希后的密文, nickname VARCHAR(50) DEFAULT COMMENT 昵称, role TINYINT NOT NULL DEFAULT 0 COMMENT 0-普通用户 1-管理员, create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT 注册时间 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT用户表; DROP TABLE IF EXISTS t_author; CREATE TABLE t_author ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(50) NOT NULL COMMENT 作者名, dynasty VARCHAR(20) DEFAULT COMMENT 朝代, introduction TEXT COMMENT 作者简介 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT作者表; DROP TABLE IF EXISTS t_poem; CREATE TABLE t_poem ( id INT PRIMARY KEY AUTO_INCREMENT, title VARCHAR(100) NOT NULL COMMENT 诗词标题, content TEXT NOT NULL COMMENT 诗词正文, author_id INT NOT NULL COMMENT 关联作者表, dynasty VARCHAR(20) DEFAULT COMMENT 朝代冗余存储, type VARCHAR(20) DEFAULT COMMENT 诗/词/曲, image VARCHAR(200) DEFAULT COMMENT 配图路径, favorite_count INT DEFAULT 0 COMMENT 收藏数, status TINYINT DEFAULT 0 COMMENT 0-显示 1-下架, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, KEY idx_author_id (author_id), KEY idx_dynasty (dynasty) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT诗词表; DROP TABLE IF EXISTS t_favorite; CREATE TABLE t_favorite ( id INT PRIMARY KEY AUTO_INCREMENT, user_id INT NOT NULL COMMENT 用户ID, poem_id INT NOT NULL COMMENT 诗词ID, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, UNIQUE KEY uk_user_poem (user_id, poem_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT收藏表;password字段用 VARCHAR(64) 是给 MD5 密文留的位如果你用 BCrypt得改 60这个看项目里工具类怎么写的。uk_user_poem唯一索引是收藏表的关键靠它才能用INSERT IGNORE避免重复收藏也方便checkExists查询走索引。t_favorite没有设置外键约束这是 MyBatis 项目的常见做法逻辑外键删除诗词时手动 delete 收藏记录避免物理外键带来的增删锁开销。3. 把 SSM 骨架跑起来环境、版本与三份核心配置拿到带说明文档的源码包最怕的是环境不匹配导致启动即失败。SSM 这个技术栈已经稳定很多年了版本组合其实非常固定照下面这个组合来基本不会遇到兼容性地狱。3.1 版本组合JDK 1.8 Tomcat 8.5 MySQL 5.7 最稳组件建议版本原因JDK1.8绝大多数毕业设计源码基于 JDK 1.8 编译高版本会遇到反射、cglib 代理警告Tomcat8.5兼容 JDK 1.8 且默认支持 JSTL、EL 表达式MySQL5.7驱动用 com.mysql.jdbc.Driver避免 8.0 的时区、SSL 认证问题Maven3.6.x依赖下载稳定阿里云镜像可正常拉取 spring 与 mybatis 依赖如果你本机只装了 MySQL 8.0也不是不能跑但要注意driver 要换com.mysql.cj.jdbc.DriverURL 里要加serverTimezoneAsia/Shanghai和allowPublicKeyRetrievaltrue。这个细节我放到最后一章避坑里说因为 90% 的“连接失败”都是版本错配引起的。3.2 jdbc.properties三行参数决定数据库能不能连上源码包里的配置文件位置通常在src/main/resources下。先把jdbc.properties改成你自己数据库的账号密码jdbc.drivercom.mysql.jdbc.Driver jdbc.urljdbc:mysql://localhost:3306/poetry_db?useUnicodetruecharacterEncodingutf8useSSLfalse jdbc.usernameroot jdbc.passwordyourpasswordcharacterEncodingutf8必须和表的 utf8mb4 对齐少这一参数页面插入的中文会全部变问号。useSSLfalse是为了避免 MySQL 5.7 默认的 SSL 握手在本地环境报Communications link failure。密码字段不要出现特殊字符如果一定有建议在 MySQL 里临时改一个简单密码跑通后再改回去。spring-mybatis.xml负责把数据源、SqlSessionFactory、Mapper 扫描串起来重点看三个 propertybean iddataSource classorg.springframework.jdbc.datasource.DriverManagerDataSource property namedriverClassName value${jdbc.driver}/ property nameurl value${jdbc.url}/ property nameusername value${jdbc.username}/ property namepassword value${jdbc.password}/ /bean bean idsqlSessionFactory classorg.mybatis.spring.SqlSessionFactoryBean property namedataSource refdataSource/ property nametypeAliasesPackage valuecom.poetry.entity/ property namemapperLocations valueclasspath:mapper/*.xml/ /bean bean classorg.mybatis.spring.mapper.MapperScannerConfigurer property namebasePackage valuecom.poetry.dao/ /beantypeAliasesPackage配了之后PoemMapper.xml 里的resultType可以直接写Poem而不是全限定类名少写很多冗余。mapperLocations指定 XML 位置如果这里漏配或路径写错启动时不会有明显报错但调用 Mapper 方法会抛BindingException这是最常见的黑匣子问题。3.3 spring-mvc.xml静态资源放行和注解驱动SSM 项目的 Controller 层由 spring-mvc.xml 管理。里面有一个特别容易翻车的地方静态资源被 DispatcherServlet 拦截。mvc:annotation-driven/ context:component-scan base-packagecom.poetry.controller/ mvc:resources mapping/static/** location/static// mvc:resources mapping/upload/** location/upload// bean idmultipartResolver classorg.springframework.web.multipart.commons.CommonsMultipartResolver property namemaxUploadSize value5242880/ property namedefaultEncoding valueUTF-8/ /beanmvc:resources放行静态资源特别是/upload/**否则上传的诗词配图在浏览器里永远 404。multipartResolver的 bean id 必须叫multipartResolverSpring 在识别文件上传时按 id 查找改名就失效。3.4 启动验证从 Tomcat 日志里找真正的失败原因配置改完后用 IDEA 配置 TomcatArtifact 选 war exploded启动。这一步我的经验是不要只在 IDEA 控制台看红色报错要拉到最后一行SSM 的报错信息往往被一堆 Spring 日志刷掉了。最典型的几个症状ClassNotFoundException: org.springframework.web.context.ContextLoaderListener—— 依赖没有打包进 war检查 Artifact 的 lib。Invalid bound statement (not found)—— mapper XML 路径没扫到检查 mapperLocations。Table poetry_db.t_poem doesnt exist—— 建表脚本没执行或执行到了别的库。这三个问题各就各位启动日志出现Started Tomcat或Spring容器初始化完成之后再用浏览器访问首页才算真正跑通。别急着研究业务代码先让页面能开后续才谈得上改功能。4. 核心代码这样写检索、分页、上传和事务跑通骨架之后真正的编码集中在四个地方多条件检索的 SQL、列表分页、图片上传、收藏的事务操作。这四块也正好是面试官常问的 SSM 知识点代码质量直接决定答辩时的自信程度。4.1 MyBatis 动态 SQL一个方法吃掉多条件检索用户在前端勾选朝代、输入关键词后端要能组合出不同 SQL。MyBatis 的whereif是标准答案不要用拼接字符串的方式既危险又难看。看一下 PoemMapper.xml 里的写法select idselectByCondition resultTypePoem parameterTypemap SELECT p.id, p.title, p.content, p.dynasty, p.type, a.name AS authorName, p.favorite_count FROM t_poem p LEFT JOIN t_author a ON p.author_id a.id where if testkeyword ! null and keyword ! AND (p.title LIKE CONCAT(%, #{keyword}, %) OR p.content LIKE CONCAT(%, #{keyword}, %)) /if if testdynasty ! null and dynasty ! AND p.dynasty #{dynasty} /if if testauthorId ! null AND p.author_id #{authorId} /if AND p.status 0 /where ORDER BY choose when testsort hot p.favorite_count DESC /when otherwise p.create_time DESC /otherwise /choose /selectwhere标签会自动去掉第一个if前面多出来的AND这是 MyBatis 的隐藏福利。LIKE 查询要用CONCAT(%, #{keyword}, %)不要直接写%${keyword}%后者存在注入风险。ORDER BY里用了choose做排序切换这是一个很加分的细节答辩时能解释清楚“为什么排序条件不用 if”。4.2 PageHelper 分页两个参数和一个 PageInfo分页是后台管理页面的刚需。项目里一般已经引入 PageHelperService 层调用分页时注意它和普通查询的配合逻辑Override public PageInfoPoem queryPage(PoemQuery query) { // 1. 启动分页只对紧接着的一条查询生效 PageHelper.startPage(query.getPageNum(), query.getPageSize()); // 2. 调用 Mapper 查询 ListPoem list poemMapper.selectByCondition(query); // 3. 封装成分页信息对象 return new PageInfo(list); }startPage是静态方法它的作用域只有下一条 SQL所以不要在前面加任何别的数据库操作。pageNum从 1 开始pageSize默认给 10这两个参数我一般从前端传入前端再提供上一页、下一页按钮。PageInfo里包含大量现成的字段比如getTotal()总条数、getPages()总页数、getList()当前页数据JSP 页面直接遍历省去自己写 COUNT 语句。4.3 收藏功能为什么 Service 层要加事务收藏涉及两张表往 t_favorite 插入记录同时更新 t_poem 的 favorite_count。这两步必须同时成功或同时失败因此需要事务。Spring 的声明式事务只需要一个注解Service public class FavoriteServiceImpl implements FavoriteService { Autowired private FavoriteMapper favoriteMapper; Autowired private PoemMapper poemMapper; Transactional(rollbackFor Exception.class) public boolean addFavorite(Integer userId, Integer poemId) { int exists favoriteMapper.checkExists(userId, poemId); if (exists 0) { return false; } favoriteMapper.insert(userId, poemId); poemMapper.increaseFavoriteCount(poemId); return true; } }Transactional默认只回滚 RuntimeException加上rollbackFor Exception.class后遇到受检异常也会回滚。这里要提醒一句事务注解只有在 Controller 调用 Service 方法且经过 Spring 代理时才会生效。如果你在同一个类的内部方法间调用事务会失效这是面试爱挖坑的地方。checkExists利用了 t_favorite 的唯一索引查询效率很高也避免了先查再插的并发窗口。4.4 图片上传UUID 文件名与目录创建管理员添加诗词时会上传一张配图。上传处理的代码不复杂但有两个细节决定成败文件名不能是用户提交的原始名上传目录必须先创建。PostMapping(/admin/poem/save) public String save(Poem poem, RequestParam(imageFile) MultipartFile imageFile, HttpServletRequest request) throws IOException { if (!imageFile.isEmpty()) { String uploadDir request.getServletContext().getRealPath(/upload); File dir new File(uploadDir); if (!dir.exists()) { dir.mkdirs(); } String original imageFile.getOriginalFilename(); String ext original.substring(original.lastIndexOf(.)); String fileName UUID.randomUUID().toString().replace(-, ) ext; imageFile.transferTo(new File(dir, fileName)); poem.setImage(/upload/ fileName); } poemService.savePoem(poem); return redirect:/admin/poem/list; }UUID.randomUUID()生成 32 位无横线字符串避免中文文件名和特殊字符导致访问失败。getRealPath(/upload)拿到的目录在 Tomcat 下是实际部署路径但用 IDEA 的 war exploded 模式时它指向 out 目录重启可能被清理这个问题我放在第五章展开。transferTo是 Spring 封装好的方法底层处理了临时文件复制比手动FileOutputStream可靠得多。5. 避坑SSM 毕业设计最常见的 5 个翻车现场SSM 项目跑不起来的原因高度集中我把这些年看过的错误归结成五类每类按现象、原因、解决的顺序写。对照排查大多数问题能在十分钟内定位。5.1 本地能跑部署到 Tomcat 后首页 404现象IDEA 里启动 Tomcat 访问首页一切正常但把 war 包手动丢到 Tomcat webapps 后浏览器打不开或者打开的页面样式全丢。原因war 包部署后访问路径要带上下文名。比如 war 叫poetry.war访问地址必须是http://localhost:8080/poetry/而不是http://localhost:8080/。另外如果 web.xml 里 DispatcherServlet 的 url-pattern 写成了/*所有静态资源请求都会被拦截。解决先确认 url-pattern 是/而不是/*再用http://localhost:8080/项目名/访问。样式丢失的话在 JSP 里用${pageContext.request.contextPath}拼静态资源路径不要用相对路径。5.2 启动报 Access denied for user rootlocalhost现象Tomcat 启动日志出现java.sql.SQLException: Access denied for user rootlocalhost (using password: YES)但命令行 mysql -u root -p 能进。原因jdbc.properties 里的密码和 MySQL 实际密码不一致或者是改了密码后没刷新权限。还有一种情况是 MySQL 8.0 的 caching_sha2_password 认证插件和旧驱动不兼容也会报这个错。解决先用命令行确认密码再检查 jdbc.password。如果是 MySQL 8.0换驱动为com.mysql.cj.jdbc.Driver并加上allowPublicKeyRetrievaltrue。这里血泪经验是不要凭印象直接在命令行SELECT user, host, plugin FROM mysql.user;看一眼认证方式。5.3 页面新增诗词后中文全是问号现象管理员录入“春江花月夜”列表页显示“????”。英文和数字正常。原因三处编码至少有一处不对——JSP 页面没设 UTF-8、数据库表是 latin1、JDBC URL 少了 characterEncoding。最常见的是只改了其中一处。解决把 JSP 顶部加上pageEncodingUTF-8表切换成 utf8mb4URL 带上characterEncodingutf8。切换表字符集用ALTER TABLE t_poem CONVERT TO CHARACTER SET utf8mb4;不用重建表。如果已经插入了脏数据先删掉再测。5.4 JSP 页面上的 ${} 原样输出现象页面源码里能看到{poem.title}但浏览器渲染出来还是这串字符没有显示诗词标题。原因JSP 的 EL 表达式被关闭了。通常是 web.xml 的版本声明太低Servlet 2.4 以前默认不启用 EL也可能是page 指令里写了isELIgnoredtrue。解决在 JSP 顶部加% page isELIgnoredfalse %同时把 web.xml 的 schema 版本改为 3.1 或更高比如http://xmlns.jcp.org/xml/ns/javaee。改完重启 Tomcat这个问题基本就清了。5.5 上传的图片在浏览器里 404现象上传成功数据库里 image 字段有值文件也确实存在磁盘上但浏览器访问/upload/xxx.jpg报 404。原因上传目录用的是getRealPath在 IDEA 部署模式下这是 out 目录下的临时路径重启后被 Tomcat 清理。另外 spring-mvc.xml 里没有放行/upload/**也会导致 404。解决在 spring-mvc.xml 里加入mvc:resources mapping/upload/** location/upload//并把上传目录改成项目外部的固定路径比如D:/poetry_upload/通过配置访问映射。如果项目要求必须放 webapp 内最好用 war 方式部署而不是 exploded。6. 让这个平台值得写进简历链路验收、扩展与答辩准备项目跑通只是第一步答辩时能被问住的不是“这个功能怎么写的”而是“这个功能是怎么验证的”“数据哪来的”“能不能扩展”。我习惯在答辩前按照下面这组清单整体过一遍既检查功能完整性也算给自己做一轮回归测试。6.1 核心链路验收清单步骤操作预期结果1注册新用户用普通身份登录可以登录后台菜单不可见2用管理员账号登录进诗词管理新增一首诗保存后列表出现配图正常显示3前台按朝代“唐”筛选只返回朝代唐的诗词4点击收藏再进个人中心收藏列表有记录返回详情页收藏按钮状态更新5管理员下架该诗词前台列表不再展示该诗词这个清单覆盖了角色权限、CRUD、检索、收藏、状态管理五条核心链路任何一步失败都说明对应的 Controller 或 Mapper 有问题答辩前必须修完。6.2 三个低成本扩展方向全文检索、点赞、作者聚类如果想让项目多一点亮点不用换架构只在现在基础上有三个低成本方向。把 LIKE 查询换成一列全文索引MySQL 5.7 支持中文全文检索用MATCH...AGAINST替代CONCAT(%,...)。在 t_user 加favorite_count冗余之外再给收藏表加一条积分逻辑。还可以按朝代统计诗词数量做一个“唐诗三百首数据柱状图”用 ECharts 读一个 JSON 接口数据量和视觉冲击力都够。6.3 答辩准备看熟数据字典和测试记录说明文档里一般会带数据字典和 ER 图这一部分是答辩问得最密集的。把 t_poem 表每个字段的设计原因背下来尤其是dynasty冗余存储和favorite_count冗余计数面试官和答辩老师都喜欢问“你为什么要冗余这个字段”。准备一份测试记录写清每个接口的输入输出比临时翻代码更从容。我当年就是吃了没做验收记录的亏答辩被问到“收藏失败的边界情况”时愣住现在回头看把 checkExists 返回 false 的场景跑一遍也就五分钟。这个项目不求功能多但每一步都要能讲清楚选择和取舍数据库加了唯一索引、事务回滚边界、上传文件名防重这些细节比页面数量更提分。希望帮到你。本文还有配套的精品资源点击获取
返回列表