尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Java Web课程设计:基于JSP+Servlet+MySQL的个人家乡平台开发实战

Java Web课程设计:基于JSP+Servlet+MySQL的个人家乡平台开发实战 简介基于JavaJSPMySQL实现的个人与家乡展示管理平台面向高校Java Web课程设计、毕业设计及入门学习者。前端提供欢迎页照片轮播、个人简介、技能管理、家乡简介、特色介绍与留言板后端覆盖用户管理、技能信息增删改查、家乡美食美景风土人情管理以及留言回复功能游客可注册留言管理员可统一管理整体结构清晰适合作为Web开发综合练习或课设蓝本。资源共2000个文件压缩包29.69MB以SVG图标、SCSS样式、JSP页面、Java类、JavaScript脚本和CSS样式为主辅以字体文件、配置文件及SQL数据库脚本便于直接导入运行和二次开发。已有289人学习下载内容包含前后端源码、页面模板与数据库初始化文件可帮助读者快速理解Java Web分层开发、用户权限管理及留言交互流程也能在现有功能上扩展个人特色模块。1. 基于 JavaJSPMySQL 的个人与家乡展示管理平台课程设计该怎么落地这份资源不是那种单页静态的个人主页而是一个包含前端展示、后台管理、用户留言与回复的完整 Java Web 项目。说直白点就是一个基于 JavaJSPMySQL 实现的个人与家乡展示管理平台首页做照片轮播个人技能按科研、学习成绩、开发技能、获奖分类维护家乡部分拆成美食、美景、风土人情三个维度游客注册后可以留言管理员登录后在 Web 界面做增删改查。对要交 Java Web 课程设计的同学来说它把「个人主页 家乡宣传 后台管理」三件事揉在同一个 JSP Servlet MySQL 项目里改一改姓名、照片、文字和表数据就能直接变成自己的作品。2. 数据库与项目骨架四张表如何撑起整个平台2.1 前端框架选型sb-admin-2 和 Font Awesome 在项目里的角色拆开资源包会发现前端目录下躺着一批 css 文件sb-admin-2.css、sb-admin-2.min.css、all.css、fontawesome.css、all.min.css。这说明了项目的样式体系不是从零写的而是站在 sb-admin-2 这个开源后台管理模板肩膀上。sb-admin-2 是 Bootstrap 生态里很流行的一套后台模板提供侧边栏、顶栏、卡片、表格、表单这些现成组件。这个项目把同一套 CSS 同时用在了前台页面和后台管理页面带来的直接好处是首页的轮播、导航、卡片布局不需要手写复杂的样式后台的增删改查表格也能直接复用框架的表格和按钮类。你打开页面看到的那种统一的蓝灰色按钮、卡片阴影、表格间隔色基本都是模板自带的。使用边界也需要说清楚。sb-admin-2 的样式是为后台管理设计的前台页面如果完全照搬页面会显得「很后台」不够有个人主页的气质。所以在改自己版本的时候前台部分建议只保留它的栅格系统、按钮和卡片自己额外写几行内联样式去调字体、间距和背景色后台管理页则保持原样就行。2.2 表结构设计user、skill、feature、message 四张表怎么分后端功能拆成四块用户管理、技能管理、家乡特色管理、留言管理。对应到数据库至少需要四张表。这四张表各自只干一件事互不嵌套是课程设计里最稳妥的表设计方式。用户表 user 要区分管理员和游客所以必须有 role 字段游客注册后要能登录留言所以 username 要唯一。技能表 skill 要支撑下拉菜单里的科研情况、学习成绩、开发技能、获奖四个分类所以 category 字段单独拎出来。家乡特色表 feature 要同时容纳美食、美景、风土人情三种类型用 type 字段区分。留言表 message 要兼顾「游客留言」和「管理员回复」所以回复内容直接挂在同一条记录上用 reply_content 字段保存。表结构设计如下字段不算多但每个都有明确用途。2.3 建库建表 SQL 实战我一般会在项目根目录放一个 init.sql方便换机器时一键重建数据库。下面是这个平台的核心建表语句直接对着 MySQL 执行即可。CREATE DATABASE IF NOT EXISTS home_platform DEFAULT CHARACTER SET utf8mb4 DEFAULT COLLATE utf8mb4_general_ci; USE home_platform; -- 用户表区分管理员和游客 CREATE TABLE user ( id INT PRIMARY KEY AUTO_INCREMENT COMMENT 主键, username VARCHAR(50) NOT NULL UNIQUE COMMENT 登录名已注册后不可重复, password VARCHAR(100) NOT NULL COMMENT 密码建议存 MD5 摘要, role VARCHAR(20) NOT NULL DEFAULT visitor COMMENT admin 或 visitor, create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT 注册时间 ) ENGINEInnoDB COMMENT用户表; -- 技能表科研情况、学习成绩、开发技能、获奖等都放这里 CREATE TABLE skill ( id INT PRIMARY KEY AUTO_INCREMENT COMMENT 主键, category VARCHAR(50) NOT NULL COMMENT 分类对应下拉菜单选项, title VARCHAR(100) NOT NULL COMMENT 条目标题比如 蓝桥杯省赛二等奖, detail TEXT COMMENT 详细说明可留空, create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT 录入时间 ) ENGINEInnoDB COMMENT技能条目表; -- 家乡特色表美食、美景、风土人情共用一张表 CREATE TABLE feature ( id INT PRIMARY KEY AUTO_INCREMENT COMMENT 主键, type VARCHAR(20) NOT NULL COMMENT food 美食 / view 美景 / culture 风土人情, title VARCHAR(100) NOT NULL COMMENT 特色名称, content TEXT COMMENT 介绍文字, image_url VARCHAR(255) COMMENT 配图路径, sort_order INT DEFAULT 0 COMMENT 排序值越小越靠前 ) ENGINEInnoDB COMMENT家乡特色表; -- 留言表游客留言管理员回复回复内容放同一行 CREATE TABLE message ( id INT PRIMARY KEY AUTO_INCREMENT COMMENT 主键, nickname VARCHAR(50) NOT NULL COMMENT 游客昵称, content VARCHAR(500) NOT NULL COMMENT 留言内容, reply_content VARCHAR(500) COMMENT 管理员回复内容为空表示未回复, create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT 留言时间, reply_time DATETIME COMMENT 回复时间 ) ENGINEInnoDB COMMENT留言表;这里有三处值得留意。第一所有表都指定了 utf8mb4 字符集而不是默认的 latin1否则插入中文会直接报错或变问号这是新手最常踩的坑。第二skill 表没有外键关联 user因为游客也能看到技能页面技能数据是公共展示数据不需要归属到某个用户。第三message 表把留言和回复放在同一行而不是拆成两张表目的是让查询留言列表时一条 SQL 就能把「谁留的、管理员回了没、回了什么」全拿出来省一次联表操作。执行完建表语句后记得插入初始管理员账号和几条示例数据否则后台登录和管理页面全是空的。示例数据按你自己的信息改管理员密码可以先存明文后面有精力再换 MD5。3. 前端页面落地轮播、下拉菜单与留言板的 JSP 写法3.1 首页轮播图用 Bootstrap Carousel 实现照片切换首页是整个平台的入口要求是照片轮播 其他功能链接。项目已经引入了 Bootstrap所以直接用它自带的轮播组件最省事没必要手写 JavaScript。!-- index.jsp 首页轮播区域 -- div idhomeCarousel classcarousel slide mx-auto stylemax-width: 960px; >!-- admin/skill_edit.jsp 技能录入表单的分类下拉框 -- % page importjava.sql.* % % page importcom.util.DBUtil % select namecategory classform-select required option value-- 请选择分类 --/option % Connection conn null; PreparedStatement ps null; ResultSet rs null; try { conn DBUtil.getConnection(); ps conn.prepareStatement( SELECT DISTINCT category FROM skill ORDER BY category); rs ps.executeQuery(); while (rs.next()) { String cat rs.getString(category); % option value%cat%%cat%/option % } } finally { if (rs ! null) rs.close(); if (ps ! null) ps.close(); if (conn ! null) conn.close(); } % /select这段代码用了 JSP 脚本片段直接访问数据库读取 skill 表里去重后的分类列表。用 SELECT DISTINCT 而不是 SELECT是因为同一个分类下有多条技能记录下拉框只需要显示分类名称重复的去掉。category 字段值同时作为 option 的 value 和显示文本这样表单提交后 Servlet 拿到的就是「学习成绩」这类中文分类名后面查询直接按这个字段过滤。分类名称要做成中文存储后端查询时注意请求编码问题否则提交回来的中文分类名会被 Servlet 解析成乱码导致新记录存进乱码分类。这个问题在第 5 章我会专门展开。3.3 家乡特色展示三列布局与图片路径家乡特色要求从美食、美景、风土人情几方面介绍。我在做这个模块时前台展示用了三列卡片布局每一列对应一种类型SQL 查询时按 type 字段分组。!-- hometown/feature.jsp 家乡特色列表页 -- % page importjava.sql.* % % page importcom.util.DBUtil % div classrow % Connection conn null; Statement st null; ResultSet rs null; try { conn DBUtil.getConnection(); st conn.createStatement(); rs st.executeQuery( SELECT type, title, content, image_url, sort_order FROM feature ORDER BY sort_order ASC, id DESC); while (rs.next()) { String type rs.getString(type); String typeName ; if (food.equals(type)) { typeName 美食; } else if (view.equals(type)) { typeName 美景; } else { typeName 风土人情; } % div classcol-md-4 mb-4 div classcard h-100 img src${pageContext.request.contextPath}/%rs.getString(image_url)% classcard-img-top alt%rs.getString(title)% div classcard-body h5 classcard-title%typeName%%rs.getString(title)%/h5 p classcard-text%rs.getString(content)%/p /div /div /div % } } finally { if (rs ! null) rs.close(); if (st ! null) st.close(); if (conn ! null) conn.close(); } % /div这段逻辑的核心是按 type 值做中英文映射。数据库里存的是 food、view、culture页面上要显示成美食、美景、风土人情映射放在 JSP 里完成而不是在数据库加一个 type_name 字段因为类型总共就三种硬编码映射代码量最小。ORDER BY sort_order ASC, id DESC 的意思是先按 sort_order 排序sort_order 相同再按 id 倒序。这样在后台管理里新添加的特色如果 sort_order 没设置默认 0会排在最前面符合「新的内容优先展示」的习惯。图片路径这一段比较特殊数据库 image_url 字段存的是相对路径比如 images/view1.jpg模板渲染时前面拼上上下文路径保证浏览器能取到。4. 后端功能闭环登录、注册与增删改查的 Servlet 实现4.1 注册与登录Session 控制和角色区分用户管理模块包含管理员和游客两类。游客注册后可以留言管理员登录后管理整个后端。实现上我用一个 LoginServlet 统一处理登录一个 RegisterServlet 处理注册登录成功后把用户对象塞进 Session后续页面根据 Session 里的 role 判断是否放行。// LoginServlet.java 登录校验核心代码 protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String username request.getParameter(username); String password request.getParameter(password); UserDao dao new UserDao(); User user dao.findByUsernameAndPassword(username, password); if (user null) { response.sendRedirect(admin/login.jsp?error1); return; } HttpSession session request.getSession(); session.setAttribute(currentUser, user); session.setMaxInactiveInterval(30 * 60); if (admin.equals(user.getRole())) { response.sendRedirect(admin/index.jsp); } else { response.sendRedirect(index.jsp); } }登录逻辑里最容易被忽略的是 setMaxInactiveInterval它设置 Session 的有效时间这里设成 30 分钟。如果游客登录后长时间不动Session 过期再点击留言提交时就会因为拿不到用户信息而跳回登录页这是课程设计答辩时一个常见的「演示翻车点」。登录成功后按 role 跳转不同页面管理员进后台管理首页普通游客回前台首页。UserDao.findByUsernameAndPassword 内部用 PreparedStatement 查 user 表避免 SQL 注入public User findByUsernameAndPassword(String username, String password) { String sql SELECT id, username, password, role FROM user WHERE username ? AND password ?; try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setString(1, username); ps.setString(2, password); try (ResultSet rs ps.executeQuery()) { if (rs.next()) { User user new User(); user.setId(rs.getInt(id)); user.setUsername(rs.getString(username)); user.setRole(rs.getString(role)); return user; } } } catch (SQLException e) { e.printStackTrace(); } return null; }这里用 try-with-resources 让连接和查询完自动关闭是老项目里常见的资源管理改进。参数设置那两行setString 的序号从 1 开始对应 SQL 里的两个问号顺序不能反。4.2 技能管理 Servlet用 action 参数分发增删改查技能管理要覆盖科研情况、学习成绩、开发技能、获奖等多个分类每条记录都要在 Web 界面实现增删改查。如果给每个分类写一个 Servlet代码量会翻四倍。正确做法是一个 SkillServlet通过请求里带的 action 参数判断操作类型。// SkillServlet.java 增删改查分发核心代码 protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String action request.getParameter(action); if (add.equals(action)) { String category request.getParameter(category); String title request.getParameter(title); String detail request.getParameter(detail); SkillDao dao new SkillDao(); dao.insert(category, title, detail); response.sendRedirect(admin/skill_list.jsp); } else if (update.equals(action)) { int id Integer.parseInt(request.getParameter(id)); String category request.getParameter(category); String title request.getParameter(title); String detail request.getParameter(detail); SkillDao dao new SkillDao(); dao.update(id, category, title, detail); response.sendRedirect(admin/skill_list.jsp); } else if (delete.equals(action)) { int id Integer.parseInt(request.getParameter(id)); SkillDao dao new SkillDao(); dao.delete(id); response.sendRedirect(admin/skill_list.jsp); } else { response.sendRedirect(admin/skill_list.jsp); } }action 参数分发的模式在课程设计里非常实用。前端每个表单里放一个隐藏字段input typehidden nameaction valueadd提交后 Servlet 根据这个字段分发到不同分支。这样整个技能模块只需要一个 Servlet、一个 Dao、一个列表页代码组织清晰答辩时也好讲。update 分支里用 Integer.parseInt 将字符串 id 转成 int如果前端传了非法 id 会抛 NumberFormatException所以前端表单提交前一定要做非空校验。我这里直接放在 Servlet 里没做异常捕获实际项目里应该在页面层拦截掉空 id。4.3 留言与回复用一条 SQL 完成双向操作留言管理的特殊之处在于它有两个角色参与游客提交留言管理员提交回复。两者操作的是同一条记录的不同字段。// MessageDao.java 留言查询与回复更新关键方法 // 查询所有留言按时间倒序未回复的排前面 public ListMessage findAll() { String sql SELECT id, nickname, content, reply_content, create_time, reply_time FROM message ORDER BY (reply_content IS NULL) DESC, create_time DESC; // ... 执行查询并封装成 ListMessage } // 管理员回复只更新 reply_content 和 reply_time public void reply(int id, String replyContent) { String sql UPDATE message SET reply_content ?, reply_time NOW() WHERE id ?; // ... 设置参数并执行 } // 游客留言插入新记录 public void insert(String nickname, String content) { String sql INSERT INTO message (nickname, content) VALUES (?, ?); // ... 设置参数并执行 }ORDER BY (reply_content IS NULL) DESC 这一句利用了 MySQL 特性IS NULL 表达式结果为 1 时表示未回复DESC 排序让未回复的留言排前面管理员打开留言管理页能第一时间看到待处理的留言处理完回复后这条记录自动沉底。这是一个不需要额外加 status 字段的小技巧。回复时间用 NOW() 在数据库端生成而不是在 Java 代码里 new Date()好处是时间以 MySQL 服务器时间为准不需要考虑应用服务器和数据库服务器之间的时区差异也少写一行代码。留言的昵称直接打字不需要登录所以插入时只用 INSERT 两句参数。如果想把留言功能做更完整可以加一个 is_deleted 逻辑删除字段但课程设计到这一步已经能满足「留言 回复」的要求多一个回收站功能反而要增加排查复杂度。5. 避坑与排查MySQL 8.0 时区、中文乱码与路径三座山5.1 现象启动项目后控制台报 ClassNotFoundException第一次启动项目Tomcat 直接抛java.lang.ClassNotFoundException: com.mysql.jdbc.Driver页面打不开数据库。原因这个错误十有八九是 MySQL 驱动版本和驱动类名不匹配。MySQL 8.0 之后的驱动包类名从com.mysql.jdbc.Driver改成了com.mysql.cj.jdbc.Driver老项目的 Class.forName 如果还写旧类名加载直接就失败。解决把 Java 代码里的驱动注册改成新类名同时检查 WEB-INF/lib 下的驱动 jar 是否真的是 8.x 版本Class.forName(com.mysql.cj.jdbc.Driver);如果改完还报错检查 lib 目录的 mysql-connector jar 包有没有拷全。我一般会把 mysql-connector-java 的 jar 单独放在一个 lib 文件夹部署时拷到 WEB-INF/lib避免和 Tomcat 自带的旧驱动冲突。5.2 现象页面和数据库里的中文全部变成问号登录页面正常显示但注册用户后数据库里 username 字段存的是??。原因这是典型的三层编码不一致。第一层是 JSP 页面本身的 pageEncoding 没设成 UTF-8第二层是请求提交到 Servlet 时没有设置请求编码Tomcat 默认按 ISO-8859-1 解析中文参数第三层是数据库表和连接 URL 没指定 utf8。三层里任何一层不对中文就会变问号。解决三层同时处理缺一不可。JSP 页面顶部加上编码指令数据库连接 URL 带上编码参数表字符集在建表时固定为 utf8mb4% page languagejava contentTypetext/html; charsetUTF-8 pageEncodingUTF-8 %String url jdbc:mysql://localhost:3306/home_platform ?useUnicodetruecharacterEncodingUTF-8;ALTER TABLE user CONVERT TO CHARACTER SET utf8mb4;改完这些记得重启 Tomcat别只改代码不重启那是最容易自我怀疑的地方。5.3 现象首页轮播图片全部裂开审查元素发现路径不对本地打开 index.jsp 图片正常部署到 Tomcat 后图片全部裂开浏览器控制台报 404。原因图片路径没带上下文路径。JSP 页面里如果直接写img srcimages/banner1.jpg浏览器会把这个相对路径解析成http://localhost:8080/images/banner1.jpg但如果应用部署名是home_platform实际图片地址是http://localhost:8080/home_platform/images/banner1.jpg路径对不上自然 404。解决所有引用静态资源的路径统一加上${pageContext.request.contextPath}前缀img src${pageContext.request.contextPath}/images/banner1.jpg /如果项目里图片路径分散在好多 JSP 页面我一般会全局搜索srcimages和hrefcss把所有静态资源引用统一替换替换完后逐个页面刷新检查不放过任何一个裂图。5.4 现象部署后访问项目根路径返回 404直接在浏览器输入http://localhost:8080/home_platform/返回 404但手动访问 index.jsp 却能打开。原因web.xml 里没配置 welcome-file或者配置的文件名和实际首页文件名不一致。Tomcat 默认找 index.html 和 index.jsp如果项目首页叫 home.jsp没有显式声明就会出现这个情况。解决在 web.xml 里明确指定欢迎页welcome-file-list welcome-fileindex.jsp/welcome-file /welcome-file-list如果首页还依赖 Servlet 转发那就得配置一个空路径的 Servlet 映射。但课程设计场景下让 index.jsp 直接当首页最简单避免多一层跳转逻辑。6. 第二轮改进换成自己的家乡数据与部署验证6.1 三步把模板改成「我的家乡」资源拿到手后第一轮先跑通原版第二轮才是真正把它变成你自己的东西。我改这类项目有一个固定顺序按这个顺序来能少走很多弯路。第一步改数据库里的内容而不是改代码。用 Navicat 或者命令行连上 MySQL把 user 表的管理员密码改成自己的把 feature 表里默认的家乡数据删掉插入你自己家乡的美食、美景、风土人情。这样前台页面刷新后内容立刻变成你的代码一行不动。UPDATE user SET password yourpassword WHERE username admin; DELETE FROM feature; INSERT INTO feature (type, title, content, image_url, sort_order) VALUES (food, 牛肉面, 本地最有名的早餐汤头浓郁面条筋道。, images/food1.jpg, 1), (view, 滨江公园, 傍晚沿江散步看晚霞映在水面上。, images/view1.jpg, 1);第二步替换 webapp/images 目录下的图片文件。原项目里的 banner1.jpg、food1.jpg 这些文件名不要改直接覆盖同名文件。这样页面里的图片路径不需要调整替换照片的操作就完成了。图片尺寸尽量统一轮播图建议 1600x600 左右卡片图按 4:3 裁切否则页面看起来会参差不齐。第三步调整轮播节奏和首页文案。把 index.jsp 里 style="width:16px;margin-left:4px;vertical-align:text-bottom;cursor:text;" />
返回列表