多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

JavaWeb日记系统实战:Servlet+JSP+MySQL实现登录注册与增删改查

JavaWeb日记系统实战:Servlet+JSP+MySQL实现登录注册与增删改查 1. 项目全貌梳理与核心需求拆解做JavaWeb课程设计、毕业设计或者只是想把Servlet、JSP这套老技术栈彻底吃透的人应该都绕不开一个经典命题写一个带用户登录、增删改查的小系统。今天我聊的这个项目——基于JavaWeb和MySQL的JSPServlet日记系统看起来名字又长又直白其实就是把Java、JSP、Bootstrap、Servlet、MySQL这一整套技术串起来做成一个能注册、能登录、能写日记、能管理日记的完整Web应用。先说这个系统到底能干什么。用户打开浏览器访问项目地址首先看到的是登录页面和注册入口注册完账号之后进入系统主页主页展示了当前用户写过的所有日记列表支持按时间倒序排列每一篇日记可以查看详情、编辑内容、删除记录同时支持分页浏览。整个系统没有复杂的业务逻辑但却把JavaWeb开发最核心的套路全部覆盖了前端页面用JSPBootstrap来渲染浏览器发请求Servlet接收请求、调用DAO层操作MySQL数据库再把结果塞回JSP页面展示。这就是最标准的Model1和Model2混合模式理解了这个项目的请求流转后面学Spring MVC、Spring Boot都会顺畅很多。这个项目适合谁呢我觉得主要三类人。第一类是正在学JavaWeb的学生课堂上听了Servlet生命周期、JSP内置对象这些概念但缺乏一个完整的项目把这些抽象概念落到实际代码里第二类是准备做课程设计或毕业设计的同学需要一套结构清晰、注释规范、答辩能讲明白的参考代码第三类是刚入职或者想回顾JavaWeb基础的后端开发用这个项目把JDBC、分页查询、Session管理等基本功重新捡起来。我当年第一次完整跑通这个项目的时候最大的感受就是原来一个请求从浏览器到数据库再回到浏览器的完整链路是这样的。 这种认知上的打通比背十遍面试题都管用。这篇内容我会把这个日记系统的架构设计、数据库建表、核心代码逻辑、运行配置、常见报错一次性讲清楚同时把我实际运行项目时踩过的坑也一并整理出来希望能帮你省下几个晚上的调试时间。2. 技术选型背后的小算盘为什么是JSPServletBootstrap很多初学者拿到项目后第一反应是这套技术是不是太老了甚至有人会问为什么不直接上Spring Boot。这里我得替这套技术栈说句公道话作为教学项目和基础训练JSPServlet恰恰是不可跳过的一环。2.1 Servlet和JSP的分工逻辑先理清Servlet和JSP到底是什么关系。Servlet是一个运行在服务端的Java类专门用来接收HTTP请求、处理业务逻辑、返回响应JSP本质上也是一个Servlet只是在写法上更偏向于页面展示可以让Java代码和HTML混在一起写服务端会把JSP编译成Servlet再执行。在这个日记系统里分工是这样的所有请求地址比如/login、/register、/diary/list都由Servlet接收Servlet里写判断逻辑、调用数据库方法、处理Session而JSP页面只负责展示数据通过${}EL表达式或者c:forEachJSTL标签把后台传过来的数据循环渲染成HTML表格和卡片。简单说Servlet是后台大脑JSP是前台门面。这种分工方式的好处是职责边界清晰开发者能直观感受到每一层做了什么。很多新手在接触Spring Boot时容易一头雾水因为框架帮我们把请求分发、参数封装、响应转换全自动化了出问题反而不知道去哪排查。而JSPServlet所有步骤都摆在明面上请求怎么进的Servlet、参数怎么从request里取值、数据怎么塞进session一眼就能看懂。2.2 Bootstrap在前端扮演的角色再说Bootstrap。这个项目的前端没有用Vue、React这些框架而是选择了Bootstrap 3或4版本配合JSP使用。原因有两点第一JSP是服务端渲染配合Bootstrap这种“拿过来直接用”的CSS组件库非常顺手不需要构建工具、不需要npm安装依赖把bootstrap.min.css和bootstrap.min.js放进webapp目录下的static文件夹页面里通过link标签引入就能生效第二Bootstrap自带的栅格系统、表单样式、按钮、分页组件、模态框可以让一个没有任何前端功底的Java开发者在半小时内拼出一个像模像样的管理界面尤其是日记列表的分页条直接用Bootstrap的pagination类名就能实现省去大量手写CSS的时间。2.3 MySQL为什么是省心之选数据库选用MySQL这个决策应该没什么争议。MySQL在JavaWeb学习阶段的地位基本是统治级的一方面是免费开源另一方面是生态环境太好了网上随便一搜就是海量的建表语句、报错解决方案。而且MySQL 5.7和8.0配合JDBC驱动连接Java程序在配置文件里写好driverClassName、url、username、password四个参数就能跑起来对新手极其友好。不过这里也有一点需要提前说清楚的取舍如果用最新的MySQL 8.0.xJDBC驱动类名要写com.mysql.cj.jdbc.DriverURL里还需要加上serverTimezoneAsia/Shanghai这个时区参数否则会报The server time zone value Öйú±ê׼ʱ¼ä is unrecognized的这个经典错误。我在后面踩坑实录那一节会详细展开这个问题。如果用的是MySQL 5.7.x驱动类名写com.mysql.jdbc.Driver即可但也别高兴太早——5.7版本在某些字符集配置上同样有坑。整体来看只要选一个版本然后把它彻底搞定MySQL就是整个项目里最省心的环节。2.4 为什么不直接用Spring Boot我没有一上来就推荐Spring Boot因为如果你是第一次接触JavaWeb直接跳到Spring Boot反而容易留下知识盲区。Servlet规范、Filter过滤器、Session管理、请求转发与重定向、JDBC连接管理这些底层原理在Spring Boot里都被封装成了几个注解和工具类短期来看写代码很爽长期来看一旦遇到和高并发、调优、框架源码相关的面试题就会暴露出基础不稳的问题。我个人的建议是先用这个JSPServlet项目把地基打牢再顺着学Maven、MyBatis、Spring、Spring Boot届时你会发现框架只是一个更高效率的封装底层思路你全都见过。3. 数据库设计实战从用户表到日记表数据库设计是整个系统的地基。我在设计这个日记系统时没有一上来就写代码而是先在纸上把业务涉及的数据实体和它们之间的关系理清楚。这个系统的核心实体很简单用户user和日记diary一个用户对应多篇日记所以是典型的一对多关系。3.1 用户表结构分析用户表用于存储系统的登录账号信息字段设计不需要花哨但每个字段的类型、长度、约束都得有意义。以下是我在实际项目中应用的建表语句CREATE TABLE user ( id INT NOT NULL AUTO_INCREMENT COMMENT 用户ID主键, username VARCHAR(50) NOT NULL COMMENT 用户名登录账号, password VARCHAR(100) NOT NULL COMMENT 密码建议MD5加密存储, nickname VARCHAR(50) DEFAULT NULL COMMENT 昵称用于页面展示, email VARCHAR(100) DEFAULT NULL COMMENT 邮箱备用, create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT 注册时间, PRIMARY KEY (id), UNIQUE KEY uk_username (username) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;这里有几个细节值得单独说明。第一主键用INT AUTO_INCREMENT自增整数在数据量不大的学习项目里完全够用没必要学互联网大厂去搞雪花算法那是分布式场景才需要考虑的问题。第二username字段建了唯一索引UNIQUE KEY防止用户重复注册。如果你不提前建这个约束就只能在注册的Servlet代码里先做一次”用户名是否存在”的查询多写代码不说还有并发注册导致脏数据的风险。第三密码字段长度我留了100即使你后续不用MD5而改用SHA-256或者加盐加密存储空间也足够不用反反复复改表结构。3.2 日记表结构分析日记表是这个系统的核心业务表。每个字段的设计都对应一个真实的功能需求。我的建表语句如下CREATE TABLE diary ( id INT NOT NULL AUTO_INCREMENT COMMENT 日记ID, user_id INT NOT NULL COMMENT 所属用户ID关联user表, title VARCHAR(100) NOT NULL COMMENT 日记标题, content TEXT NOT NULL COMMENT 日记正文TEXT类型支持长文本, weather VARCHAR(20) DEFAULT NULL COMMENT 当天天气, mood VARCHAR(20) DEFAULT NULL COMMENT 当天心情, create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT 创建时间, update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 最后修改时间, PRIMARY KEY (id), KEY idx_user_id (user_id), CONSTRAINT fk_diary_user FOREIGN KEY (user_id) REFERENCES user (id) ON DELETE CASCADE ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;user_id字段设置了外键fk_diary_user指向用户表的主键同时设置了级联删除ON DELETE CASCADE意思是当用户账号被删除时这个用户的所有日记一并删除避免产生孤儿数据。虽然在学习项目中删除用户的功能可能根本不会写但这个设计习惯值得保留面试官问起外键约束时你能讲得明明白白。weather和mood这两个字段是我在基础需求上做的合理扩展。写日记的时候顺手选一下今天天气怎么样、心情怎么样会让这个系统更有仪式感同时也给以后扩展“按心情筛选日记”功能留了口子。这两个字段用VARCHAR(20)就够存“晴”“多云”“开心”“emo”这种短词没必要用枚举类型因为需求随时可能调整枚举改起来太麻烦。update_time字段的ON UPDATE CURRENT_TIMESTAMP是MySQL 5.6之后支持的写法每次更新记录时自动刷新修改时间省去在Java代码里手动维护这个字段。这也是我在实际项目里特意保留的一个小亮点答辩的时候可以让老师知道你不只是照着模板敲代码是真的理解每个字段的用意。3.3 表关系与数据操作复盘两张表通过user_id建立起一对多关系后CRUD操作就有了明确的逻辑注册功能向user表INSERT一条记录。登录功能根据用户名查询user表比对密码。新增日记向diary表INSERT其中user_id取当前登录用户的ID。日记列表SELECT * FROM diary WHERE user_id 当前用户ID ORDER BY create_time DESC LIMIT ?,?注意加WHERE条件实现“仅看自己的日记”这个需求不然后果就是所有用户都能看到彼此的内容这是新手最容易忽略的越权漏洞。删除日记DELETE FROM diary WHERE id ? AND user_id ?删除条件里带上user_id既能防止用户删除别人的日记又是一种简易的数据权限校验。关于JDBC连接数据库的操作我建议把所有重复性的代码封装到一个DBUtil工具类里核心思路是在静态代码块里加载数据库驱动提供一个getConnection()方法返回连接对象再提供close()方法关闭资源。很多初学者每写一个DAO方法就重新写一遍Class.forName、DriverManager.getConnection代码重复得惨不忍睹封装工具类之后每个DAO方法只关注自己的SQL逻辑这不仅是代码整洁的问题更是一种工程意识的体现。4. 核心功能实现与关键代码解析数据库设计好了接下来就是把这个系统的核心功能一个个实现出来。我按照实际开发的顺序来拆解环境准备、项目骨架搭建、注册登录模块、日记管理模块、分页查询优化。4.1 环境准备与项目骨架搭建我开发这个项目时使用的环境组合是JDK 1.8、IntelliJ IDEA 2020.3、Tomcat 8.5、MySQL 5.7Maven做依赖管理。这套组合主打一个“稳定兼容”JDK1.8是JavaWeb项目的常青树Tomcat 8.5支持Servlet 3.1规范和这个项目使用的Servlet API版本完美匹配。在IDEA里创建项目的步骤是这样的新建Maven项目勾选Web Application模板填好GroupId和ArtifactId然后手动补充webapp/WEB-INF/web.xml。在pom.xml中添加以下关键依赖dependencies !-- Servlet API -- dependency groupIdjavax.servlet/groupId artifactIdjavax.servlet-api/artifactId version4.0.1/version scopeprovided/scope /dependency !-- JSP API -- dependency groupIdjavax.servlet.jsp/groupId artifactIdjavax.servlet.jsp-api/artifactId version2.3.3/version scopeprovided/scope /dependency !-- JSTL标签库 -- dependency groupIdjavax.servlet/groupId artifactIdjstl/artifactId version1.2/version /dependency !-- MySQL驱动 -- dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId version8.0.26/version /dependency /dependencies这里的provided作用域值得留意Servlet和JSP API由Tomcat容器提供打包时不需要打进去如果误用了compile默认作用域会导致后面把项目部署到Tomcat时出现类冲突。依赖版本我用的驱动是8.0.26虽然数据库是5.7但MySQL 8.0驱动向下兼容5.x版本的数据库这样可以少装一个驱动文件。实际连接URL这样写String url jdbc:mysql://localhost:3306/diary_system?useSSLfalseserverTimezoneAsia/ShanghaicharacterEncodingutf8;useSSLfalse因为我是在本地开发没必要开启SSL加密serverTimezoneAsia/Shanghai解决了时区报错characterEncodingutf8保证中文数据不乱码。这三件套相当于Java连接MySQL的“保命符”缺一个都可能触发莫名其妙的报错。4.2 注册登录模块Session管理是核心注册登录是几乎所有Web系统的入口功能这个模块做扎实了后面所有功能都会顺理成章。注册的流程是这样的用户填写用户名、密码、确认密码点击提交后表单数据POST到RegisterServlet。Servlet先做基础校验——用户名不能为空、密码长度不能小于6位、两次密码必须一致——然后调用UserDao.findByUsername(username)检查用户名是否已被占用如果没有则调用UserDao.register(user)插入数据库最后重定向到login.jsp页面让用户登录。注意我在这里用的是response.sendRedirect()而不是request.getRequestDispatcher().forward()因为注册成功后如果直接转发到登录页用户刷新浏览器会导致表单重复提交重定向可以避免这个问题。登录模块有一个细节值得多说几句小程序项目里通常用Session保存用户状态。用户输入正确的用户名密码后Servlet里执行这段代码User user userDao.findByUsernameAndPassword(username, password); if (user ! null) { HttpSession session request.getSession(); session.setAttribute(currentUser, user); session.setMaxInactiveInterval(30 * 60); // 设置会话超时时间为30分钟 response.sendRedirect(request.getContextPath() /diary/list); } else { request.setAttribute(errorMsg, 用户名或密码错误); request.getRequestDispatcher(/login.jsp).forward(request, response); }session.setAttribute(currentUser, user)把整个User对象放进会话之后日记相关的Servlet里只需要通过(User) session.getAttribute(currentUser)就能拿到当前登录用户的信息这是最简洁的做法。设置会话超时时间setMaxInactiveInterval(30 * 60)是为了防止用户长时间挂着不操作导致Session一直被占用。登录成功后的页面跳转推荐用重定向因为登录成功后地址栏应该重置到主页地址让用户产生清晰的页面变化感知登录失败则用请求转发把错误提示errorMsg带回登录页回显。整个模块需要配套一个LogoutServlet调用session.invalidate()销毁会话再把用户带回登录页。4.3 日记管理模块CRUD循环日记模块是整个系统的重头戏包含列表展示、新增日记、编辑日记、删除日记四个操作与数据库的增删改查一一对应。列表展示是这样实现的DiaryServlet接收前端的/diary/list请求从Session中取出当前用户调用DiaryDao.findByUserId(userId, offset, pageSize)查询数据库得到ListDiary集合后通过request.setAttribute(diaryList, list)放到request域中然后请求转发到diaryList.jsp。JSP页面里用JSTL标签循环渲染数据c:forEach items${diaryList} vardiary div classcard mb-3 div classcard-body h5 classcard-title${diary.title}/h5 p classcard-text${diary.content}/p span classbadge bg-info天气${diary.weather}/span span classbadge bg-success心情${diary.mood}/span small classtext-muted创建时间${diary.createTime}/small a href${pageContext.request.contextPath}/diary/edit?id${diary.id}编辑/a a href${pageContext.request.contextPath}/diary/delete?id${diary.id} onclickreturn confirm(确定要删除这篇日记吗)删除/a /div /div /c:forEach注意${pageContext.request.contextPath}这个EL表达式的用法它动态获取项目上下文路径这样无论是本地部署还是服务器部署都能正确拼出URL避免硬编码路径导致项目名改动后全部链接失效的问题。删除链接上加了onclick确认弹窗也给操作加了一道保险。新增日记和编辑日记可以共用一个JSP页面。diaryEdit.jsp页面里先判断有没有diary对象如果有则表单回填数据没有则为空表单。提交时统一交给DiarySaveServlet处理在Servlet里再做一次判断如果请求参数里有id且这个id对应的日记属于当前登录用户就执行UPDATE操作否则执行INSERT操作。这种“一个Servlet处理保存”的设计比拆成addServlet和updateServlet更简洁代码量能少三分之一。这里要特别强调一个越权防护细节编辑和删除操作时必须携带user_id条件。比如做删除功能SQL不能只写DELETE FROM diary WHERE id?而应该写成DELETE FROM diary WHERE id? AND user_id?user_id从当前Session获取。不这么做的话用户只要猜到或者拿到别人的日记ID就能把别人的日记删掉这在安全上是非常低级的漏洞但也是新手项目里出现频率最高的漏洞之一。4.4 分页查询避免一次加载全部数据日记系统的数据量在现阶段不至于爆炸但分页功能是JavaWeb面试必问的知识点也几乎是所有后台管理系统的标配。用LIMIT关键字实现物理分页其实非常直观核心逻辑只有三步计算出总记录数SELECT COUNT(*) FROM diary WHERE user_id?。根据每页显示条数计算总页数totalPages (totalCount pageSize - 1) / pageSize这里用(总数页大小-1)/页大小这个公式做向上取整避免多算一页。查询当前页数据SELECT * FROM diary WHERE user_id? ORDER BY create_time DESC LIMIT ?,?两个问号分别是offset偏移量和pageSize页大小offset (currentPage - 1) * pageSize。JSP页面的分页条用Bootstrap的pagination组件实现nav aria-labelPage navigation ul classpagination li classpage-item c:if test${currentPage 1}disabled/c:if a classpage-link href?page${currentPage - 1}上一页/a /li c:forEach begin1 end${totalPages} vari li classpage-item c:if test${i currentPage}active/c:if a classpage-link href?page${i}${i}/a /li /c:forEach li classpage-item c:if test${currentPage totalPages}disabled/c:if a classpage-link href?page${currentPage 1}下一页/a /li /ul /nav页码遍历用JSTL的c:forEach从1循环到总页数当前页高亮用active类控制上一页在首页时禁用下一页在末页时禁用。这套写法是可复用的标准模板以后做任何系统的分页都可以直接抄。5. 从配置到上线IDEA运行JavaWeb项目的完整流程项目代码写完了最终得跑起来才算完成。很多初学者面临的最大障碍不是写代码而是不知道如何在IDEA里把一个JavaWeb项目配上Tomcat并运行起来。我在这里完整复盘一遍流程每个步骤都是踩过坑之后总结出来的标准操作。5.1 本地开发环境配置首先是要保证三个基础软件装好JDK 1.8、MySQL 5.7或8.0、IDEA。JDK安装后要在系统环境变量里配置JAVA_HOME指向JDK安装目录然后在Path中添加%JAVA_HOME%\bin之后在命令行敲java -version验证是否成功。这一步如果没配好后面IDEA里编译项目都会报找不到JDK。MySQL的安装我强烈建议用安装包方式而不是解压版虽然解压版更干净但需要手动初始化数据目录、配置my.ini、注册Windows服务每步都有出错概率。安装版一路Next也能自定义数据目录和端口省心很多。安装完成后使用MySQL自带命令行或者Navicat/SQLyog这类可视化工具执行前面提到的建表语句把数据库创建出来。注意MySQL 8.0默认的认证插件是caching_sha2_password老版本的JDBC驱动连接会报错所以要么升级驱动到8.0版本要么创建用户时指定mysql_native_password认证这也是一个高频坑。5.2 用IDEA配置Tomcat并启动项目在IDEA中打开项目后点击顶部工具栏的“Add Configuration”选择“Tomcat Server”注意要选Local版本。在Application server下拉框里点击Configure找到你本地安装的Tomcat目录比如D:\apache-tomcat-8.5.82IDEA会自动识别版本号。然后切到Deployment页签点击号把当前项目以Artifact方式添加进去Application context建议填/这样项目访问地址就是http://localhost:8080/不用再带一长串项目名。这里有一个新手常犯的错误只配置了Tomcat但忘了把项目部署进去导致点运行按钮后浏览器访问404。每次修改代码后建议手动点击右上角的“Rerun”重启Tomcat。有些朋友希望代码改完自动热部署可以在Tomcat配置的On frame deactivation选择Update classes and resources但JSP改动能自动生效Java类改动经常还是要重启容器。还有一个第一节课就会遇到的坑IDEA默认Tomcat会占用1099端口的JMX配置端口如果本机这个端口被占用会导致启动失败报错信息里会明确提示把JMX port改成一个别的端口号比如1098即可。5.3 常见启动故障快速定位项目跑不起来先按这个顺序排查第一看IDEA控制台的Tomcat Catalina Log和Localhost Log标签页里面有完整的异常堆栈信息这是定位问题的第一手资料第二检查项目的web.xml文件是否存在且配置正确Servlet的servlet-mapping路径有没有写错第三确认MySQL服务已经启动——Windows下可以在“服务”窗口找到MySQL服务双击启动或者命令行执行net start mysql第四检查数据库连接配置是否和本地数据库一致尤其是密码必须和安装MySQL时设置的一致。我遇到过一个非常无语的情况数据库密码正确、驱动正确、URL正确但是连接还是报Access denied for user rootlocalhost最后发现是MySQL服务端启用了skip_grant_tables模式密码验证被彻底跳过导致所有密码校验异常。这类问题排查起来耗时但只要你掌握先看日志、再查配置、最后测联通的思路总能一步步接近真相。6. 高频踩坑与排查手册我在这套系统上跌过的跟头每个做JavaWeb项目的人都会遇到一些让人崩溃的报错。我把自己在开发和运行这套日记系统过程中遇到的高频问题整理成了一份排查手册每一个都给出了具体的解决方案和产生原因希望对你有实际帮助。6.1 MySQL 5.7升级到8.0后的连接问题项目开发后期我把本地数据库从MySQL 5.7升级到了8.0.26版本一启动项目就报错Loading class com.mysql.jdbc.Driver. This is deprecated. The new driver class is com.mysql.cj.jdbc.Driver。这不是致命错误但每次启动都会出现。解决方案是把驱动类名从com.mysql.jdbc.Driver改成com.mysql.cj.jdbc.Driver。紧接着还遇到了第二个问题java.sql.SQLException: The server time zone value Öйú±ê׼ʱ¼ä is unrecognized or represents more than one time zone。这是因为MySQL 8.0服务端的时区信息无法被JDBC识别需要在连接URL上增加serverTimezoneAsia/Shanghai参数。这两个问题几乎每个用MySQL 8.0跑JavaWeb项目的人都会遇到记下这个配置组合基本可以一次解决。6.2 中文参数乱码的三种解法做日记系统时中文乱码问题几乎必然出现从前端表单提交的中文数据存到数据库再查出来就变成了“???”或者在页面上显示成乱码。这个问题有三个层面的解决方案需要同时做对才能彻底解决。第一层是页面层面JSP页面顶部必须声明% page contentTypetext/html;charsetUTF-8 languagejava %meta charsetUTF-8也要写上。第二层是Servlet层面在doPost方法中如果使用POST提交需要在读取参数之前调用request.setCharacterEncoding(UTF-8)如果使用GET提交还需要在Tomcat的server.xml中给Connector增加URIEncodingUTF-8属性或者在代码里对参数做new String(param.getBytes(ISO-8859-1), UTF-8)转换。第三层是数据库层面建表时指定DEFAULT CHARSETutf8mb4同时确保数据库连接URL带上characterEncodingutf8。三层都做对了中文才能一路通行无阻。6.3 部署到服务器后配置修改的一个细节本地跑通之后把项目打成WAR包放到远程服务器上的Tomcat部署会发现数据库连不上了。这多半是因为本地配置写死了localhost:3306到了服务器上应该改成服务器的数据库地址。我建议把这个配置统一提取到一个db.properties资源文件里通过Properties类加载读取这样以后不管是本地开发还是服务器部署只需要改一个文件不涉及到代码重新编译。这也是从“学生项目”向“工程化项目”迈进的第一步。6.4 类找不到与被占用的端口如果启动时看到ClassNotFoundException: com.mysql.jdbc.Driver要么是pom.xml中没有引入MySQL驱动依赖要么是驱动没有随项目打进WEB-INF/lib目录。在IDEA里检查Project Structure中的Artifacts选项卡把MySQL驱动的lib目录加入输出包即可。另一个经典问题是端口占用Port 8080 was already in use。解决方法是查出占用端口的进程并结束它Windows用netstat -ano | findstr 8080查看PID然后在任务管理器里结束对应进程或者在Tomcat配置中把HTTP端口改成一个不常用的比如8081。这两个问题都不难但是它们是新手最经常卡住不动的地方。7. 项目复盘与后续扩展这套代码还能带给你什么整个日记系统从头到尾跑通之后我强烈建议你不要只停留在“能运行”这个阶段而是顺着现有代码继续做扩展因为这套结构其实是很多更复杂系统的缩小版它身上承载的概念可以延伸到各种业务系统。一个很好的扩展方向是给日记增加标签系统在现有diary表的基础上增加一张tag表再增加一张diary_tag关联表来维护多对多关系。这个改动会迫使你去思考“中间表”的设计理念也会让你的项目在答辩时更有说头。第二个方向是给系统加入简单的数据统计比如在首页用ECharts图表展示用户每月写了多少篇日记、心情占比分布虽然ECharts在JSP中直接引入是旧时代的做法但理解这种前后端协作方式仍然有价值。第三个方向是安全加固密码加密从MD5换成加盐的SHA-256增加防SQL注入的预编译占位符其实现在用的PreparedStatement基本已经防住给Session增加简单的验证码校验逻辑。就我个人经验而言做完这个项目后再去学SSM或者Spring Boot你会发现那些框架里的RequestMapping、ResponseBody、MyBatis的Mapper接口本质上都是对Servlet和JDBC的封装。你已经见过“裸实现”的样子再看“封装实现”理解速度会有质的提升。这个日记系统虽然小但它是连接Java基础知识和主流框架的一座稳定的桥值得你为它花上几个周末。如果在运行过程中遇到这里没有提到的问题建议你先按“浏览器URL对不对 → Tomcat日志报什么 → 数据库连接正常不 → SQL语句有没有执行成功”这条链路逐层排查。JavaWeb排错说白了就是一条请求从浏览器出发最终到达数据库每一个环节都可能出问题你把它当成一条流水线来看待定位问题就会有理有据、不慌不忙。
返回列表