尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Spring Boot个人博客系统实战:从数据库设计到部署上线

Spring Boot个人博客系统实战:从数据库设计到部署上线 简介这是一套基于Spring Boot开发的完整个人博客系统源码面向Java Web初学者与Spring生态实践者旨在帮助开发者掌握前后端分离之外的传统服务端渲染架构落地能力。资源包含全部源代码、MySQL建表SQL、Redis缓存配置及配套静态资源涵盖用户认证、文章CRUD、评论交互与数据看板等典型业务模块适合作为课程设计、毕业项目或技术栈整合学习范例。压缩包共598个文件主体为29个核心Java类含Controller、Service、Entity、70个JS脚本、58个CSS样式、30个HTML模板及119个MyBatis映射XML文件辅以图片、字体与配置文件整体大小92.43MB。已有5042人学习下载提供开箱即用的IDEA工程结构、ThymeleafjQuery前端渲染方案、Spring Security权限控制及Redis缓存集成实现便于快速部署、调试与二次开发。 做个人博客系统这种项目的人十有八九都是为了毕业设计、课程作业或者给简历上凑一个完整项目。但说实话市面上打着“Spring Boot个人博客系统源码数据库”旗号的东西一大堆真正下载下来能一次跑通、代码结构又清晰的真没几个。大部分不是缺依赖就是SQL脚本不全再不济就是前端页面丑得没法看。我这次整理这套博客系统不打算整那些花里胡哨的东西就老老实实把一套能用、能扩展、能写进简历的完整工程拆开讲清楚——从数据库设计、后端接口到页面渲染、部署发布每一步怎么来的、为什么这么做都说明白。这套系统给我的感觉就是典型的“麻雀虽小五脏俱全”项目文章发布、分类管理、标签聚合、评论互动、后台登录、站点配置都齐了。它适合什么人看特别适合两种人一是准备用Spring Boot做毕设但不知道从哪下手的同学二是学过SSM或者Spring Boot基础、想找个完整项目练手沉淀的初学者。如果你已经能独立写CRUD、懂基本的前后端交互逻辑那这篇文章能帮你把项目从“会写功能”拉到“能做工程”的层次。要是零基础也不用慌我会把环境准备和手写SQL脚本的细节都交代清楚照着一路敲下来项目一定能跑。1. 动手前先想清楚一个博客系统到底要做什么很多同学拿到“做一个博客系统”这种需求第一反应就是打开IDEA直接建工程、写实体类。十有八九写到一半发现功能越加越多或者表结构改来改去最后代码全乱了。这种情况我见过太多所以先把需求边界和模块划分定下来再谈代码。1.1 功能清单别做“大而全”做“够用且完整”个人博客系统本质上就是一个典型的CMS内容管理系统。按照我的经验一个能应付毕设答辩、又能体现技术含量的博客系统核心功能就这几个文章模块文章的发布、编辑、删除、详情展示、列表分页这是整个系统的地基。分类与标签文章支持归属一个分类、打多个标签用来做内容归档和聚合查询。评论模块访客可以对文章发表评论后台可以对评论进行管理删除、审核。后台管理管理员登录后进入后台维护文章、分类、标签、评论、站点基础设置。站点配置博客名称、博主简介、首页公告、友情链接等一般放在一张配置表里统一管理。至于消息通知、站内信、多用户写作、复杂权限体系、第三方登录这些一律不做。原因很简单个人博客的场景就是“一个人写一堆人看”整那么多附加功能只会把核心业务淹没掉。而且从毕设评审的角度看你把文章、分类、标签、评论这几条链路做扎实了比堆十来个半吊子功能强十倍。1.2 用户角色与操作路径以“后台管理”为圆心设计这套系统的用户角色只有两个管理员和访客。访客能做的事情非常有限——看文章、看分类、看标签、发评论管理员能做的事情围绕“内容管理”展开——登录后台、写文章、管分类标签、删违规评论、改站点信息。设计操作路径的时候我习惯从页面反推接口。比如后台要有一个文章列表页那后端必须提供“分页查询文章”的接口列表页上要有“编辑”按钮那后端必须提供“按ID查文章详情”的接口编辑页要能保存那后端必须提供“新增/更新文章”的接口。这种“页面推接口”的方式比对着数据库表空想接口要高效得多也直接决定了你的Controller层会有哪些方法。2. 数据库先行8张表撑起整个博客系统“源码数据库”这种交付形式数据库脚本就是灵魂。我见过太多的项目源码分享代码是一套的SQL脚本又是另一套的导入数据库就直接报错。所以这套系统里我把SQL脚本命名为schema_and_data.sql里面同时包含建表语句和初始化数据保证拿到手就能跑。2.1 核心表结构设计冗余字段用在对的地方整个博客系统我设计了8张表别看数量不多表与表之间的关系是够用的表名作用关键字段t_user管理员用户表id, username, password, nickname, avatar, create_timet_article文章表id, title, summary, content, category_id, cover_image, status, view_count, create_time, update_timet_category分类表id, name, description, create_timet_tag标签表id, name, create_timet_article_tag文章-标签关联表id, article_id, tag_idt_comment评论表id, article_id, nickname, email, content, parent_id, status, create_timet_friend_link友链表id, site_name, site_url, description, create_timet_site_config站点配置表id, config_key, config_value, description这里有个很重要的设计细节就是文章的category_id字段。按三范式来说文章表里存category_id就是外键引用但实际上为了查询方便我额外在文章表里冗余了一个category_name字段。为什么这么做因为在首页文章列表页大概率要展示每篇文章属于哪个分类如果每次都要去t_category表查一遍名称一个页面十条文章就要多出十条关联查询。虽然MyBatis的关联查询可以做但让SQL简单一点、让代码少一点才是这种规模项目最该追求的。2.2 评论表设计用parent_id实现楼中楼评论表里最容易忽视的字段是parent_id。这个字段的作用是记录“这条评论回复的是哪条评论”如果不为空就说明这是一条子评论。其实没有parent_id也能做评论功能就是所有评论平铺一层没法搞楼中楼。但是我强烈建议加上因为加这个字段的成本很低却能让评论模块显得完整很多在毕业设计答辩时也是一个可以讲半天的设计亮点。2.3 初始化数据与SQL脚本的导入细节SQL脚本里除了建表语句还需要包含一条管理员账号的INSERT语句和几个测试分类、几篇示例文章。注意一点管理员密码不要用明文直接用BCrypt加密后的字符串写进去。Spring Security自带的BCryptPasswordEncoder能直接校验密码是admin123加密串形如$2a$10$...。这样既方便你本地登录测试又显得你懂密码安全的基本操作。导入的时候有一个容易踩的坑如果你的MySQL版本是8.0以上建议在连接串里加上?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai否则导入带有中文的SQL脚本可能出现乱码或者遇到Public Key Retrieval is not allowed的错误。命令行导入时用source xxx.sql或者mysql -uroot -p schema_and_data.sql都可以但务必确认当前数据库的默认字符集是utf8mb4。3. 技术选型与工程骨架别一股脑用最新版本关于Spring Boot的版本我看到热词里有“spring boot 2.1”“spring boot 2.6”“spring boot 4.x”这些我直接给结论这套博客项目用Spring Boot 2.7.x是最稳的。原因有两个第一是2.7.x是2.x系列的最后一个大版本文档和生态都极其成熟你遇到的问题搜索基本都有答案第二是很多学校机房或者老一点的教程还停留在2.x用3.x反而容易因为JDK版本、Jakarta命名空间等问题卡住。JDK选1.8或者11都行如果是3.x就必须17没必要给自己制造麻烦。3.1 持久层选型为什么我推荐MyBatis Plus持久层这块Spring Boot官方推荐的是Spring Data JPA但是国内项目用MyBatis系的更多毕设也大多是MyBatis或者MyBatis Plus。MyBatis Plus对个人项目来说太友好了内置的单表CRUD方法让你不用写基础SQLLambdaQueryWrapper可以链式构建查询条件分页插件一套就支持MySQL分页。这套博客系统里文章列表分页、评论分页、标签文章查询全都依赖它代码量比纯MyBatis能少三分之一。3.2 前端方案Thymeleaf服务端渲染最省事前端这块很多人纠结要不要前后端分离。我的明确建议是个人博客系统用Thymeleaf做服务端渲染不要上Vue。原因很简单Vue那套意味着你要维护两套工程、处理跨域、考虑鉴权怎么对接工作量直接翻倍。Thymeleaf的好处是服务端直接把数据填入HTML模板返回浏览器静态资源CSS、JS、图片放在src/main/resources/static下后端方法返回视图名称就能渲染页面对毕设和练手项目来说再合适不过。页面布局上前台我建议找一个开源博客模板改一改比如基于Bootstrap的免费模板把里面的死数据替换成Thymeleaf的th:each循环后台管理界面建议直接用AdminLTE或者layui这类现成框架。你自己手写CSS也不是不行但成品效果大概率不如直接改一个成熟模板毕竟毕设展示时颜值也是分。3.3 工程目录结构按业务模块分包别按技术分包工程包名我习惯用com.blog再按业务模块划分com.blog ├── controller │ ├── admin后台接口 │ └── front前台接口 ├── service ├── mapper ├── entity ├── config │ ├── MybatisPlusConfig.java │ ├── WebMvcConfig.java │ └── InterceptorConfig.java ├── common │ ├── Result.java统一返回体 │ └── PageResult.java分页返回体 └── BlogApplication.java很多初学者喜欢按controller/service/mapper/entity这样分这种分法也没什么错但一旦功能多了controller包下会堆二三十个类找起来很头疼。按admin/front分的好处是你能一眼看出哪些接口给后台用、哪些接口给前台页面用层次清晰答辩的时候也更好讲。4. 核心模块逐个拆解文章、分类、评论的落地细节骨架搭好了接下来就是往里面填核心功能。我按“文章发布流程 - 列表查询 - 分类标签聚合 - 评论互动”这条主线来讲每个环节都是在实际开发中反复改过多次之后沉淀下来的写法。4.1 文章发布摘要与正文的分工写文章功能首先要搞清楚摘要和正文是两个字段不是同一份数据。t_article里我设计了summary字段用来存放文章摘要在列表页展示content字段存放完整正文。有的同学图省事列表页直接截取正文前100个字符当摘要这个做法不推荐——一是效率低二是格式会很乱Markdown截图出来可能是一堆符号。后台文章编辑页我用了Markdown编辑器前端引入editor.md或simplemde都行。提交的时候把编辑器里的内容原样POST到后端后端只做HTML转义和长度校验不做过多的内容处理。Markdown转HTML的渲染工作可以放在前端页面加载时用marked.js完成也可以后端用commonmark-java库转好再返回给前端。我更推荐后端渲染因为这样文章详情页拿到的直接是HTML字符串搜索引擎也能正常抓取内容。4.2 文章列表分页PageHelper还是MyBatis Plus分页插件分页查询是整个系统最常用的操作也是面试官最爱问的点。MyBatis Plus的分页插件用起来非常简单在配置类里注册一个PaginationInnerInterceptor然后Service层调用PageArticle page new Page(current, size);和articleMapper.selectPage(page, wrapper);就能拿到分页数据。当你查询文章列表时需要带上分类名称、标签名称、发布时间等信息。分类名称通过冗余字段来拿标签名称则是先查文章-标签关联表再把标签列表组装进文章对象里。这里有个小细节不要在一个循环里反复查数据库比如遍历10篇文章、每篇都查一次标签就是典型的N1问题。正确做法是先查出所有文章ID再用IN查询一次性把所有标签查出来在内存里做组装。4.3 分类与标签聚合页一条SQL还是多条SQL分类和标签的聚合查询是博客系统最容易写乱的地方。拿“点击某个分类展示该分类下所有文章”这个需求来说最简单的两个方法方法一先查t_category表拿到分类ID再用article.category_id去查文章列表。方法二直接在文章表上写WHERE category_id ?连表查询都不需要。这两个方法没本质区别推荐用方法二因为t_article表已经冗余了category_id和category_name一条查询就能搞定不需要JOIN也不需要额外查分类表。标签聚合稍微麻烦点因为文章和标签是多对多关系。查“某个标签下的文章列表”需要三步先查t_tag表拿标签ID再查t_article_tag表拿到所有文章ID最后用article.id IN (...)查询文章表。三步操作在逻辑上必须保证事务性所以在Service方法上加上Transactional注解。这三个查询没有写在一个SQL里而是分开查为什么因为代码可读性更高、索引利用率更好等数据量真的大了再考虑用JOIN去优化也不迟。4.4 评论模块表单提交、防刷与展示前台评论表单比较简单访客填昵称、邮箱和评论内容就能提交。但是这里至少有四个细节要注意第一parent_id的处理。提交评论时前端带一个隐藏域标记回复的是哪条评论如果是一级评论parent_id就是0。前端展示评论时后台按parent_id把评论组织成树形结构返回。第二评论内容过滤。不管前台还是后台提交内容都要做HtmlUtils.htmlEscape转义否则用户提交一段script就变成XSS攻击了。多写一行转义代码能帮你避开一个非常大的安全隐患。第三评论审核。个人博客的评论建议默认状态为“待审核”管理员在后台看到评论列表后选择通过或删除。虽然多了一步操作但可以防止垃圾评论直接展示在全省。第四评论数统计。文章详情页会显示评论数这个数字如果每次实时统计一次COUNT(*)也不是不行但为了展示方便我通常在t_article表里加一个comment_count字段每次新增评论时加一、删除评论时减一查询列表时直接读这个字段即可省去一次关联统计。5. 前端页面与后台管理服务端渲染也可以很顺手后端接口写完了剩下就是页面渲染的逻辑。这一步很多同学会卡住因为对Thymeleaf的语法不熟。多说一句Thymeleaf的语法核心就那几个th:text往标签里填文本th:each做循环遍历th:href拼URLth:if/th:unless做条件判断。把这些掌握住页面基本就能写了。5.1 前台页面骨架与通用模板片段前台页面我建议拆成三个公共片段头部导航header.html、底部版权footer.html和侧边栏sidebar.html。用Thymeleaf的th:fragment定义片段其他页面通过th:replace引入。这样做的好处是改导航栏只要改一处所有页面同步更新不会出现样式不统一的问题。首页的数据来源是从Controller的Model里传过去的。例如首页需要展示文章列表、分类列表、最新评论和站点信息就在Controller方法里分别查询并model.addAttribute()塞进去。页面里用${page.records}拿到文章列表用${categories}遍历分类用${siteConfig}读取博客名模板引擎会自动渲染成HTML字符串返回给浏览器。5.2 后台管理页面与权限拦截后台管理的所有页面统一放在/admin路径下通过一个登录拦截器控制访问权限。我这里不用Spring Security的完整登录流程而是用一个更轻量的方案用户登录成功后把用户ID存进Session然后写一个HandlerInterceptor在preHandle里检查Session里有没有登录标记没有就重定向到/admin/login。这样做代码量小、也够用。既然是轻量方案那后台接口的权限控制就都依赖这个拦截器。需要注意一个小坑拦截器的excludePathPatterns必须把登录页、登录接口、静态资源都排除掉否则你连CSS和JS都加载不出来。后台管理的页面布局一般用AdminLTE左侧菜单栏固定右侧内容区动态加载。每个管理页面就是一张表格配一个“新增”按钮和每行的“编辑/删除”按钮。这里没必要做到Ajax异步提交直接表单同步提交、控制器重定向回列表页是最稳的做法——少写一堆前端异步代码功能逻辑一样完整。5.3 文件上传与静态资源映射文章封面图片、头像这些需要文件上传功能。Spring Boot里文件上传很简单前端用input typefile后端Controller用MultipartFile接收然后保存到本地磁盘目录。但是有一个关键配置容易忽略——默认的Spring MVC静态资源映射只覆盖classpath:/static/你上传到磁盘的文件如果不额外配置映射浏览器是访问不到的。所以需要在WebMvcConfig里加一句Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler(/upload/**) .addResourceHandler(file: uploadPath /); }这样上传目录就变成了一个可通过URL访问的静态资源目录图片上传后直接存/upload/xxx.jpg这种路径文章里引用图片、展示封面都靠它。上传时最好给文件名拼一个时间戳或者UUID避免重名互相覆盖。6. 这几个坑跑项目时十有八九会遇到说实话我自己搭这套博客系统的时候没少被各种奇奇怪怪的问题绊住脚。这里整理几个我在调试过程中觉得最典型、也最值得注意的坑希望你能绕开。6.1 MySQL时区问题导致的时间差8小时这个坑出现的频率极高而且表现很诡异文章发布时间存进去是2025-01-01 12:00:00查出来却变成了2025-01-01 04:00:00整整少了8小时。原因就是驱动和数据库的时区设置不一致。解决办法是在application.yml里的数据库连接串上显式指定时区url: jdbc:mysql://localhost:3306/blog?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai同时确认MySQL服务本身的时区设置执行SHOW VARIABLES LIKE %time_zone%;看到System或者08:00都可以。两个地方对齐了时间就不会乱跳。6.2 字段自动填充createTime竟然为nullMyBatis Plus新增文章之后你会期望数据库自动填好的create_time能回填到实体类的createTime字段里。但默认情况下不一定行你需要用MyBatis Plus的字段自动填充功能。在实体类的createTime字段上加TableField(fill FieldFill.INSERT)然后写一个MetaObjectHandler的Bean在insertFill方法里给createTime和updateTime赋值。这样做的好处是新增文章后端代码里不需要手动setCreateTime(new Date())代码更干净。6.3 前端页面展示Markdown为什么HTML源码被原样输出了这个坑是在做文章详情页的时候遇到的数据库里的Markdown原文被th:text显示在页面上全都是#、**这些符号而不是渲染后的标题和加粗。原因是Thymeleaf的th:text会把内容当纯文本转义输出要想渲染成HTML必须使用th:utext。前后台展示文章内容的时候用th:utext${article.content}前提是你已经对内容做了可信处理后端转HTML或者已经清洗过XSS否则会带来注入风险。6.4 SQL语句里的关键字冲突文章表里有个comment_count字段还有个status字段status在MySQL里不属于保留字但某些版本里容易和关键字冲突。为了避免莫名其妙的SQL报错尽量在涉及status、comment这些有歧义的字段名时在SQL语句里用反引号包一下或者直接换个字段名比如comment_status。这是个很小的习惯但是能帮你省下很多排查时间。6.5 部署到服务器后上传文件无法访问本地跑的时候文件上传正常一到云服务器就40480%是因为部署方式导致的路径问题。假如你在Linux上用的是java -jar blog.jar那么相对路径“upload/”是相对当前用户的home目录的不是jar包所在目录。建议在配置文件里指定一个绝对路径比如/home/blog/upload/并在WebMvcConfig里把那个目录映射到/upload/**。另外检查Linux目录权限确保运行jar包的账号有写权限——这个坑我当年也踩过排查了整整一个晚上。7. 从本地到服务器打包部署的完整链路项目能跑通是一回事能部署到公网让人访问是另一回事。毕设答辩的时候如果能在现场展示一个公网访问的博客地址效果会好很多。部署这条路不复杂但有不少细节需要确认。7.1 打包前的关键配置首先application.yml里要区分开发环境和生产环境。我习惯把数据库连接、上传路径这些外部化到application-prod.yml里用spring.profiles.activeprod切换。打包之前先把测试数据库的连接改掉不然部署到服务器上会连到你本机的数据库去那自然是连不通的。其次确认pom.xml里打包方式是jar而不是war。Spring Boot内嵌Tomcat的工程直接用mvn clean package打包即可产物在target/目录下。7.2 服务器部署步骤服务器上只要装好JDK 1.8和MySQL把SQL脚本导入数据库然后执行java -jar blog-0.0.1-SNAPSHOT.jar --spring.profiles.activeprod加一个--server.port80也行如果想直接用80端口就不用再配Nginx转发。不过我更推荐前面再挂一个Nginx做静态资源缓存和反向代理这样博客的静态资源CSS、JS、图片访问速度更快后端进程也相对安全毕竟Tomcat直接暴露在公网不是一个好习惯。7.3 一个小建议日志别忽略开发阶段很多人习惯用System.out.println打日志部署之后这点一定要改掉。用Slf4j的Logger把日志输出到文件排查问题的时候才知道发生了什么。配置很简单logging: file: name: logs/blog.log level: com.blog: info这样线上报错看一眼blog.log就能定位问题比黑屏终端滚动强多了。8. 这套博客还能怎么扩展项目做到能跑、能演示、能部署其实你的收获已经不小了。如果还想让这个项目的含金量再上一层楼我个人建议按下面的优先级去扩展。第一优先级是搜索功能。现在文章找起来只能靠分类和标签加一个基于LIKE的标题搜索很简单五分钟的事如果想让搜索体验更好可以集成Elasticsearch不过个人博客的数据量完全没必要MySQL的LIKE %关键字%在万级数据下性能完全够用。第二优先级是静态化。每篇文章详情页在首次访问或文章更新时生成一个静态HTML文件Nginx直接读取磁盘文件返回不经过Java应用。这个优化思路在面试里讲出来是加分项但我不建议毕设阶段做容易把自己绕晕。第三优先级是接入第三方登录。如果博客要对外开放GitHub或微信扫码登录是个不错的亮点。不过我个人的建议是除非你技术底子已经比较扎实否则还是先专注于把现有功能打磨好扩展功能容易改出一堆隐藏bug。我自己的体会是做这种“源码数据库”类型的完整项目最锻炼人的不是某个技术点本身而是整体流程的把控从需求分析到表设计从接口联调到部署上线每一步都踩一遍坑、填一遍坑你对Spring Boot这整个生态的感知就不再是某个章节的知识点了而是串成了一条线。这套博客系统做完再回头去看那些散落在教程里的知识点——AOP、拦截器、事务、分页、文件上传、自动配置——你会发现自己真的看懂了。本文还有配套的精品资源点击获取
返回列表