ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

JavaWeb新闻管理系统课程设计:Servlet+JDBC+MySQL实战与避坑指南

JavaWeb新闻管理系统课程设计:Servlet+JDBC+MySQL实战与避坑指南

简介:面向JavaWeb课程设计及期末大作业场景,这套新闻管理系统源码包提供了从后端业务逻辑到前端页面的完整实现。项目基于Servlet/JSP和数据库连接池构建,涵盖新闻分类展示、详情查询、后台管理、登录验证等典型功能模块,既适合初学者对照学习代码分层,也便于直接修改后作为课设提交。压缩包共274个文件,约6.85MB,其中包含96个编译后的class文件、48个Java源文件、22个JSP页面、SQL数据库初始化脚本、8个JAR依赖包以及CSS、图片等静态资源,结构清晰,导入IDE配置数据库即可运行。目前已有579人学习下载,属于关注度较高的课设参考项目。借助源码中的数据库脚本和完整目录结构,读者可以快速理清DAO、Servlet、JSP之间的调用关系,并在此基础上扩展搜索分页、评论互动等进阶功能。

1. 一个能跑的 JavaWeb 新闻管理系统:先看这份 95 分代码包长什么样

做 Java 期末大作业,最怕的不是不会写代码,而是交上去的东西老师跑不起来。我见过太多人精心设计的功能,最后栽在 Tomcat 版本不对、数据库用户名密码不匹配这种问题上。基于 JavaWeb 的新闻管理系统,本身就是 JavaWeb 课程设计的经典骨架:JSP 做页面、Servlet 做控制、JDBC 操作 MySQL,没有花哨的框架,但后端核心流程一个不缺。这份源码包的特点是结构干净,类不多,职责明确,适合拿来二次开发而不是直接抄完就交。它最适合三类人:正在愁期末大作业怎么交的、想从理论课跨到完整项目的、以及答辩前需要快速搞懂每个类在干什么的人。下面我按自己拆工程的习惯,把架构、跑通步骤、坑和改造方案一条条展开讲。

2. 系统架构与库表设计:Servlet、JDBC 和 news 类是怎么协作的

先声明一个原则:拿到任何 JavaWeb 项目,不要急着按 F5,先看类名和包结构。这份资源里的类不多,但每一个都站在自己的位置上:DBHelper 负责拿数据库连接,HomeQueryServlet 处理首页新闻列表的请求,QueryServlet 处理带条件或分类的查询,news 类对应新闻表的实体,NewsDao 封装了所有 SQL 操作。能把这几层关系讲清楚,答辩就成功了一半。

2.1 为什么课程设计用 Servlet + JDBC 而不是 Spring Boot

这两年 Spring Boot 很流行,但很多学校 JavaWeb 课程设计大纲还锁在 Servlet + JSP + JDBC 这个组合上,原因其实很现实:这个技术栈强制你手写 SQL、手写请求转发、手写连接管理,老师能从代码里看到你确实掌握了 Java 基础语法和数据库操作,而不是复制了一堆注解。用这套老技术栈做新闻系统还有一层额外好处,就是运行流程完全可控:一个请求从 Tomcat 到 Servlet,再到 DAO 层,再到 MySQL,每一跳都是明明白白的 Java 代码。

老技术栈的代价是样板代码多。拿数据库连接来说,Spring Boot 里一个配置就好,JDBC 则需要手动加载驱动、建连接、关资源。新闻系统这种体量正好卡在“多但不多”的临界点上,代码量够撑起课程设计,又不至于让你在期末周熬夜调不通。

2.2 数据库设计:新闻分类表、新闻表后台管理员表

新闻管理系统的核心数据无非三类:新闻、分类、管理员。我一般建议三张表,确定的理由也很朴素:新闻表需要外键指向分类,分类做下拉筛选时要用;管理员单独成表,是因为后台登录功能需要和新闻查询解耦。

建表 SQL 可以参考下面的写法,MySQL 8 和 5.7 都兼容:

DROP DATABASE IF EXISTS news_db; CREATE DATABASE news_db DEFAULT CHARACTER SET utf8mb4; USE news_db; CREATE TABLE category ( id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(50) NOT NULL UNIQUE COMMENT '分类名称,如校园/科技/公告' ); CREATE TABLE news ( id INT AUTO_INCREMENT PRIMARY KEY, category_id INT NOT NULL, title VARCHAR(200) NOT NULL, content TEXT NOT NULL, author VARCHAR(50) DEFAULT 'admin', publish_time DATETIME DEFAULT CURRENT_TIMESTAMP, click_count INT DEFAULT 0, FOREIGN KEY (category_id) REFERENCES category(id) ); CREATE TABLE admin ( id INT AUTO_INCREMENT PRIMARY KEY, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(100) NOT NULL );

