首页 / 资讯中心 / 文章详情

Java Web新闻管理系统实战:数据库设计、分页与部署避坑全解析

Java Web新闻管理系统实战:数据库设计、分页与部署避坑全解析 ★ FEATURED ARTICLE
简介一份大学毕业论文《网站新闻管理系统》的完整设计文档属于计算机专业方向可作为基于JSP和SQL Server 2000开发Web系统的毕业设计参考。文档系统阐述了网站新闻管理系统的总体设计、数据库设计、详细设计与运行效果发布覆盖新闻浏览、新闻录入、新闻修改、删除新闻及管理员身份验证等核心功能模块同时给出新闻文章表、用户表的字段设计并展示登录界面、发布界面、修改与删除模块等关键页面效果有助于快速理解从需求分析、数据库建模到编码实现的完整流程。资源为1个doc文件压缩包大小约461KB篇幅紧凑但结构完整符合毕业论文写作规范。已有96人学习适合需要完成Web类课程设计或毕业论文的在校生参考既能梳理系统开发思路也可为文档结构、表设计和界面说明提供可复用的撰写模板。1. 网站新闻管理系统毕业论文把它做成“能跑起来”的样子比堆功能重要大学毕业设计里的网站新闻管理系统拆开看就是一套内容发布系统前台把新闻按栏目展示给访客后台让管理员登录后发布、编辑、审核、下线新闻。这类课题每年都有大量学生选难点从来不在“功能多不多”而在“能不能在答辩现场稳定跑起来”——数据库连不上、中文乱码、图片不显示、分页点第二页就报错才是最常见的翻车现场。这篇笔记按我实际做过的方案把从需求拆解、表结构设计到编码部署的完整路径讲清楚适合用 Java Web 做毕设、需要快速把系统跑通的从业者和学生参考。2. 先把需求拆干净新闻管理系统的模块边界与数据表设计2.1 新闻管理系统的功能边界游客能看什么、管理员能改什么新闻管理系统的需求一句话概括是“前台只读后台可写”。我在给学生审毕设方案时习惯先把角色和动作列成一张对照表避免后面写代码时把权限逻辑混进业务逻辑。角色可用功能典型页面游客浏览新闻列表、查看新闻详情、按栏目筛选、搜索关键词、发表评论index.jsp、news_list.jsp、news_detail.jsp普通管理员登录后台、发布新闻、编辑自己的新闻、上传封面图admin/login.jsp、admin/news_edit.jsp超级管理员管理普通管理员、管理栏目、审核并上下线新闻、删除不当评论admin/user_list.jsp、admin/category_list.jsp这里有一个容易忽略的点新闻本身要有一个状态字段。很多毕设把新闻表做成“插入即显示”导致后面想加“审核”功能时必须改表。更稳的做法是从第一天就区分草稿、已发布、已下线三种状态答辩时还能顺势讲出“内容审核”这个业务亮点。与常见误用不同前台搜索不要直接去查 content 字段的模糊匹配。新闻内容动辄几千字用LIKE %关键词%在大数据量下会全表扫描。毕设规模下性能不明显但如果你答辩时被问到“搜索怎么做”能说出“先查标题和摘要再查正文或者用全文索引”会显得有思考。别急着上 Elasticsearch新闻管理系统用不上反而会被追问“为什么引入这么重的东西”。2.2 数据表设计五张表把用户、栏目、新闻、评论串起来新闻管理系统最少需要五张核心表用户表、栏目表、新闻表、评论表以及用户-栏目权限辅助表。如果你的选题包含“管理员只能管自己栏目下的新闻”就需要在新闻表上加create_by字段去关联用户表。CREATE DATABASE news_system DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE news_system; CREATE TABLE t_user ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(64) NOT NULL, role TINYINT NOT NULL DEFAULT 1 COMMENT 1普通管理员 2超级管理员, created_time DATETIME NOT NULL ); CREATE TABLE t_category ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(50) NOT NULL, sort_order INT DEFAULT 0 ); CREATE TABLE t_news ( id INT PRIMARY KEY AUTO_INCREMENT, title VARCHAR(200) NOT NULL, category_id INT NOT NULL, summary VARCHAR(500), content TEXT, source VARCHAR(50), status TINYINT NOT NULL DEFAULT 0 COMMENT 0草稿 1已发布 2已下线, view_count INT NOT NULL DEFAULT 0, create_by INT NOT NULL COMMENT 发布人ID, created_time DATETIME NOT NULL, updated_time DATETIME NOT NULL, KEY idx_category (category_id), KEY idx_status_time (status, created_time) ); CREATE TABLE t_comment ( id INT PRIMARY KEY AUTO_INCREMENT, news_id INT NOT NULL, nickname VARCHAR(50) NOT NULL, content VARCHAR(500) NOT NULL, status TINYINT NOT NULL DEFAULT 1, created_time DATETIME NOT NULL, KEY idx_news_id (news_id) );这段 DDL 里有两个容易被新手忽略的参数。utf8mb4不是utf8前者能存下手机号里常见的 emoji 和生僻字后者在插入四字节字符时会直接报错。COLLATE utf8mb4_general_ci里的ci是 case insensitive也就是查询时WHERE usernameAdmin也能匹配admin对登录模块的体验更友好。KEY idx_status_time (status, created_time)是一个联合索引服务于前台最常见的查询按发布时间倒序取已发布的新闻。只对 status 建索引也能跑但加上 created_time 后排序在索引内完成减少 filesort 开销。新闻表的数据量在毕业设计阶段不会很大但这个写法在论文数据库设计章节里能写出一段像样的理由。2.3 权限与状态为什么新闻要分成草稿、已发布、已下线新闻管理系统中状态字段是业务设计的核心。常见的初始化错误是把 status 设为 1已发布后在列表页直接写死WHERE status 1前台倒是正常了后台想恢复一篇已下线的新闻时却无从下手。正确做法是管理员编辑新闻时点击“保存草稿”写 status0点击“发布”写 status1。超级管理员的审核列表默认显示 status0 和 status1 的记录对已发布内容执行“下线”就是 status2。前台查询始终只取status 1后台查询则按状态参数动态拼接条件。-- 前台列表页 SELECT id, title, summary, created_time FROM t_news WHERE status 1 AND category_id ? ORDER BY created_time DESC LIMIT ?, ?; -- 后台审核列表 SELECT id, title, status, create_by, created_time FROM t_news WHERE status IN (0, 1, 2) ORDER BY updated_time DESC LIMIT ?, ?;两条 SQL 的差异就是前后台数据隔离的业务表达。你把这个设计放进论文“系统设计”一节时画一张状态流转图评审老师普遍认可。3. 工程搭建与数据库初始化从新建项目到第一条新闻入库3.1 毕设技术选型JSPServletMySQL 比 Spring Boot 更稳的四个理由新闻管理系统用 JSPServletMySQL 做在答辩场景里有四个实际优势。第一代码结构简单MVC 分层清晰评审老师一看就懂Spring Boot 自动配置太多反而讲不清楚“请求是怎么走完一圈的”。第二调试和部署依赖少Tomcat 解压即用不用处理 Maven 依赖冲突。第三毕业设计评审更看重你是否真的理解实现手写 JDBC 比mybatis-plus自动生成更能回答追问。第四老一代评委对 JSP 有天然熟悉度看到 JSP 页面能快速进入你的项目语境。当然这不是绝对的。如果你导师明确要求 SSM 或 Spring Boot那就按要求做但建议把“为什么导师方案更合适”写进论文背景里。我一般会和学生说选型不是选最火的是选你答辩现场最有把握讲明白的。环境版本建议JDK 1.8 Tomcat 8.5 MySQL 5.7这三个版本组合兼容性最好。Tomcat 9 也问题不大但 Tomcat 10 把javax.servlet换成jakarta.servlet老教程里的包名全部失效别给自己挖坑。3.2 数据库初始化建库建表 SQL 与 JDBC 连接串数据库初始化除了建表更关键的是准备测试数据。很多毕设翻车在“前台点开一张图片都是空的”原因是库里根本没有新闻。初始化脚本我习惯做成这样INSERT INTO t_category (name, sort_order) VALUES (时政要闻, 1), (科技前沿, 2), (文体娱乐, 3); -- 密码这里用 MD5(admin123) 占位实际项目建议用加盐哈希 INSERT INTO t_user (username, password, role, created_time) VALUES (admin, MD5(admin123), 2, NOW()); INSERT INTO t_news (title, category_id, summary, content, status, create_by, created_time, updated_time) VALUES (测试新闻一系统部署完成, 1, 这是摘要, p这是正文内容用于验证前后台通路。/p, 1, 1, NOW(), NOW()), (测试新闻二数据库联调通过, 2, 这是摘要, p分类列表、详情页和评论功能均正常。/p, 1, 1, NOW(), NOW());MD5 在真实项目里已经不够安全但毕业设计用 MD5 是普遍现象。这里建议你在论文“安全性设计”里写“采用加盐哈希”但代码实现可以用 SHA-256避免被评委当场指出 MD5 缺陷。实际上我更推荐在工具类里写一个String password DigestUtils.sha256Hex(input salt)工作量几乎为零讲安全时却多一个亮点。JDBC 连接串是另一个踩坑高发地。用 JDBC 直连时URL 必须写成jdbc.drivercom.mysql.jdbc.Driver jdbc.urljdbc:mysql://localhost:3306/news_system?useUnicodetruecharacterEncodingutf8mb4useSSLfalseserverTimezoneAsia/Shanghai jdbc.usernameroot jdbc.passwordyourpasswordcharacterEncodingutf8mb4要跟在useUnicodetrue后面才生效只写一个characterEncodingUTF-8在 MySQL 5.7 下经常出现“中文写入正常、读出乱码”的怪问题。serverTimezoneAsia/Shanghai不加的话数据库里存的DATETIME取出来容易差 8 个小时这是时区黑匣子别只看“页面显示不对”就跑去改代码。3.3 第一条新闻入库用最简单的 main 方法验证通路项目结构建好后先不写 Servlet写一个带有main方法的 JDBC 测试类把数据源、连接、插入、查询整套链路验证一遍。这样能隔离问题连不上是环境问题插入失败是 SQL 问题都不用带着 Web 容器一起排。public class JdbcSmokeTest { public static void main(String[] args) throws Exception { String url jdbc:mysql://localhost:3306/news_system ?useUnicodetruecharacterEncodingutf8mb4 useSSLfalseserverTimezoneAsia/Shanghai; try (Connection conn DriverManager.getConnection(url, root, yourpassword); PreparedStatement ps conn.prepareStatement( INSERT INTO t_news (title, category_id, summary, content, status, create_by, created_time, updated_time) VALUES (?, ?, ?, ?, ?, ?, NOW(), NOW()))) { ps.setString(1, 冒烟测试新闻); ps.setInt(2, 1); ps.setString(3, JDBC 通路验证); ps.setString(4, p这条记录用于确认数据库连接与写入正常。/p); ps.setInt(5, 1); ps.setInt(6, 1); System.out.println(insert rows: ps.executeUpdate()); } } }注意我用的是PreparedStatement而不是Statement参数通过占位符传进去。这既避免拼接 SQL 时引号转义的手忙脚乱也顺带把 SQL 注入这种安全问题挡在门外。答辩时被问“怎么防 SQL 注入”直接回答“所有参数都走预编译占位”就行。try-with-resources 写法保证了 Connection、PreparedStatement 用完后自动关闭避免连接泄漏。如果你项目里用的是连接池则要养成connection.close()归还连接的习惯而不是真的把连接关掉——很多学生在这里搞混导致池子用完就报 “Too many connections”。4. 核心功能落地新闻发布、分页列表与登录拦截的代码写法4.1 新闻发布从表单到数据库的完整链路注意 XSS 与空值校验新闻发布是后台最核心的操作。常见做法是 JSP 表单提交给一个NewsServletServlet 里做参数校验再调用 Service 和 DAO 写入数据库。这一段代码能完整展示 MVC 分层也是答辩时讲得最多的地方。WebServlet(/admin/news/add) public class NewsAddServlet extends HttpServlet { Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding(UTF-8); String title request.getParameter(title); String content request.getParameter(content); String categoryId request.getParameter(categoryId); String source request.getParameter(source); String status request.getParameter(status); // 空值校验标题和栏目不能为空 if (title null || title.trim().isEmpty() || categoryId null || categoryId.trim().isEmpty()) { response.sendRedirect(request.getContextPath() /admin/news_edit.jsp?error1); return; } News news new News(); news.setTitle(HtmlUtils.htmlEscape(title)); news.setContent(content); news.setCategoryId(Integer.parseInt(categoryId)); news.setSource(source); news.setStatus(Integer.parseInt(status)); news.setCreateBy(((User) request.getSession().getAttribute(loginUser)).getId()); NewsService service new NewsService(); service.addNews(news); response.sendRedirect(request.getContextPath() /admin/news/list); } }这段逻辑里最值得讲的是HtmlUtils.htmlEscape(title)。新闻标题展示在页面时如果原样输出用户提交scriptalert(1)/script就会在别人浏览器里执行这就是 XSS 攻击。标题字段做 HTML 转义正文允许富文本改用白名单过滤后面第 6 章会说是毕设里体现安全意识的好细节。再说一个新手常踩的坑Integer.parseInt(categoryId)如果前端传来的不是数字会抛NumberFormatException页面直接 500。更防御性的写法是包一层 try-catch 并在 catch 里跳回编辑页。虽然“正常操作不会出现”但数据库里万一有残留脏数据后台批量导入时就会炸。我一般会写一个parseIntSafe工具方法解析失败返回-1再统一判空代码可读性也更好。4.2 分页列表LIMIT 偏移量计算与排序稳定性翻到第 2 页就报错怎么排前台列表和后台列表都要分页。最稳妥的分页实现是传入page当前页码和pageSize每页条数SQL 里用LIMIT offset, pageSize其中offset (page - 1) * pageSize。这个公式要刻在脑子里90% 的分页翻车都是这里算错。public ListNews findPage(int categoryId, int page, int pageSize) { String sql SELECT id, title, summary, created_time FROM t_news WHERE status 1 ; ListObject params new ArrayList(); if (categoryId 0) { sql AND category_id ? ; params.add(categoryId); } sql ORDER BY created_time DESC, id DESC LIMIT ?, ?; params.add((page - 1) * pageSize); params.add(pageSize); return queryList(sql, params); }ORDER BY created_time DESC, id DESC是很多人漏掉的细节。同一秒内发布两条新闻时created_time 相同如果只按 created_time 排序MySQL 返回顺序每次可能不一样分页就会出现“第一页有条记录翻到第二页又出现一次”的重复现象。补上id DESC后排序键唯一分页稳定。这个坑我在实际项目里见过不止一次排查了很久才发现不是代码问题而是排序稳定性问题。分页还要同时查总条数以计算总页数。总条数查询用SELECT COUNT(*) FROM t_news WHERE status 1不要为了省一条 SQL 把COUNT(*)也写在主查询里MyBatis 之外的原始 JDBC 下分页 SQL 和统计 SQL 分开是标准做法。总页数计算公式是totalPages (totalCount pageSize - 1) / pageSize这个公式避免totalCount / pageSize整除后丢掉余数导致最后一页无数据。4.3 登录拦截Filter 拦截后台 URL 的三种实现与 session 失效处理后台所有/admin/*路径都必须要求登录。用 Servlet 过滤器实现是标准做法比在每个 Servlet 里复制粘贴“是否登录”判断代码干净得多。WebFilter(urlPatterns {/admin/*}) public class LoginFilter implements Filter { Override public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { HttpServletRequest request (HttpServletRequest) req; HttpServletResponse response (HttpServletResponse) res; HttpSession session request.getSession(false); User loginUser session null ? null : (User) session.getAttribute(loginUser); // 放行登录页本身和登录提交动作 String uri request.getRequestURI(); if (uri.endsWith(/admin/login.jsp) || uri.endsWith(/admin/login)) { chain.doFilter(request, response); return; } if (loginUser null) { response.sendRedirect(request.getContextPath() /admin/login.jsp); return; } chain.doFilter(request, response); } }注意request.getSession(false)的false参数拿不到 session 时返回 null而不是强制新建一个 session。这是防 Session 伪造的细节也能避免每次未登录请求都创建一个无意义的 session 对象。同理登录成功后才request.getSession().setAttribute(loginUser, user)。三种写法指的是注解式WebFilter、纯 web.xml 配置、和 Spring 时代的Interceptor。在纯 JSPServlet 项目里WebFilter最方便但如果你用 Tomcat 8.5 以下版本WebFilter有时不被识别稳妥做法是在web.xml里声明filter filter-nameLoginFilter/filter-name filter-classcom.news.admin.filter.LoginFilter/filter-class /filter filter-mapping filter-nameLoginFilter/filter-name url-pattern/admin/*/url-pattern /filter-mappingweb.xml 方式还能显式控制过滤器顺序比如把字符集过滤器和登录过滤器排在一起字符集过滤器必须在所有业务过滤器之前否则request.setCharacterEncoding不生效。很多“后台新增的新闻标题全是乱码前台却正常”的诡异现象就来自过滤器顺序颠倒。5. 新闻管理系统避坑实录5 个高频故障的现象、原因与解法5.1 数据库中文全变成“????”或控制台乱码现象新闻标题插入成功后MySQL 里显示的是四个问号或者页面上读出来变乱码。原因分三层建库时字符集不是 utf8mb4JDBC 连接串少了useUnicodetruecharacterEncodingutf8mb4写入请求没有执行request.setCharacterEncoding(UTF-8)。三层中任何一层不对中文都传不到底。检查时按这个顺序逐个看哪一层都没问题再用SHOW CREATE TABLE t_news确认表级字符集因为有时库是 utf8mb4表却被某些工具建成了 latin1。解决把建表 DDL 里ENGINEInnoDB DEFAULT CHARSETutf8mb4写全连接串补参数所有接收参数的 Servlet 第一行写request.setCharacterEncoding(UTF-8)。如果已经写脏了数据用UPDATE t_news SET title CONVERT(CONVERT(title USING latin1) USING utf8mb4)这类转换恢复但恢复前一定先备份。5.2 图片上传成功页面却显示裂图现象后台点击上传封面图提示成功数据库里也有图片路径前台img标签却一直转圈或显示小裂图。原因图片被传到磁盘某个绝对路径下比如D:/upload/xxx.jpg而 JSP 页面里的src/upload/xxx.jpg是相对于 Tomcat 部署根目录解析的两个路径完全不在一起。浏览器访问http://localhost:8080/upload/xxx.jpg时Tomcat 找不到该资源就返回 404。解决两种方案任选。第一种是把上传目录放到 Web 应用根目录下比如webapp/uploadIDE 里能看到部署时随项目一起打包缺点是重部署会丢图。第二种是给 Tomcat 配虚拟目录conf/server.xml的 Host 节点里加Context docBaseD:/upload path/upload /让/upload映射到磁盘目录。第二种更接近真实生产环境答辩时还可以讲“图片资源与代码分离”。上传文件时务必用UUID.randomUUID()重命名文件避免两个用户上传同名的a.jpg互相覆盖。5.3 分页点击第 2 页报错或首页删除最后一条后列表空白现象列表页第一页正常点第 2 页 URL 变成list?page2但页面 500或者在最后一页删掉唯一一条数据后当前页没数据了。原因500 多半是page参数从前端传到 Servlet 时是字符串Integer.parseInt遇到空值或非数字抛异常。另一个隐蔽原因是用 POST 提交翻页刷新页面时浏览器弹出“确认重新提交”用户点取消后把空参数传给 Servlet。列表空白则是没有做“当 page 大于总页数时回退到最后一页”的保护。解决接收page时做防御int page parseIntSafe(request.getParameter(page), 1);解析失败就用默认值 1查询总页数后page Math.min(page, totalPages);让边界情况自动收敛。翻页建议改成 GET 请求顺带把查询条件拼进 URL这样刷新、收藏、分享都不会丢参数。5.4 富文本内容提交后p标签被删光样式全丢现象后台用富文本编辑器填写正文带颜色、加粗的段落保存后前台显示时所有 HTML 标签被剥掉变成纯文本挤在一起。原因为了防 XSS有的同学会对 content 也做HtmlUtils.htmlEscape富文本里所有标签都会被转义成lt;pgt;浏览器表现出“标签被删除”。这是把“防 XSS”和“富文本展示”两个需求混在一个处理逻辑里了。解决标题、摘要这类纯文本字段做转义正文用白名单过滤只允许p、br、strong、img等安全的排版标签禁止script、iframe、a hrefjavascript:...。可以引入jsoup的Jsoup.clean(html, Safelist.relaxed())一行搞定白名单机制既保留排版又去掉危险代码比粗暴转义专业得多。5.5 部署到服务器后一直报ClassNotFoundException: com.mysql.jdbc.Driver现象本地 IDEA 里跑得好好的把 war 包丢到服务器 Tomcat 后启动连数据库就报找不到驱动类。原因本地开发时IDEA 会把mysql-connector-java.jar加到 classpath打包 war 时如果把 jar 放在WEB-INF/lib里问题是小概率经常翻车的是把驱动 jar 放到 Tomcat 的 lib 目录后与 war 包内置的版本冲突。报ClassNotFoundException通常是 jar 根本不在运行时 classpath 里。解决把mysql-connector-java.jar放进webapp/WEB-INF/lib随项目打包最省心。另外检查驱动类名MySQL 8.x 的驱动类名是com.mysql.cj.jdbc.DriverMySQL 5.7 用com.mysql.jdbc.Driver两个版本搞混就会“明明有 jar 却找不到类”。用 Maven 构建时在 pom.xml 里给 mysql-connector-j 设置scoperuntime/scope。排查时进入 Tomcat 的webapps/项目名/WEB-INF/lib目录ls看一眼 jar 是否真的在这比看报错日志更快。6. 答辩加分项把新闻管理系统做成能演示、能部署、能说清的作品6.1 部署到外网服务器用宝塔面板和 Tomcat 跑通整个链路如果答辩现场用的是你自己的笔记本一旦现场 Wi-Fi 抽风或投影仪不给力演示就会很被动。更稳的做法是提前把系统部署到一台云服务器上答辩时打开浏览器输入公网地址直接演示。常见做法是用宝塔面板搭建网站安装 Nginx 反代到 Tomcat 8080 端口或者直接把 Tomcat 的 webapps 目录指到项目路径。部署顺序我一般建议这样先在服务器装 JDK 1.8 和 Tomcat 8.5保证curl localhost:8080能看到 Tomcat 默认页再把本地打好的 war 包丢进webapps启动后访问http://服务器IP:8080/项目名/。如果要用域名就在宝塔里新建站点并把proxy_pass指向127.0.0.1:8080。整个链路里最容易失败的是服务器安全组没放行 80/443/8080 端口现象是所有外部请求都超时但在服务器本机却访问正常这属于典型的“外网不通、内网通”先查防火墙而非项目配置。6.2 答辩前自查三分钟演示脚本和三句必背的技术口径演示时不要开一堆代码页按这条脚本走打开首页点一个栏目进详情页发一条评论切到后台登录发布一条带图片的新闻回到前台确认新新闻出现在列表顶部再演示删除这条新闻。整个过程控制在三分钟内评委问数据的功夫你已经把“增删改查登录评论分页”全演示完了。接下来准备三个回答口径第一“分页怎么实现”——说清offset (page-1)*pageSize和ORDER BY created_time DESC, id DESC的排序稳定性。第二“如何防注入”——“所有 SQL 用 PreparedStatement 预编译占位”。第三“你说一下路由流程”——“浏览器请求 → Tomcat 匹配 Servlet → Servlet 调 Service → DAO 操作 MySQL → 数据回填 JSP 渲染”。这三句背熟比把整个项目代码背下来有用得多。回想我自己带过的毕设里翻车的几乎都不是代码写不出来而是没提前把部署环境跑通、没设计好演示顺序。这个新闻管理系统本身不难但它值得你花两小时把部署链路完整走一遍给数据库写足测试数据给每张表截图放进论文。这些都是答辩时最直接的后悔药。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?
咨询建站