ARTICLE DETAIL

资讯详情

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

JSP+MySQL实战:搭建个人与家乡展示平台全流程指南

JSP+MySQL实战:搭建个人与家乡展示平台全流程指南 简介基于JavaJSPMySQL实现的个人与家乡展示管理平台面向Java Web课程设计及初学者提供前后端完整源码与建库脚本。平台功能覆盖前端展示与后端管理双端前端包括首页照片轮播、个人简介、个人技能分类管理科研、学习成绩、开发技能、获奖等、家乡简介、特色介绍美食、美景、风土人情以及支持回复的游客留言板后端支持管理员与游客用户管理、技能信息增删改查、家乡特色内容管理及留言管理适合作为课程设计或毕业设计的参考模板。资源共2000个文件以JSP页面、Java源码、SQL脚本、CSS样式、JavaScript脚本及大量SVG图标为主压缩包大小29.69MB便于直接部署和二次开发。已有289人学习下载。通过该资源可快速搭建一套可运行的展示管理平台从中理解MVC分层、数据库交互、会话管理、文件目录组织等Java Web核心知识同时可按需替换图片与文案定制个人化展示页面。1. 这不是又一个“增删改查”JSPMySQL做个人与家乡展示平台值不值个人与家乡展示管理平台乍看是一个典型的课程设计题目但真动手做过的人都知道这类项目恰恰是 JavaWeb 里最完整的“小麻雀”展示页、后台管理、登录拦截、图片上传、数据库设计全都有。早几年我帮人远程调试过很多次类似项目最常见的翻车点不是功能写不出来而是 Tomcat 版本不匹配、MySQL 驱动没导对、图片路径死活加载不出。如果你正在准备 JavaWeb 课程设计或者想用最少成本搭一个个人与家乡宣传展示站这篇实战笔记会从选型、建库到部署踩坑把 JSP MySQL 这条经典路线完整走一遍。2. 技术选型与项目结构JSPServletMySQL 组合的真实边界2.1 为什么 JSP 在今天仍是课程设计与中小型展示站的最短路径Spring Boot 早已统治企业级 Java 后端但搜索 Java 开发岗位面试题时你会发现JSP 和 Servlet 依然是绕不开的基础。对“个人与家乡展示管理平台”这类项目JSP 有三个不可替代的优势第一JSP 页面天然支持 Java 脚本嵌入数据从 MySQL 查出来直接用 JSTL 或表达式输出不需要额外引入 JSON 库与前端框架前后端联调成本几乎为零第二JSP 的 include 指令能让头部、导航、页脚在多个页面间复用而家乡展示平台恰好由“景点介绍、美食推荐、文化历史、后台管理”这些结构相似的页面组成第三部署成本低一个 WAR 包放进 Tomcat 的 webapps 目录就能运行不依赖 Node.js 或 Nginx 构建链路。但这个组合的边界也要说清楚它扛不住高并发做不了复杂的前后端分离也不适合多人协作的大型工程。它真正擅长的是“一个人花两三天写完、跑在一台小服务器上、每天几百到几千访问量”的展示型站点。如果你预期未来要接入支付、要对抗大规模流量那请直接上 Spring Boot 与 MyBatis本文这套方案更适合当下快速落地、先拿下项目验收的场景。做选型时不要因为 JSP 老就觉得它没用老技术能活到今天是因为它解决旧问题依然最省力。2.2 项目分层从 JSP 页面到 MySQL 的连接路径我一般会按三层结构组织工程层次对应位置核心职责表现层WebContent 下的 jsp 目录负责数据展示、表单提交、超链接跳转控制层src 下的 controller / servlet 包接收请求、调用业务方法、设置转发或重定向数据层src 下的 dao / util 包JDBC 连接 MySQL、执行增删改查、封装数据模型一次完整的请求链路是JSP 页面里的超链接或表单提交到 Servlet 的 doGet 或 doPost 方法Servlet 调用 DAO 层封装的查询方法DAO 内部通过 JDBC 执行 SQL把 ResultSet 转换成 Java 对象再通过 request.setAttribute 把数据传回 JSP 渲染。这个流程看起来直接但我在实际调试中见过不少同学把所有数据库操作全部写在 JSP 页面里一个项目有几十个页面就散落几十处 SQL最后查问题时如同面对一个黑匣子不知道从哪里改起。所以无论如何请至少把数据库连接抽成一个 DBUtil 工具类把针对家乡信息表和用户表的操作按 DAO 模式独立出来。Servlet 里哪怕直接 new 一个 DAO 对象也没关系关键在于层次分明。这样后面出任何问题你都能准确地定位到表现层还是数据层不至于被自己的代码绕晕。2.3 用 Maven 还是不用 Maven两种建工程方式的取舍建工程时你首先会面临一个选择Maven 工程还是普通动态 Web 工程。Maven 的好处是依赖管理省心pom.xml 里加一行依赖就能下载 MySQL 驱动和 Servlet API换机器跑时能一键恢复环境缺点是它要求你理解坐标概念且首次构建需要连中央仓库网络不好时会卡很久。普通动态 Web 工程的好处是 Eclipse 或 IDEA 里新建项目即可把 mysql-connector-java 的 jar 直接丢进 WEB-INF/lib 文件夹就能用缺点是如果项目要交给别人运行对方还得手动找 jar 包版本对不上就等着 ClassNotFoundException 吧。我的个人习惯是只要目标环境能上网优先建 Maven 工程。哪怕你不会写复杂的 pom 配置只引入 mysql-connector-java 和 jsp-api 两个依赖就足够启动。IDEA 对 Maven 的支持很成熟创建时选 Web 骨架即可。但如果你完全没接触过 Maven且时间只剩两三天那就老老实实用普通动态 Web 工程别在项目结构上冒险。两条路我都带人走过最终跑起来效果一样差别只在于交付给别人时“能否一键重建环境”。3. 数据库设计家乡展示平台的表结构与 MySQL 关键配置3.1 三张核心表用户、家乡文化条目与图片资源的字段设计先确定数据模型。这个平台本质上分两块访客在展示区浏览家乡的风土人情管理员在后台维护这些内容。所以表的数量不宜多三张核心表足够管理员表用于后台登录家乡信息表存放景点、美食、文化等条目图片资源表存展示图片的路径。三者通过外键关联。字段设计上有几个细节容易踩坑我直接说结论第一每张业务表都要带 create_time 和 update_time 字段。你可能觉得现在用不上但首页一旦需要按发布时间倒序排列你要用的就是 MySQL 排序能力ORDER BY create_time DESC。第二给“点击量”这类数值字段设置默认值为 0用 MySQL 的DEFAULT 0语法这样插入数据时不传这个字段也不会报错。我们常说 mysql 操作要严谨默认值就是让你少犯错的第一道防线。第三图片字段不要存二进制 Base64而是存文件路径文件本身放服务器磁盘否则数据库体积会快速膨胀备份也痛苦。3.2 字符集与时区utf8mb4 和 serverTimezone 的一次性配置mysql 安装教程看再多不如真正踩一次中文乱码坑。多数人装完 MySQL 后不会去检查默认字符集如果库是 latin1 或 utf8mb3中文字符存储和读取时就会出问题。我在建库时会明确指定CREATE DATABASE IF NOT EXISTS hometown_db DEFAULT CHARACTER SET utf8mb4 DEFAULT COLLATE utf8mb4_unicode_ci;utf8mb4 是完整支持四字节 UTF-8 的字符集emoji 表情也能存下。另一个和编码同样重要的配置是时区。MySQL 8.0 连接时如果不加 serverTimezone驱动会因为无法解析服务器时区而抛异常。同时 SSL 握手也可能导致连不上解决方式是在 JDBC 连接串尾部追加这样一段参数?useSSLfalseallowPublicKeyRetrievaltrueserverTimezoneAsia/ShanghaiuseSSLfalse关闭 SSL 加密握手allowPublicKeyRetrievaltrue是允许客户端从服务器获取公钥完成认证。这两个参数缺一个MySQL 8.0 的 caching_sha2_password 认证方式都可能让你卡在“mysql ssl 连接错误”上。这段参数配一次后面所有 DAO 类里的连接都能复用属于一次配置长期受益的典型操作。3.3 初始化 SQL 脚本带外键与默认值的落地示例建库建表的整个初始化脚本可以这样写直接复制到 mysql 命令行或 Navicat 里执行USE hometown_db; -- 管理员表后台登录用 CREATE TABLE admin ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(64) NOT NULL, create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB; -- 家乡信息表景点、美食、文化共用一张表category 区分 CREATE TABLE hometown_info ( id INT PRIMARY KEY AUTO_INCREMENT, title VARCHAR(100) NOT NULL, category VARCHAR(20) NOT NULL COMMENT scenic/food/culture, content TEXT, cover_image_id INT, view_count INT DEFAULT 0, sort_order INT DEFAULT 0, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, CONSTRAINT fk_cover_image FOREIGN KEY (cover_image_id) REFERENCES hometown_image(id) ) ENGINEInnoDB; -- 图片资源表只存路径不存二进制 CREATE TABLE hometown_image ( id INT PRIMARY KEY AUTO_INCREMENT, home_info_id INT, image_path VARCHAR(255) NOT NULL, position_x DOUBLE DEFAULT 0, position_y DOUBLE DEFAULT 0, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, CONSTRAINT fk_image_hometown FOREIGN KEY (home_info_id) REFERENCES hometown_info(id) ) ENGINEInnoDB;这段脚本中有几个细节值得注意。category字段用 varchar 而不是 int是方便 JSP 页面上直接用字符串做分类判断省掉一层映射。sort_order INT DEFAULT 0和view_count INT DEFAULT 0就是所谓的 mysql 设置默认值为 0 的标准写法这样 insert 语句里可以不传这个字段。外键的依赖顺序也关键图片表先建家乡信息表后建因为 hometown_info 的 cover_image_id 要引用 hometown_image 的 id。如果你先建信息表MySQL 会直接报“无法添加外键约束”的错误。还有ON UPDATE CURRENT_TIMESTAMP是 MySQL 自动在数据更新时刷新时间戳的机制这样 Java 代码里不用手动维护 update_time 字段。4. 核心功能实现登录拦截、内容管理与图片上传4.1 数据库连接工具类从 DriverManager 到连接池的演进数据层的第一步是写一个工具类负责给 DAO 提供连接。最简单的版本是这样package util; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; public class DBUtil { private static final String URL jdbc:mysql://localhost:3306/hometown_db ?useSSLfalseuseUnicodetruecharacterEncodingutf8 serverTimezoneAsia/ShanghaiallowPublicKeyRetrievaltrue; private static final String USER root; private static final String PASSWORD 123456; static { try { Class.forName(com.mysql.cj.jdbc.Driver); } catch (ClassNotFoundException e) { throw new RuntimeException(MySQL驱动加载失败请检查jar包是否放入WEB-INF/lib, e); } } public static Connection getConnection() throws SQLException { return DriverManager.getConnection(URL, USER, PASSWORD); } }这段代码里有两个关键点你需要知道。第一驱动类名在 MySQL 5.x 时代是com.mysql.jdbc.Driver到了 MySQL 8.0 改成了com.mysql.cj.jdbc.Driver。如果你用的新版驱动却写旧类名加载时会报 ClassNotFoundException。第二useUnicodetruecharacterEncodingutf8解决的是中文传输编码问题和建表时指定的 utf8mb4 是两回事——前者管 JDBC 连接后者管存储字符集。这个工具类可以支撑整个项目跑通但如果你想把连接管理做得更好一点常见做法是引入连接池。开发环境用 DriverManager 即可生产部署再考虑 DBCP 或 Druid。连接池的本质是复用连接而不是每次新建MySQL 频繁建连的开销在并发情况下会被放大。课堂项目里用 DriverManager 不会成为瓶颈但你要知道这条路的存在免得面试被问到时一无所知。4.2 登录与 Session 拦截Filter 控制后台访问权限后台管理页面不能任由访客访问必须登录后才能操作。我的做法是写一个 Filter 统一拦截/admin/*路径package filter; import javax.servlet.*; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import java.io.IOException; public class LoginFilter implements Filter { Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest req (HttpServletRequest) request; HttpServletResponse resp (HttpServletResponse) response; HttpSession session req.getSession(false); if (session ! null session.getAttribute(adminUser) ! null) { chain.doFilter(request, response); } else { resp.sendRedirect(req.getContextPath() /login.jsp); } } }这段代码里最值得讲的是req.getSession(false)。参数传 false 时如果当前请求没有绑定 Session容器不会新建而是返回 null如果不传参数容器会为每个被拦截的请求都创建一个新 Session你的拦截器就失去意义了。登录成功后在登录 Servlet 里写一行session.setAttribute(adminUser, username)即可放行。Filter 配置到 web.xml 时要注意 URL 匹配规则filter filter-nameloginFilter/filter-name filter-classfilter.LoginFilter/filter-class /filter filter-mapping filter-nameloginFilter/filter-name url-pattern/admin/*/url-pattern /filter-mapping/admin/*和/admin是两种完全不同的匹配。前者匹配 admin 下的所有子路径后者只匹配 admin 这一个精确路径。如果只写/admin你访问/admin/list.jsp时会发现拦截器完全没生效。这是我调试时遇到最多的低级错误但也是最容易检查出来的——页面直接能在无登录状态下打开说明 Filter 映射范围错了。4.3 图片上传与坐标定位用 Java 处理文件路径与展示位置“jsp 图片如何对坐标定位”这个搜索词背后其实是两类困惑第一类是图片怎么上传到服务器并保存路径第二类是怎么让图片在页面上显示到指定位置。传统的 JSP 项目做法是先把文件写入服务器磁盘再把相对路径存进数据库页面里用img src${imagePath}输出。路径的存储格式要设计统一我一般存/uploads/2025/05/xxx.jpg不写主机名和端口这样换部署环境时不需要改数据库里的数据。文件上传的 Servlet 核心逻辑里有一个关键参数DiskFileItemFactory factory new DiskFileItemFactory(); ServletFileUpload upload new ServletFileUpload(factory); upload.setFileSizeMax(5 * 1024 * 1024); // 单个文件最大 5MB upload.setSizeMax(20 * 1024 * 1024); // 请求体总大小最大 20MB文件写盘后需要把路径和图片元数据一起写入 hometown_image 表。坐标字段 position_x 和 position_y 在这个表里设计的意图是如果你要在“家乡地图”背景上定位多个地标图片这两个字段就存经纬度或相对坐标。前端拿到后通过 CSS 的 transform 或定位属性把图片渲染到指定位置。如果你的场景只是普通图文混排站点结构与内容展示没那么复杂坐标字段可以直接不用但表结构预留总比后补好。4.4 家乡条目 CRUDJSP 页面 Servlet 的完整闭环以“新增家乡条目”为例完整链路是JSP 表单提交到 ServletServlet 解析请求并保存内容然后调用 DAO 插入数据库最后重定向回列表页。表单页面的关键点在于 enctypeform action${pageContext.request.contextPath}/admin/info/add methodpost enctypemultipart/form-data input typetext nametitle placeholder条目标题 required select namecategory option valuescenic景点/option option valuefood美食/option option valueculture文化/option /select textarea namecontent rows10 placeholder介绍内容/textarea input typefile namecoverImage acceptimage/* button typesubmit发布/button /form一旦表单里加了 file 输入框enctypemultipart/form-data是必须的。否则文件上传控件只会提交文件名文件内容根本不会传输。但加了这个属性后Servlet 里不能再直接用request.getParameter(title)取普通字段了因为业务请求体被 multipart 重写了。你需要用 ServletFileUpload 把整个请求体解析成 FileItem 列表再逐一判断是普通字段还是文件protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding(UTF-8); try { ListFileItem items new ServletFileUpload(new DiskFileItemFactory()) .parseRequest(request); String title null, category null, content null; FileItem fileItem null; for (FileItem item : items) { if (item.isFormField()) { String name item.getFieldName(); String value item.getString(UTF-8); if (title.equals(name)) title value; if (category.equals(name)) category value; if (content.equals(name)) content value; } else { fileItem item; } } String imagePath saveFile(fileItem, request); HometownInfoDao dao new HometownInfoDao(); boolean ok dao.insert(title, category, content, imagePath); if (ok) { response.sendRedirect(request.getContextPath() /admin/info/list); } else { response.sendError(500, 数据插入失败); } } catch (Exception e) { throw new ServletException(e); } }这里有三个坑必须要提醒你。第一item.getString(UTF-8)必须显式传编码参数否则 FileItem 默认按 ISO-8859-1 解码中文全部变乱码。第二response.sendRedirect是重定向浏览器会发起第二次请求地址栏变更为目标路径而request.getRequestDispatcher().forward是转发地址栏不变。改完数据后用重定向可以避免 F5 刷新时表单重复提交。第三DAO 层插入时如果用到事务比如同时插入家乡表记录和图片表记录必须在同一个 Connection 上手动开启事务提交成功才 commit否则回滚。JSPMySQL 项目里最常见的“数据对不上”问题九成是事务没控制好。5. 部署避坑与常见问题排查Tomcat、JDK 与 MySQL 版本配套5.1 现象启动时数据库连不上报 SSL 连接错误或 Public Key Retrieval 异常用 MySQL 8.0 加 JDBC 8.0 的组合时第一次连接数据库经常报SSL connection error或者Public Key Retrieval is not allowed。原因MySQL 8.0 默认启用 caching_sha2_password 认证而 JDBC 驱动默认尝试和服务器做 SSL 握手本地开发环境没有受信任的证书就会失败同时客户端要获取服务器公钥默认策略里没有授权。解决在 JDBC URL 末尾追加useSSLfalseallowPublicKeyRetrievaltrueserverTimezoneAsia/Shanghai。很多包含 mysql 安装教程的文章不会提这一步因为 5.7 版本默认不需要这三个参数这是切换 8.0 之后最容易翻车的点。如果你是第一次在一个全新环境部署建议先在命令行或 Navicat 里确认能连上数据库再启动项目这样能快速区分是数据库本身问题还是驱动连接问题。5.2 现象页面从 MySQL 查出的中文显示为问号这个坑的判断路径有先后顺序。先在 Navicat 或命令行执行select * from hometown_info;看数据库存的值是否正常。如果数据已经是乱码说明建库或建表时字符集不是 utf8mb4执行ALTER TABLE hometown_info CONVERT TO CHARACTER SET utf8mb4;补救。如果数据库正常但页面上是问号那就是连接或渲染层出了问题。检查三处JSP 页面头部有没有% page contentTypetext/html;charsetUTF-8%Servlet 的 doPost 开头有没有request.setCharacterEncoding(UTF-8)DBUtil 的 URL 里有没有characterEncodingutf8。编码有三处要统一就是 JSP 页面、HTTP 请求、MySQL 连接少一处中文就断一处。我自己还遇到过一种更隐蔽的情况SQL 脚本文件本身是 GBK 编码Navicat 导入时识别错误导致数据源头就是坏的。这种只能重建数据。5.3 现象上传的图片在列表页显示不出来直接 404404 意味着浏览器请求的 URL 没有对应的资源。最典型的错误是Java 代码把图片写到了磁盘绝对路径如D:/uploads/xxx.jpg页面上 img 标签的 src 却是/uploads/xxx.jpgTomcat 根本没把磁盘目录映射成虚拟路径。解决在 Tomcat 的 conf/server.xml 的 Host 节点下增加一行虚拟目录映射Context docBaseD:/uploads path/uploads /这段配置的意思是当浏览器请求/uploads/xxx.jpg时Tomcat 去D:/uploads目录找文件并返回。如果你是直接改工程内部路径另一种常见做法是用request.getServletContext().getRealPath(/)获取部署根目录然后把文件写到根目录下的 uploads。要注意的是IDEA 集成 Tomcat 运行时getRealPath 返回的可能是 Tomcat 解压临时目录服务器重启后文件可能被清空所以像图片这种持久化资源配置磁盘虚拟路径才是正解。5.4 现象页面加载完总要手动刷新一次才能看到最新内容这个现象看起来像玄学实际是浏览器缓存了同一 URL 的页面。你重定向回列表页时 URL 没有变化浏览器直接返回了缓存内容只有手动刷新才会重新请求服务器。解决思路有两种一是在 Servlet 重定向后面加时间戳参数让每次 URL 都不同这是我推荐的做法response.sendRedirect(request.getContextPath() /admin/info/list?t System.currentTimeMillis())二是在 JSP 页面 head 区域加meta http-equivCache-Control contentno-cache meta http-equivPragma contentno-cache如果你搜索过“jsp 页面让加载完后刷新一次”看到的大多数答案是用 JavaScript 的location.reload()但那只是浏览器端强制刷新治标不治本。正确的方向是让服务器告诉浏览器页面已过期或者让 URL 变化以绕过缓存。首页展示内容更新后同样存在这个问题所以列表和详情页的重定向路径都可以加上时间戳参数。6. 从课程设计到可维护工程把展示平台升级为真实可用系统的三个方向跑通项目只算完成了一半。如果这个平台需要长期运行或者你想在答辩时多讲几句有深度的内容我建议从三个方向进阶。第一个方向是把 JDBC 替换成 MyBatis。现在的 DAO 里有大量重复的 PreparedStatement 和 ResultSet 处理代码换用 MyBatis 后 SQL 写在注解或 XML 里映射规则由框架自动处理。这个改造的核心不是换依赖而是把你“先写 SQL 再手动封装”的思维换成“先定义实体再映射字段”。它的额外收益是你的代码里少了很多 try-catch读起来像业务文档。第二个方向是图片存储服务化。本地磁盘方案足够应付课程设计但真实场景里磁盘扩容和备份成本会随着图片数量膨胀。让你的上传 Servlet 对接对象存储服务上传成功后拿到公网 URL 再存数据库JSP 展示时只需输出完整 URL。改造时要注意历史数据里存的是相对路径需要写一次性脚本在 URL 前补全域名前缀否则老图片全部失效。第三个方向是页面展示层的性能优化。家乡展示平台是图片密集型页面首屏加载速度很大程度取决于图片体积。我会在服务端生成缩略图上传原图的同时压缩一份宽度 800px 的版本列表页显示缩略图详情页显示原图。用 Java 的 ImageIO 可以实现但要注意 JPEG 压缩时会丢失 EXIF 信息手机竖拍的照片可能会旋转 90 度。这类细节往往只有实测才能发现所以做完优化后一定要用不同尺寸的图片验证展示效果。我个人的习惯是每完成一个功能模块就手动走一遍完整的用户路径从列表页点进详情页再退出重进确认数据、图片和跳转都没有问题再提交代码。这套看似原始的冒烟测试帮我避开了很多次“功能写完但部署即翻车”的尴尬。项目不怕简单怕的是可用性没有保障。这套 JSPMySQL 方案如果表结构设计合理、编码统一、路径明确它完全能支撑起一个真实运行的家乡展示平台。希望这篇实践笔记能让你少踩几个坑项目顺利跑起来。希望帮到你。本文还有配套的精品资源点击获取
返回列表