这里关键参数要说一下:news 表的外键约束保证不会插入一个不存在的分类 ID;publish_time 用 DATETIME 而不是 TIMESTAMP,因为新闻系统的发布时间需要到秒,DATETIME 范围够用;content 用 TEXT,一般几百字的新闻够用了。注意,category 表要提前插入几条分类数据,否则首页和新增新闻的下拉框全是空,这属于新手最容易漏的步骤。

2.3 DBHelper 类与 Servlet 请求流转:拿到一条新闻数据的完整链路

DBHelper 是这个系统的拧螺丝角色,所有 DAO 方法都要从这里拿连接。一个合格的实现要保证驱动只加载一次,后续每次拿连接直接走 DriverManager。

import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; public class DBHelper { private static final String URL = "jdbc:mysql://localhost:3306/news_db?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&useSSL=false"; private static final String USER = "root"; private static final String PASSWORD = "123456"; private static Connection conn; static { try { Class.forName("com.mysql.cj.jdbc.Driver"); } catch (ClassNotFoundException e) { e.printStackTrace(); } } public static Connection getConnection() throws SQLException { if (conn == null || conn.isClosed()) { conn = DriverManager.getConnection(URL, USER, PASSWORD); } return conn; } public static void close(Connection conn, java.sql.Statement stmt, java.sql.ResultSet rs) { // 关闭顺序:rs 先关,stmt 其次,conn 最后 } }

这段代码里的 URL 参数最容易踩坑:characterEncoding=utf8 保证中文不乱码,serverTimezone=Asia/Shanghai 是 MySQL 8 的硬性要求,不写会报时区异常。另外注意 I 用的是 com.mysql.cj.jdbc.Driver,MySQL 5.7 虽然也能用这个驱动类,但如果你用的是老驱动包,就要改回 com.mysql.jdbc.Driver。这里的静态代码块保证 Class.forName 只执行一次,属于很标准的写法。

Web 请求流转链路其实很短。以首页为例:浏览器访问 index.jsp 或 HomeQueryServlet 映射的地址,Tomcat 创建 Servlet 实例并调用 doGet 方法,HomeQueryServlet 调 NewsDao 的查询方法拿到 List ,存进 request 作用域,forward 给 home.jsp,JSP 用 JSTL 或脚本遍历输出。

@WebServlet("/home") public class HomeQueryServlet extends HttpServlet { private NewsDao newsDao = new NewsDao(); @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 设置编码,防止响应中文乱码 request.setCharacterEncoding("UTF-8"); response.setContentType("text/html;charset=UTF-8"); List<news> newsList = newsDao.getLatestNews(10); request.setAttribute("newsList", newsList); request.getRequestDispatcher("home.jsp").forward(request, response); } }

这段代码里的 getLatestNews(10) 是 DAO 层一个按时间倒序取前 10 条的方法,SQL 大概是 SELECT * FROM news ORDER BY publish_time DESC LIMIT ?。把查询结果塞进 request 再用 forward 转发,JSP 里就可以通过 ${newsList} 直接遍历。要注意不能用重定向,因为重定向会丢弃 request 里的数据。

3. 本地跑通:从 JDK、MySQL 到 IDEA 的部署全流程

这一章是整篇文章里最“动手”的部分。我见过太多人源码在手却跑不起来,问题往往不是代码有问题,而是环境组合不对。下面按我平时搭环境的顺序来,每一步都有明确验证标准。

3.1 环境版本怎么配对:JDK、Tomcat、MySQL 缺一不可

不要小看版本配对,JavaWeb 老项目对版本极其敏感。列出我验证过的组合,也列两个不建议的组合。

组件推荐版本不推荐版本
JDKJDK 8 或 JDK 11JDK 17(老 Tomcat 不兼容)
TomcatTomcat 8.5 或 9.0Tomcat 10(包名从 javax 变 jakarta)
MySQL5.7 或 8.0MySQL 8 但用过期的驱动包
IDEA2020 及以上版本无特殊限制

Tomcat 10 是个大坑,它把 javax.servlet 换成了 jakarta.servlet,这份源码里的 import javax.servlet.* 直接编译不过。所以安装 Tomcat 之前先确认版本大于 9.0 就换掉。

3.2 初始化数据库:SQL 文件导入的正确姿势

从压缩包里找到 .sql 后缀文件后,别用记事本打开看,直接导入。两种方式任选其一。第一种是命令行方式,适合已经配好 mysql 命令的环境变量:

mysql -u root -p < news_db.sql

输入密码后如果没有报错,再登录进去确认表是否存在。第二种是可视化方式,Navicat 或 DBeaver 里右键数据库,选择运行 SQL 文件,注意编码要选 UTF-8,否则中文注释和初始数据会乱。导入完以后执行下面这条语句验证:

USE news_db; SHOW TABLES; SELECT COUNT(*) FROM category;

如果 category 表返回空,你需要手动补几条初始数据再把系统跑起来。很多同学在这里跳过验证,结果系统启动后页面上一片空白,还以为是 Java 代码的问题。

3.3 IDEA 里配置 Tomcat 并启动:artifact 是多数人翻车的地方

IDEA 跑 JavaWeb 项目,核心思路是把你的项目打成一个 artifact(war 包格式),挂到 Tomcat 上。步骤如下:打开 Project Structure,确保 Project SDK 选了 JDK 8;然后在 Artifacts 里添加 Web Application: Exploded;接着进入 Run 菜单的 Edit Configurations,新建 Tomcat Server → Local,Deployment 选项卡里点加号,选中刚建好的 artifact。这一步是最多人漏掉的,不配置 Deployment,Tomcat 启动成功但访问页面 404。

启动前还要确认几个联动设置:Application context 填 /news 或保持根路径 /;HTTP port 保持 8080,但需要确认本机没有被占用;如果项目里已经带了 web.xml,IDEA 会自动识别,不用额外设置。

点击运行按钮,看到 Tomcat started on port 8080 字样,浏览器访问 http://localhost:8080/(按你的 context 路径调整),能出现新闻列表页就算半只脚踏过了门槛。出现 404 或 500 时,优先看 IDEA 控制台的日志,而不是重新启动。

4. 避坑排查:Tomcat 端口冲突、中文乱码、驱动异常这些翻车现场

这套新闻系统我至少帮人排过十几次错,翻车翻来翻去无非集中在下面几个点。每个我都按“现象 → 原因 → 解决”的方式记录下来。

4.1 Tomcat 启动后页面打不开,控制台提示端口被占用

现象:点击启动后 IDEA 报 Error, Port 8080 is already in use,或者 Tomcat 能启动但页面一直 loading。

原因:电脑上已有其他进程占用了 8080 端口,常见的是之前启动过的 Tomcat 没关闭,或者用了其他 Web 开发工具。

解决:先关掉之前在运行的 Tomcat 服务,或在命令行执行 netstat -ano | findstr :8080 找到 PID,然后 taskkill /F /PID 进程号。也可以直接把 Tomcat 的端口改掉,办法是找到 conf/server.xml 里的 Connector 端口,把 8080 改成 8088,然后重启。

4.2 启动时 ClassNotFoundException: com.mysql.cj.jdbc.Driver

现象:进入首页后跳 500,错误日志里出现 ClassNotFoundException。

原因:MySQL 驱动 jar 包没放进 WEB-INF/lib 目录,或者放的位置不对只在外部 library 里。

解决:把 mysql-connector-java 的 jar 包复制到项目的 src/main/webapp/WEB-INF/lib 下,IDEA 里右键这个包选 Add as Library。这个步骤做完再热重启一次。驱动类名如果和 MySQL 版本对不上,也会报同样的错,参考第 2.3 节里说的按版本改驱动类路径。

4.3 新闻标题和内容在页面上全是问号

现象:MySQL 里中文正常,页面上展示时全是??,或者反过来页面正常但数据库里是乱码。

原因:页面、数据库表、JDBC 连接三处字符集没有统一。JSP 页面可能是 GBK,数据库连接没带 characterEncoding=utf8,或者表建成了 latin1。

解决:数据库建库时用 utf8mb4,JDBC URL 带上 characterEncoding=utf8,JSP 头部声明都统一:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>

如果库里已经有乱码数据,把表删掉重建再导入 SQL,这个毁掉重来是最省时间的。

4.4 访问后台登录页报 500,提示 Access denied for user

现象:控制台刷出 Access denied for user 'root'@'localhost' (using password: YES)。

原因:DBHelper.java 里的用户名或密码与本地 MySQL 实际配置不一致。课程设计包里为了方便,密码经常写死成 123456,你本机改了密码就会崩。

解决:打开 DBHelper.java,把 USER 和 PASSWORD 改成自己 MySQL 的账号密码,然后重新编译。注意 IDEA 里改了 Java 文件要重新部署,有些同学改了代码不重启,还以为没生效,这也是个隐形坑。

4.5 页面报 500 空指针,根源在查询结果集为 null

现象:数据库表存在,数据也有,但 Servlet 或 JSP 里抛 NullPointerException。

原因:业务逻辑里把一个查询结果直接拿了出来,没有做空判断。比如 news 对象为空就调用 getTitle(),或者 DAO 查询时参数占位符数量不对,导致查询失败返回 null。

解决:先在 DAO 层打印日志确认查询返回,再在 Servlet 里对 null 集合做兜底,返回一个空集合而不是 null。止损技巧是给系统加一段统一编码过滤器,虽然不是本题的必须项,但能一次性解决后续新增 Servlet 乱码的问题。

5. 把这份课程设计改造成高分答辩作品:三个加分的扩展方向

系统跑通了,接下来要想的是怎么在答辩或演示时比别人多走一步。三个方向按性价比排序:分页浏览、关键词搜索、后台登录安全。动手改这些,本质是在原有骨架上填肉,不需要重构整体结构。

5.1 给新闻列表加分页组件:从全量查询改成 LIMIT 分页

很多同学的演示里,新闻列表一口气查出所有数据,二十条以内还好,超过五十条页面拖垮。突破点是改 DAO 层的查询方法,加一个分页参数。核心 SQL 是:

SELECT * FROM news ORDER BY publish_time DESC LIMIT ?, ?;

第一个占位符是 offset(偏移量),第二个是 limit(每页条数)。对应地写一个 Page 工具类,封装 pageNo、pageSize、totalCount、totalPages 四个字段。Controller 层接收 pageNo 参数,默认值为 1:

int pageNo = Integer.parseInt(request.getParameter("pageNo") == null ? "1" : request.getParameter("pageNo")); int pageSize = 5; List<news> newsList = newsDao.getNewsByPage(pageNo, pageSize); int totalCount = newsDao.getNewsCount(); int totalPages = (int) Math.ceil((double) totalCount / pageSize); request.setAttribute("totalPages", totalPages); request.setAttribute("currentPage", pageNo);

这里有两个细节要解释:totalPages 的计算用 Math.ceil 向上取整,避免余数页被吞掉;pageNo 从 request 参数取,要处理 null 值否则第一次加载就报空指针。JSP 页面底部放上一页、下一页两个链接,链接里带上 pageNo 参数,一套分页逻辑就通了。

5.2 加一个模糊搜索:用 LIKE 做标题和水润内容检录

新闻系统最实用的功能是搜索,不是点分类。搜索的关键在 DAO 层动态拼接 SQL,但要注意 SQL 注入的边界。常规做法是使用 PreparedStatement 的占位符,而不是直接拼接字符串:

public List<news> searchNews(String keyword) { String sql = "SELECT * FROM news WHERE title LIKE ? OR content LIKE ? ORDER BY publish_time DESC"; try (PreparedStatement ps = conn.prepareStatement(sql)) { ps.setString(1, "%" + keyword + "%"); ps.setString(2, "%" + keyword + "%"); ResultSet rs = ps.executeQuery(); // 遍历组装 news 对象,略 } }

参数说明:LIKE 条件的 % 需要拼在 setString 的参数里,而不是写进 SQL 模板;两张表字段里标题和正文命中优先级不同,如果只想搜标题,去掉 content LIKE 那个条件即可。搜索按键在首页导航栏放一个表单,method 用 get,action 指向 QueryServlet,就能复用现有的 Servlet 入口。

5.3 答辩演示的黄金顺序与一个信息加固习惯

答辩时间通常只有十分钟,演示顺序建议按“用户视角”走:先展示首页新闻列表的加载速度 → 点击一条新闻看详情 → 用搜索查一个关键词 → 进入后台登录 → 新增一条测试新闻 → 返回首页验证新增数据出现。这条链路覆盖了查询、详情、搜索、登录、增删改查五个考核点,比打开代码一行行讲更有说服力。

回到开头那句话:这套系统的难点从来不在技术本身,而在环境一致性和对流程的理解。我从那次帮人排完 8080 端口冲突之后,每次拿到新的 JavaWeb 课程设计,都强制先走一遍数据库导入、驱动包确认、端口检测三个步骤,确认底子干净才碰业务代码。这个过程熟练到变成肌肉记忆就好了。希望帮到你,线上跑通了再来聊怎么加功能。

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

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进