基于Java Web的博客系统课程设计:从数据库设计到部署实战 📅 发布时间:2026/9/16 15:01:57 👁 浏览次数: 简介这是一份基于Java Web的博客系统课程设计完整资源包面向计算机相关专业学生、Java初学者及需要完成课程设计的开发者解决选题难、缺少可运行示例与配套说明的痛点。项目采用MVC开发模式基于Eclipse/MyEclipse集成开发环境运行在Tomcat服务器与MySQL数据库之上覆盖三种用户角色管理员可进行博主管理与在线统计普通用户可注册登录、浏览与发表文章博主可管理博文、类别、评论及修改密码功能层次清晰适合学习Servlet/JSP开发、JDBC操作及分层架构设计。资源共74个文件压缩包仅802KB包含21个JSP页面、13个Java源码文件及对应class文件、4个SQL数据库脚本外加课程设计报告DOC文档、图片素材与项目配置文件源码目录与WebRoot结构完整导入IDE即可运行。已有1323人学习下载既能用于日常练手也可作为课程设计、毕业设计的参照模板数据库脚本还原后即可体验完整流程。1. 基于 Java Web 的博客系统课设题目为什么卡在“闭环”课程设计里真正翻车的往往不是代码量而是项目没有闭环。基于 Java Web 的博客系统是 Java Web 课设里的常客登录注册、发文、分类、评论功能看着不多但要把源码、数据库脚本和课程设计报告三样东西凑齐并能相互印证才是大多数人丢分的位置。这类以压缩包交付的题目通常的预期是导入数据库脚本能初始化源码能部署报告里的 ER 图和表结构能与代码对得上。需要这条路径的人有三类第一次完整接触 Servlet/JSP 的学生被拉去验收工程质量的工程师以及负责指导的老师。后面按通常做法展开先定需求边界再落数据库表设计和初始化数据接着写核心 Java Web 代码最后讲部署验证和报告收口。工程师看建表和外键取舍学生直接照搬能跑通的部分互不耽误。2. 博客系统的需求边界与 Java Web 选型课设和技术采购一样第一步不是写代码而是把“做完”定义清楚。这个题目的功能边界其实很固定把它拆开再看技术选型后面写代码和写报告都会顺畅很多。2.1 先定义最小功能集登录注册、文章、分类、评论我一般会把博客系统砍成两个面面向浏览者的展示面面向维护者的管理面。课程设计最少得有用户模块、文章模块、分类模块、评论模块再加一个后台的简单管理。用户模块是头歌这类在线实验平台上最常见的一关一个能注册、能登录、登录后能看到自己昵称、退出后不能继续访问后台的完整流程。登录注册界面的验收点不是页面漂亮而是会话存在哪里、退出后 cookie 和 session 怎么清理。在定功能时我会直接写成一张表格这张表后面也会原样挪进课设报告的需求分析章节模块功能点验收标准优先级用户模块注册、登录、退出、修改昵称未登录访问后台被拦截必做文章模块发表、编辑、删除、分页列表标题、正文、发布时间完整显示必做分类模块分类维护、按分类筛选文章删除分类时提示文章数建议做标签模块打标签、按标签聚合一篇文章可对多个标签选做评论模块发表评论、一级回复文章详情页能看到评论列表建议做这段表格能同时回答“数据库里要有哪些表”和“代码要写哪些接口”两个问题。注意不要把“用户管理后台”写成“用户能自己给自己加管理员权限”这在课设答辩里会被追问权限提升漏洞。2.2 为什么用 Servlet/JSP MyBatis 而不是一上来就 Spring Boot很多人在选型这一步就直接跑偏。Spring Boot 用内嵌 Tomcat 启动方便但它在 Servlet 之上封装了一层课设报告里要讲请求生命周期、过滤器、会话保持这些原理时Spring Boot 会把容器和请求链路藏得太深反而不容易写出有深度的内容。更实际的问题是 JSP 在 Spring Boot 里不是默认技术栈要么改依赖要么换模板引擎对一个课程设计来说配置成本高于收益。我习惯的方案是 Maven 打包成 war用 Servlet JSP MyBatis 分层。数据访问层用 MyBatis 而不是裸 JDBC能少写大量预编译和结果集映射代码同时在报告里解释 SQL 也比解释 Hibernate 的级联逻辑容易。dependencies !-- Servlet API 由 Tomcat 提供作用域必须为 provided -- dependency groupIdjavax.servlet/groupId artifactIdjavax.servlet-api/artifactId version4.0.1/version scopeprovided/scope /dependency dependency groupIdjavax.servlet/groupId artifactIdjstl/artifactId version1.2/version /dependency dependency groupIdorg.mybatis/groupId artifactIdmybatis/artifactId version3.5.13/version /dependency dependency groupIdmysql/groupId artifactIdmysql-connector-j/artifactId version8.0.33/version /dependency /dependencies这段配置里的关键参数有两个。第一javax.servlet-api的scope必须写成provided意思是编译时用 Servlet API但打包时不把 Tomcat 自带的东西塞进 war避免出现NoSuchMethodError之类的容器冲突。第二mysql-connector-j的 groupId 是com.mysql而不是旧的mysqlMySQL 8 之后官方改了坐标照抄旧教程会把驱动依赖打不进去。2.3 运行环境与版本兼容矩阵版本选型是本项目最容易出现“我代码没问题就是跑不起来”的根源。Servlet、Tomcat、JDK、MySQL 四者的对应关系必须提前定好否则会浪费大量时间在环境问题上。组件推荐版本注意点JDK8 或 11对应 Tomcat 9.x使用 javax.servlet 包JDK17 或 21对应 Tomcat 10.1代码 import 要改成 jakarta.servletTomcat9.0.x课设最稳网上资料最多MySQL8.0.x字符集用 utf8mb4MyBatis3.5.xmapper XML 和注解二选一别混用Navicat / IDEA Database任意用来导入脚本和画 ER 图如果电脑装的是 JDK 21直接配合 Tomcat 10.1代码里把javax.servlet.http.*替换成jakarta.servlet.http.*逻辑完全一样。这个点值得在报告里写一句说明你意识到不同版本的包名分水岭比通篇写“JDK 21 环境配置教程”更有技术含量。3. 数据库设计博客系统的六张表与初始化脚本数据库设计是这类题目的评分重头戏。博客系统的表结构不需要复杂但关系要完整字段类型要经得起追问。下面这套六张表方案是我常用的能覆盖必做功能又不至于把时间耗在过度设计上。3.1 从需求到表六张表的字段视角需求表里的用户模块、文章模块、分类模块、标签模块、评论模块正好对应六张表。核心关系是用户一对多文章文章多对一分类文章和标签通过中间表多对多评论挂在文章下面。-- 先删从表再删主表避免外键报错 DROP TABLE IF EXISTS article_tag; DROP TABLE IF EXISTS comment; DROP TABLE IF EXISTS article; DROP TABLE IF EXISTS tag; DROP TABLE IF EXISTS category; DROP TABLE IF EXISTS user; CREATE TABLE user ( id INT AUTO_INCREMENT PRIMARY KEY COMMENT 用户ID, username VARCHAR(50) NOT NULL COMMENT 登录名, password CHAR(64) NOT NULL COMMENT 密码散列值 sha256(saltpassword), salt CHAR(16) NOT NULL COMMENT 用户随机盐, nickname VARCHAR(50) NOT NULL DEFAULT COMMENT 昵称, avatar VARCHAR(255) NOT NULL DEFAULT COMMENT 头像URL, role TINYINT NOT NULL DEFAULT 1 COMMENT 角色1普通用户2管理员, create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 注册时间, UNIQUE KEY uk_username (username) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT用户表; CREATE TABLE category ( id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(50) NOT NULL COMMENT 分类名称, sort INT NOT NULL DEFAULT 0 COMMENT 排序值越小越靠前, UNIQUE KEY uk_category_name (name) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT文章分类表; CREATE TABLE tag ( id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(50) NOT NULL, UNIQUE KEY uk_tag_name (name) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT标签表; CREATE TABLE article ( id INT AUTO_INCREMENT PRIMARY KEY, user_id INT NOT NULL COMMENT 作者ID, category_id INT NOT NULL COMMENT 分类ID, title VARCHAR(100) NOT NULL COMMENT 标题, summary VARCHAR(255) NOT NULL DEFAULT COMMENT 摘要, content LONGTEXT NOT NULL COMMENT 正文, view_count INT NOT NULL DEFAULT 0 COMMENT 浏览量, create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, KEY idx_article_user (user_id), KEY idx_article_category (category_id), CONSTRAINT fk_article_user FOREIGN KEY (user_id) REFERENCES user (id), CONSTRAINT fk_article_category FOREIGN KEY (category_id) REFERENCES category (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT文章表; CREATE TABLE article_tag ( article_id INT NOT NULL, tag_id INT NOT NULL, PRIMARY KEY (article_id, tag_id), KEY idx_tag_id (tag_id), CONSTRAINT fk_at_article FOREIGN KEY (article_id) REFERENCES article (id), CONSTRAINT fk_at_tag FOREIGN KEY (tag_id) REFERENCES tag (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT文章标签关联表; CREATE TABLE comment ( id INT AUTO_INCREMENT PRIMARY KEY, article_id INT NOT NULL, user_id INT NOT NULL, content VARCHAR(500) NOT NULL COMMENT 评论内容, parent_id INT DEFAULT NULL COMMENT 父评论ID为空表示一级评论, create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, KEY idx_comment_article (article_id), CONSTRAINT fk_comment_article FOREIGN KEY (article_id) REFERENCES article (id), CONSTRAINT fk_comment_user FOREIGN KEY (user_id) REFERENCES user (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT评论表;这套 DDL 里有几个参数不是随便写的。password字段用CHAR(64)而不是VARCHAR(64)因为 SHA-256 输出定长 64 位十六进制字符定长类型在索引比较时开销更小。content用LONGTEXT是因为博客正文可能含有 Markdown 源码或 HTML 片段TEXT类型上限 64KB 不够用。分类名和标签名都加了唯一索引这是为了防止同一条后台数据以两种写法存在库里。3.2 外键、索引与冗余字段课设数据库的取舍外键在生产环境里我基本不建因为大流量下的插入和删除会被外键校验拖慢而且拆库拆表后外键会变成维护负担。但课设项目我建议保留物理外键原因很直接答辩时能被问“数据完整性怎么保证”你可以理直气壮回答“数据库层面有外键约束应用层还有事务控制”这是最容易讲清楚的方案。冗余字段这个点体现出设计边界。文章表里没有冗余作者名和分类名而是只存user_id和category_id原因是分类名和昵称都可能被修改冗余会让改名字时需要同步多张表。但view_count这种统计字段是冗余在文章表里的它频繁被读取每次去 count 关联表代价更高。冗余字段的正确用法是读多写少的字段可以先冗余写多读少的一定不要碰。标签查询的常见写法是SELECT t.name FROM tag t JOIN article_tag at ON t.id at.tag_id WHERE at.article_id ?中间表不存任何业务字段只存两边的 ID。3.3 用 Navicat 导入 DDL 并初始化管理员账号拿到脚本后在 Navicat 建一个blog_db数据库字符集选utf8mb4排序规则选utf8mb4_general_ci然后直接运行上面的 DDL。执行顺序是固定的如果脚本里已经有数据必须按从表到主表的顺序删表否则外键约束会拦住DROP TABLE语句。初始化管理员账号时密码一定不能明文存。常见的做法是生成随机盐把sha256(盐 密码)这个散列值存进数据库。注册接口里的密码加密工具类至少要有这个逻辑public static String sha256(String input) throws NoSuchAlgorithmException { MessageDigest md MessageDigest.getInstance(SHA-256); byte[] hash md.digest(input.getBytes(StandardCharsets.UTF_8)); StringBuilder sb new StringBuilder(); for (byte b : hash) { sb.append(String.format(%02x, b)); } return sb.toString(); } // 注册时salt 随机16位字符串password sha256(salt 明文密码) // 登录时取用户salt重新计算sha256(salt 输入密码)和库中password比对String.format(%02x, b)的作用是把每个字节转成两位小写十六进制保证长度正好是 64。登录校验时注意不能只比对密码同一个用户每次登录都要用自己那条记录里的 salt 重新计算而不是把整个表按用户名查出来后直接拿库里的 password 字段做对比变量。初始化管理员的 SQL 语句里的散列值需要用上面的工具类先生成再粘进 INSERT 语句不要直接在 SQL 里写明文密码。3.4 被追问最多的三个设计问题答辩时数据库这块的问题基本集中在三处。第一为什么文章和标签要多一张中间表而不是把标签存成逗号分隔的字符串因为按标签聚合文章时字符串字段要LIKE %标签%扫描无法走索引中间表可以直接 join。第二为什么用户密码要加盐因为两个人密码相同时散列值也不同攻击者无法用彩虹表反查。第三为什么评论表要留parent_id这比只做一层评论多了一个字段的成本却保留了以后扩展二级回复的空间数据库设计要有可演进性。这三个问题在报告里写清楚比堆功能更能体现设计能力。4. Java Web 核心代码会话、分页与安全过滤项目题干里写了源码那源码这部分就得经得起看。课设最常见的丢分点是所有业务逻辑堆在 Servlet 里、SQL 写死在 Java 字符串里、登录拦截拿 session 判断后没有白名单。这一章把这三个点全部处理干净。4.1 工程结构与三个关键文件代码分层按 controller、service、mapper、entity、filter 五层来分controller 里只做参数接收和视图跳转业务逻辑放 service数据库访问放 mapper。工程目录结构如下src/main/java/com/course/blog ├── controller/ # Servlet负责 HTTP 请求入口 ├── service/ # 业务接口和实现 ├── mapper/ # MyBatis 的 Mapper 接口 ├── entity/ # User、Article、Comment 等实体 ├── filter/ # 登录过滤器和编码过滤器 └── util/ # 加密、分页等工具类 src/main/resources └── mybatis-config.xml src/main/webapp ├── WEB-INF/jsp/ # 页面文件 ├── static/ # css、js、图片 └── index.jspfilter 包要放两个类字符编码过滤器统一处理 POST 请求的 UTF-8登录过滤器处理会话校验。字符编码过滤器只需要三行代码但漏掉它中文乱码会让整个课设看起来极不专业。4.2 登录会话与权限控制SessionFilter 实用写法登录模块的常见误用是只在前端隐藏“写文章”按钮后端所有 Servlet 都不校验会话或者校验了但把静态资源和登录接口也拦了导致登录页的 CSS 加载不出来形成“登录框出现了但没有样式”的尴尬局面。下面这个过滤器是课设里够用的解决方式WebFilter(/*) public class LoginFilter implements Filter { private static final ListString WHITE_LIST Arrays.asList( /login.jsp, /register.jsp, /api/login, /api/register, /static/, /article/list, /article/detail ); Override public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { HttpServletRequest request (HttpServletRequest) req; HttpServletResponse response (HttpServletResponse) resp; String uri request.getRequestURI(); // 白名单直接放行注意用 contains 而不是 equals兼容 /static/css/style.css for (String prefix : WHITE_LIST) { if (uri.contains(prefix)) { chain.doFilter(request, response); return; } } Object loginUser request.getSession().getAttribute(loginUser); if (loginUser null) { response.sendRedirect(request.getContextPath() /login.jsp); return; } chain.doFilter(request, response); } }这个过滤器有两个参数值得注意。/static/的匹配用contains可以兼容/static/css/style.css这种多级路径request.getContextPath()是必须的前缀项目部署到 Tomcat 时访问路径里会带工程名写死重定向 URL 会导致 404。登录成功后应该在 session 里放User对象而不是只放用户名这样 JSP 里直接取loginUser.nickname就能展示省去页面里再向数据库查询用户信息的多余 SQL。4.3 文章列表分页PageBean 与 LIMIT 偏移量分页查询是博客系统的必做项也是分页 bug 的重灾区。我一般用 PageBean 封装分页参数SQL 只关心两个值offset和pageSize前端负责传pageNum。public class PageBeanT { private int pageNum; // 当前页码从 1 开始 private int pageSize; // 每页条数 private long total; // 总记录数 private ListT rows; // 当前页数据 } // Service 层计算偏移量 int offset (pageNum - 1) * pageSize;对应的 MyBatis mapper XML 里是这条 SQLselect idselectArticlePage resultTypeArticle SELECT id, title, summary, view_count, create_time FROM article ORDER BY id DESC LIMIT #{offset}, #{pageSize} /select分页参数表给出来方便对照参数含义示例pageNum第几页从 1 开始2pageSize每页条数建议 5 或 1010offset偏移量(pageNum - 1) * pageSize10total总记录数用 COUNT(*) 查询25pageNum 从 0 开始传是分页最常见的错误会导致第一页数据永远少十条。另外必须注意ORDER BY id DESC不要去掉否则数据更新后翻页会出现同一篇文章出现在两页里的情况。如果想要热门文章置顶就改成ORDER BY view_count DESC, id DESC多字段排序是课设里能写进报告的一个亮点。4.4 XSS 过滤编辑器与展示之间的一层保险填了富文本编辑器的项目必须处理 XSS 注入。一个最简单的防守手段是展示层做 HTML 转义把用户输入的内容里的尖括号、引号变成实体字符。Java 里的替换工具类如下public static String cleanContent(String input) { if (input null) { return ; } return input.replace(, amp;) .replace(, lt;) .replace(, gt;) .replace(\, quot;) .replace(, #39;); }注意替换顺序必须先把替换成amp;否则第二次替换产生的会被重复加工。如果博客系统需要支持 Markdown 渲染只做转义会把代码块和链接也转义掉这时需要在渲染层把白名单允许的标签还原比如只允许code、pre、a这类安全标签其余全部转义。JSP 页面里显示文章标题时也应该用c:out value${article.title}/而不是${article.title}JSTL 这个标签会默认转义文本内容是页面层最省事的保险。5. 在 Tomcat 上跑通并写进课设报告这一章解决两个收尾问题环境跑不起来时最快怎么定位报告里怎么写才能让代码和数据库“对得上”。5.1 部署参数与常见报错对照表Maven 项目打包部署的标准流程是mvn clean package生成 war 包把 war 复制到 Tomcat 的 webapps 目录启动 Tomcat 后自动解压部署。命令行方式运行可以看实时日志mvn clean package cp target/blog.war /path/to/tomcat/webapps/ /path/to/tomcat/bin/startup.sh /path/to/tomcat/bin/catalina.sh runcatalina.sh run会把日志输出到控制台而不是写进 catalina.out排查启动报错时信息更直观。部署最常遇到的四类问题整理成对照表报错/现象原因修复ClassNotFoundException: com.mysql.cj.jdbc.Driver数据库驱动 jar 没进 WEB-INF/lib确认 MySQL connector 依赖是 compile 作用域Communications link failure数据库连接 URL 或端口错误检查 jdbc:mysql://localhost:3306/blog_db中文全变问号连接 URL 缺编码参数、页面编码不一致URL 末尾加 useUnicodetruecharacterEncodingUTF-8登录成功后一直跳回登录页session 里没放 loginUser 或过滤器白名单写死检查登录 Servlet 的 setAttribute 代码连接 URL 这个参数值得多说一句characterEncodingUTF-8是 MySQL 连接层的字符集声明它管的是 Java 程序往 MySQL 发送数据的编码和数据库建库时的utf8mb4是两层东西两个都要对才不会乱码。5.2 报告别贴假 ER 图用这组证据链收口报告里数据库设计章节最常见的扣分点是截图里的 ER 图和实际建表 SQL 对不上。避免这个问题的做法是在 Navicat 的模型页面直接根据六张表生成关系图再导出成图片插入报告。注意导出的模型要和 DDL 脚本是同一次改动的产物不要在写完报告后又改了表结构却不重新截图。报告里建议放三样东西数据字典表、ER 图、测试用例表。数据字典用表格列出字段类型和说明测试用例表按功能列步骤格式是“输入什么、预期结果、实际结果”。答辩的时候拿出 Navicat把 DDL 脚本从头到尾重新执行一遍能成功再打开后台页面演示一遍发文和评论比解释十页架构图都更能说明问题。本文还有配套的精品资源点击获取