基于JSP+MySQL的个人与家乡展示管理平台开发全解析
简介这套基于 JavaJSP MySQL 的课程设计资源是一个覆盖游客浏览、注册留言到管理员后台维护的完整个人与家乡展示管理平台适合 Java Web 初学者、课程设计或毕业设计学生作为参考和二次开发基础。前端包含欢迎页照片轮播、个人简介、个人技能分类下拉选择、家乡简介、美食美景风土人情特色介绍以及支持回复的留言板后端则提供用户管理、技能管理、家乡特色管理和留言管理管理员可在 Web 界面完成增删改查。资源共 2000 个文件压缩包 29.69MB主要文件包括 48 个 JSP 页面、40 个 Java 类、82 个 JS 脚本、38 个 CSS 样式、1621 个 SVG 图标以及 SQL 初始化脚本页面与后台交互路径完整。后台界面基于 SB Admin 2 管理模板并配套 FontAwesome 图标资源目录结构清晰便于对照学习登录权限、留言回复、数据表设计与增删改查实现。目前已有 288 人学习下载适合希望基于完整项目快速上手 JSPServletMySQL 开发流程的读者。1. 一个 JSP MySQL 的课程设计选题为什么值得认真做完做 Java 方向课程设计和毕设的同学大多已经把“传统 JavaWeb 项目”当成过时产物上来就想套 Spring Boot。但“基于JavaJSPMySQL实现个人与家乡展示管理平台”这类题目恰恰是面试官最常追问的那类项目没有框架替你挡掉细节请求怎么进来、连接怎么打开、SQL 怎么拼、数据怎么渲染全得自己动手。做完它你对 Java Web 的底层链路会有一个完整概念而不是只会在 application.yml 里改端口。这个平台同时包含“对外展示”和“后台管理”两条线能用到的技术点很全JSP 页面渲染、Servlet 接收参数、JDBC 操作 MySQL、Session 会话控制、文件上传与回显。适合用来做课程设计、毕设起点、或者简历上的第一个完整 Java Web 实践项目。2. 先把平台拆成三条线数据模型、目录结构与功能边界2.1 平台不只是一个“网页”而是展示区 管理区两个视图一个个人与家乡展示管理平台如果只做静态 HTML 展示实际上用不到 JSP 和 MySQL。加上“管理”两个字项目才真正有了后端的价值。我一般会把这类平台拆成两个视图面向游客的展示区和面向管理员的后台管理区。展示区包含个人简介、家乡介绍、图片展示、以及留言或联系信息管理区则负责登录、维护个人资料、上传本地图片和更新家乡信息。两块共用同一套数据表只是通过不同路径的 Servlet 和 JSP 来访问。这种拆分的好处是功能边界清晰写代码的时候不会把展示逻辑和写操作糊在一起。新手常见的翻车姿势是把增删改写的 JSP 文件直接放在 web 根目录下游客也能看到管理入口更稳妥的做法是分成/web下的普通页面和一个/admin路径配合后面要讲的登录过滤器做访问控制。管理功能不一定要多登录、修改、上传这三项就能覆盖课程设计的大部分评分点。2.2 建三张表就够用户表、家乡信息表、图片资源表数据库设计是这个项目里最不能跳过的部分。很多人一上来就建七八张表字段多到自己都记不住最后 SQL 联表查得头疼。按这个平台的业务规模我常用的做法是三张核心表用户表存管理员账号家乡信息表存家乡名称、简介、地理标签、美食标签图片资源表存图片路径和图片属于哪条信息。下面是建表 SQL可以直接在 MySQL 里执行CREATE TABLE user ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(100) NOT NULL, nickname VARCHAR(50) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE hometown_info ( id INT PRIMARY KEY AUTO_INCREMENT, title VARCHAR(100) NOT NULL, summary TEXT, content TEXT, tag VARCHAR(50), category VARCHAR(20), create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE image_resource ( id INT PRIMARY KEY AUTO_INCREMENT, info_id INT NOT NULL, img_path VARCHAR(255) NOT NULL, img_desc VARCHAR(255), upload_time DATETIME DEFAULT CURRENT_TIMESTAMP, CONSTRAINT fk_info FOREIGN KEY (info_id) REFERENCES hometown_info(id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;字符集必须用utf8mb4否则 MySQL 5.7 之前的默认utf8存不了 emoji 和部分生僻字外键约束在课程设计里建议保留让评分的人看到你有意识地维护数据完整性。密码字段不要存明文至少用 MD5 或 SHA-256 做一层哈希这个细节经常被作为安全加分点。三张表之间的逻辑是hometown_info记录一条家乡信息image_resource通过info_id挂到某条信息下面一对多。2.3 在 IDEA 里新建传统 JSP 项目的目录结构现在用 IDEA 新建 JSP 项目和早期 Eclipse 时代略有不同但目录本质还是 Java Web 的标准结构。在 IDEA 里选择 New Project注意不要选 Spring Initializr而是选 Jakarta EE 或 Java Enterprise 分类下的 Web Application也可以直接创建普通 Java 项目后手动添加 Web 支持。我一般习惯手动搭建这样对目录结构才真正有感觉hometown-platform/ ├── src/ │ ├── main/ │ │ ├── java/ │ │ │ └── com/example/platform/ │ │ │ ├── servlet/ │ │ │ ├── dao/ │ │ │ └── util/ │ │ └── webapp/ │ │ ├── WEB-INF/ │ │ │ ├── web.xml │ │ │ └── lib/ │ │ ├── index.jsp │ │ ├── hometown.jsp │ │ └── admin/ │ │ ├── login.jsp │ │ └── edit.jspWEB-INF是安全边界放进去的 JSP 不能通过浏览器直接访问必须由 Servlet 转发lib目录放 MySQL 驱动和 JSTL 标签库的 jar 包其余 JSP 是入口页面。新手在 IDEA 里建 JSP 项目时最容易卡住的是没有把webapp目录标记为 Web 资源目录导致 run 的时候找不到页面。在 Project Structure 里确认 Web Resource Directory 指向src/main/webapp再配置一个 Tomcat 就可以启动了。3. 用 JDBC 和 JSP 把“展示”做出来从数据库到页面的完整链路3.1 写一个 JDBC 连接工具类别在每个 Servlet 里重复连接展示功能的第一步是让页面读到 MySQL 数据。很多人会不假思索地在每个 Servlet 里写DriverManager.getConnection()这种做法在小项目里能跑但连接每次都要经过 TCP 握手和 MySQL 认证页面稍多就会变慢。课程设计虽不限性能但代码评审时会看你能不能把公共逻辑抽出来。我一般会写一个DBUtil工具类负责加载驱动和获取连接public class DBUtil { private static final String URL jdbc:mysql://localhost:3306/hometown_db ?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai; private static final String USER root; private static final String PASSWORD your_password; static { try { Class.forName(com.mysql.cj.jdbc.Driver); } catch (ClassNotFoundException e) { throw new ExceptionInInitializerError(e); } } public static Connection getConnection() throws SQLException { return DriverManager.getConnection(URL, USER, PASSWORD); } }连接 URL 里的serverTimezoneAsia/Shanghai是 MySQL 8 的强制要求不加会报时区错误characterEncodingutf8保证中文参数和查询结果不乱码。MySQL 8 用com.mysql.cj.jdbc.DriverMySQL 5.x 用com.mysql.jdbc.Driver这是新手最容易踩红的点。这个工具类不负责连接的关闭关闭动作放在 DAO 层的finally块里做。3.2 用 Servlet 查数据并按需转发JSP 只负责渲染展示页面的经典链路是浏览器请求/hometown→ Servlet 调 DAO 查询hometown_info表 → 把 List 放进 request → 转发到hometown.jsp。这一层分离是 JSP 项目里基本的 MVC 分工不会导致 JSP 页面里写大量 Java 代码。下面是一个最简 ServletWebServlet(/hometown) public class HometownServlet extends HttpServlet { protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { HometownDao dao new HometownDao(); ListHometownInfo list dao.findAll(); request.setAttribute(list, list); request.getRequestDispatcher(/hometown.jsp).forward(request, response); } }WebServlet注解是 Servlet 3.0 以后的功能省去了在 web.xml 里写映射的麻烦forward是服务器内部跳转浏览器 URL 不变数据通过 request 属性传递。这里有个细节不能直接sendRedirect因为重定向会丢失 request 里的数据。接下来在hometown.jsp里用 JSTL 渲染列表% taglib prefixc urihttp://java.sun.com/jsp/jstl/core % % page contentTypetext/html;charsetUTF-8 languagejava % html body h2我的家乡/h2 c:forEach varitem items${list} div classcard h3${item.title}/h3 p${item.summary}/p span标签${item.tag}/span a hrefhometownDetail?id${item.id}查看详情/a /div /c:forEach /body /htmlJSTL 的c:forEach替代了% for(...) {} %这种 scriptlet 写法页面维护起来干净得多。${item.id}调用的是HometownInfo对象对应字段的 getter所以我一般在实体类里写驼峰命名的属性例如createTime映射数据库的create_timegetter 写成getCreateTime()。如果列表查不出来先看控制台有没有 SQL 异常再确认list是否真正放进了 request——熟悉的人一眼就能查完新手经常卡在 null 上。3.3 详情页的图片显示只存路径不存文件本体详情页要展示家乡风景图片这里有一个新手特别容易踩的坑把图片转成 Base64 字符串直接存进数据库。这种做法在小数据量下能显示但图片稍大就会让查询变得很慢而且 JSP 的img标签也会因为超长字符串而变得难以处理。常见的可靠做法是把图片文件上传到服务器的某个磁盘目录数据库只存相对路径页面通过相对路径访问。我会在项目根目录下建一个uploads目录图片上传后用 UUID 重命名用getRealPath获取真实磁盘路径来保存。访问图片时的 URL 由 Tomcat 映射可以直接在 Server 配置里加一段资源映射我的做法是让uploads目录和项目部署目录同级再用 HTTP 路径访问Context docBase/data/hometown-platform/uploads path/uploads /代码层面就一句话request.getServletContext().getRealPath(/uploads)拿到物理路径然后拼接文件名。这条链路里最容易翻车的不是保存而是部署包重新打过后旧图片丢失——如果你用getRealPath保存文件落在 Tomcat 的部署目录里重新部署会被清掉所以实际课程设计中图片保存路径要么写到外部磁盘要么在文档里明确说明“重新部署前需要备份 uploads 目录”。很多同学的图片在答辩演示前一天突然全部 404多半就是这个原因。4. 管理端登录与增删改会话、过滤器和表单提交的完整实现4.1 登录与 Session 会话保持用过滤器拦住未登录请求管理区必须先登录。登录逻辑很经典表单提交用户名和密码到LoginServletServlet 从user表查记录如果密码匹配就把用户 id 写进session.setAttribute(admin, ...)然后重定向到后台首页不匹配就返回登录页并带一个错误提示。这里的关键点是密码比对应该在 Java 端做不要拼在 SQL 里否则团队里讨论时容易被指出存在 SQL 注入风险。SQL 采用预编译写法String sql SELECT * FROM user WHERE username?; PreparedStatement ps conn.prepareStatement(sql); ps.setString(1, username); ResultSet rs ps.executeQuery(); if (rs.next()) { String hashed rs.getString(password); if (hashed.equals(md5(password))) { session.setAttribute(admin, rs.getInt(id)); response.sendRedirect(admin/index.jsp); } }PreparedStatement的?占位符能防止闭合 SQL所有需要登录才能访问的路径用一个 Filter 统一拦WebFilter(/admin/*) public class LoginFilter implements Filter { public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { HttpServletRequest request (HttpServletRequest) req; HttpSession session request.getSession(false); Object admin (session ! null) ? session.getAttribute(admin) : null; if (admin null) { ((HttpServletResponse) res).sendRedirect(request.getContextPath() /admin/login.jsp); } else { chain.doFilter(req, res); } } }getSession(false)不会自动创建新会话避免未登录用户访问时产生无意义的 session。过滤器的目标路径admin/*把整个后台文件夹保护起来不在这个路径下的管理 Servlet 要特别注意路径设计。登录页面本身要放在/admin/login.jsp因为如果放在不受保护的位置任何人都可以直接打开。4.2 信息的新增与修改一个 Servlet 处理两类操作的惯用写法后台管理家乡信息最常见的管理操作是新增和更新。可以让一个EditServlet同时接收新增和更新的请求用表单里隐藏的id字段判断有 id 就是更新没有就是新增。这样做的好处是减少 Servlet 数量逻辑清晰。JSP 里的表单写法很简单form action${pageContext.request.contextPath}/admin/edit methodpost input typehidden nameid value${info.id} input typetext nametitle value${info.title} textarea namecontent${info.content}/textarea button typesubmit保存/button /formServlet 端先统一取参数再根据 id 是否存在决定调insert还是update方法。这里要特别注意 JSP 表达式转义${info.content}是文本域的值如果内容里有/textarea或引号页面会破相甚至被脚本注入稳妥做法是用 JSTL 的c:out标签输出它可以默认做 HTML 转义。我一般把这一步视为“上线前必须检查”的项目直接写textarea${...}/textarea在展示用户可编辑的富文本时并不安全。删除操作实现更直接但一定要做确认提示。我使用的删除链接不是直接跳到 Servlet而是先在管理列表页弹一个 JavaScript 的确认框确认后再提交。删除请求建议用 POST 而不是 GET因为搜索引擎爬虫和浏览器预取可能会导致意外的数据丢失而且课程设计答辩时也更容易解释清楚你考虑了“幂等与安全”这类问题。4.3 表单乱码的根源POST 请求体编码和 Tomcat 配置JSP 项目里最常见的“翻车现场”就是后台新增一条家乡信息存进 MySQL 再查出来中文全变成了“”。这个问题几乎每次都有原因是 POST 请求体的编码默认是 ISO-8859-1而 JSP 页面声明的编码是 UTF-8。常见做法是写一个编码过滤器统一把请求和响应的编码设为 UTF-8WebFilter(/*) public class EncodingFilter implements Filter { public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { HttpServletRequest request (HttpServletRequest) req; request.setCharacterEncoding(UTF-8); res.setCharacterEncoding(UTF-8); chain.doFilter(req, res); } }这个 Filter 要注册在其它 Filter 之前因为getParameter一旦被调用编码就定型了。Tomcat 8 及以上版本对 GET 请求的 URI 编码默认是 UTF-8旧版 Tomcat 需要在server.xml的 Connector 上加URIEncodingUTF-8。如果依然乱码直接从三个位置排查MySQL 连接 URL 是否带characterEncodingutf8、数据库表是否为utf8mb4、JSP 第一行contentType是否设置为UTF-8。记住一个经验乱码是在“请求 → 应用 → 数据库 → 页面”这条链上任何一个环节丢了编码只用一处设置往往不够。5. JSP MySQL 常见问题排查五个必踩的坑5.1 Tomcat 部署后页面全是问号或白屏现象本地 IDEA 运行正常把 war 包丢到 Tomcat 的 webapps 下重启后页面中文全是问号甚至有些页面直接白屏。原因IDEA 里运行 Tomcat 时JVM 默认文件编码通常跟随系统而独立部署的 Tomcat 在 Linux 上默认用 UTF-8在 Windows 老版本里可能用 GBK导致 JSP 编译时字节流读取错误。白屏则常是缺少 JSTL jar 包Tomcat 自带 lib 不包含 JSTL 依赖。解决每个 JSP 文件第一行写完整的page指令把jstl.jar和standard.jar放进WEB-INF/lib。Linux 上可以检查一下tomcat/conf/server.xml中 Connector 是否加URIEncodingUTF-8。我自己的习惯是不依赖 IDE 里“运行”按钮能过就算过每次提交前手动把 war 复制到本地 Tomcat 跑一遍页面源码里看中文字节这一步能提前发现大多数编码问题。5.2 连接 MySQL 8 报 SSL 错误或时区错误现象点击页面时后台报SSLHandshakeException或者The server time zone value Öйú±ê׼ʱ¼ä is unrecognized。原因MySQL 8 默认开启了 SSL 加密且时区规则变了驱动版本和 MySQL 版本不匹配时旧的连接代码直接不认。解决连接 URL 里加参数jdbc:mysql://localhost:3306/hometown_db?useSSLfalseserverTimezoneAsia/ShanghaiallowPublicKeyRetrievaltrueuseSSLfalse是本机开发和课程设计环境里最简单有效的关闭方式allowPublicKeyRetrievaltrue解决 MySQL 8 在非 SSL 模式下缓存公钥的报错。如果有强迫症可以给 MySQL 配置时区而不是靠 URL 参数但相对于学生项目而言URL 参数是最低成本的“后悔药”。5.3 JDBC 驱动加载不到或 ClassNotFoundException现象控制台报java.lang.ClassNotFoundException: com.mysql.cj.jdbc.Driver但mysql-connector-javajar 明明在项目的 External Libraries 里。原因IDEA 下 jar 包必须在部署结构中打进去。很多人只把 jar 加到了项目的 classpath没有把它加入WEB-INF/lib或者 Artifact 的 Output Layout。解决Project Structure → Artifacts → 选中项目 war → 在 Output Layout 里检查是否有WEB-INF/lib把 mysql 驱动和 JSTL 的 jar 通过右边的 “Put into Output Root” 放进去。验证方式是看编译输出目录下WEB-INF/lib/里的文件用jar tf mysql-connector-java-8.x.jar看驱动类是否真实存在。5.4 图片上传后刷新页面 404现象上传图片后第一次能显示刷新一下就裂图了或者重新部署后所有图片消失。原因图片保存路径写进了部署目录内的uploads文件夹。Tomcat redeploy 时会清理旧目录上传的文件随之被清掉还有一种情况是保存时用了相对路径“uploads”但当前工作目录和 Tomcat 的 bin 目录不一致导致图片写到了别的磁盘位置。解决上传的磁盘路径不要挂在项目里。常见做法是在服务器上建一个固定目录比如 Windows 下D:/hometown_uploadsLinux 下/data/hometown_uploads在代码中通过配置文件读取路径。数据库里存的是带 URI 的访问路径例如/uploads/20250101_xxx.jpg然后把 Tomcat 的 Context 映射到那个磁盘目录。这条经验是我自己吃过亏后才固化的习惯。5.5 JSP 页面报错信息不可读项目直接 500现象后台管理页面出现异常后Tomcat 返回一个冗长的 500 页面定位不到是哪一行代码出的问题。原因标准错误输出被 Tomcat 默认页面吞掉了或者项目里把stackTrace打印到了控制台而不是页面日志。解决开发阶段不要依赖错误页直接在doGet/doPost里打印完整堆栈e.printStackTrace();然后在 IDEA 的 Console 里按异常栈逐行移动。进阶策略是给项目加一个简单的错误处理 Servlet把异常信息记录到文件或数据库。课程设计阶段不建议做复杂日志框架一个/logs目录下按天写的文本文件足够用。定位 JSP 页面报错时还有个技巧JSP 编译后的 Java 文件路径会打印在异常信息里可以找到 target 下对应的源码直接看编译产物。6. 从“能跑”到“能演示”分页、校验与打包部署的最后一公里6.1 给展示列表加分页10 条一页SQL 用 LIMIT 实现个人与家乡展示管理平台数据量不大但答辩评分项里常有“是否支持分页展示”这种加分点。常见的做法是给HometownServlet加上page参数DAO 里用 LIMIT 查询页面上放上一页和下一页链接。SQL 写法SELECT * FROM hometown_info ORDER BY create_time DESC LIMIT ?, ?;参数分别是偏移量和每页条数。计算偏移量时注意页码从 1 开始偏移量是(page-1) * pageSize。这个实现不要用LIMIT加逗号的形式拼 SQL 字符串仍然用PreparedStatement。分页时 URL 上要保留查询参数例如hometown?page2避免切页后丢失条件。6.2 给管理表单加前端校验但后端不能只信任校验后台新增家乡信息时表单里的标题、摘要、标签都可能为空。我一般会先在 JSP 页面里写一段 JavaScript 做前置提示阻止空表单提交但这只是体验层面的优化真正判空要在 Servlet 端再做一次否则绕过页面的请求会把脏数据直接写进 MySQL。后端校验可以简化成如下片段if (title null || title.trim().isEmpty()) { request.setAttribute(error, 标题不能为空); request.getRequestDispatcher(/admin/edit.jsp).forward(request, response); return; }这里用trim()过滤了纯空格输入。校验失败时返回编辑页并保留用户已填内容——做法是将请求参数重新放回 request 属性JSP 里再用${param.title}回填。这个小细节比弹一个“提交失败”更有演示价值。6.3 打包 war 部署从 IDEA 的 Artifact 到外部 Tomcat传统 JSP 项目最终要打成 war 包部署到 Tomcat。在 IDEA 里选择 Build → Build Artifacts → 选择hometown-platform:war打包后把 war 复制到 Tomcat 的webapps目录下启动即可。需要注意的是如果 Tomcat 已经存在同名目录停止后删掉旧目录再重新放 war否则会解压出旧文件覆盖新内容这种现象经常被误认为“代码没改生效”。部署完成后访问地址是http://localhost:8080/hometown-platform/如果 404先看 Tomcat logs 里有没有 report 或解压记录如果首页能开但样式丢失大概率是页面里的静态资源路径没有加pageContext.request.contextPath。这个部署流程值得在答辩前完整走三遍尤其是清理旧目录这一步是我自己几次演示翻车后养成的习惯。另外上传图片的外部磁盘目录、数据库连接参数在部署到另一台机器时要同步改我建议把连接参数单独放到一个config.properties里避免每次换环境都改 Java 源码重新编译。做成一个小工具类读配置是这类 JSP 项目性价比最高的收尾改进希望帮到你。本文还有配套的精品资源点击获取