简介:这份源码包面向Java Web初学者与课程设计开发者,提供一套基于JSP与MySQL实现的个人记事备忘系统完整工程,可用于学习Servlet与JSP协作、数据库增删改查及MVC分层思路。压缩包共374个文件,约27.51MB,以png、gif、jpg等界面素材与js、css、html前端资源为主,另有24个java源文件、9个jsp页面、7个xml配置及7个jar依赖,涵盖笔记创建、编辑、存储与检索等核心功能模块。资源中附带ueditor、bootstrap、video-js等前端库,便于理解富文本编辑与响应式布局的集成方式。目前已有109人学习下载,适合作为Web开发入门练手或毕业设计参考,帮助读者梳理JSP语法、JDBC数据持久化、数据库表结构设计及Web应用部署等关键知识点,快速搭建可运行的个人备忘系统原型。
1. 从一份 JSP 记事本源码说起:它到底能帮你省下多少事
如果你正在找一个能跑起来的 JavaWeb 完整案例,用来练手 JSP、Servlet 和 MySQL 的配合,那这份「基于 JSP 和 MySQL 实现的个人记事备忘系统」源码包值得花时间拆一遍。它不是那种只有几个页面的演示 demo,而是把用户登录、笔记增删改查、富文本编辑、前端样式一整套都串起来了。源码包里能看到notes-jsp主目录,还有 bootstrap、ueditor、video-js 这些前端资源,说明它在前端交互上是下了功夫的,不是纯裸 HTML 表单。
这个系统解决的核心问题很具体:给你一个能直接部署到 Tomcat 上、连上 MySQL 就能用的记事本应用。适合谁?一是刚学完 Servlet 和 JSP 语法、想找个完整项目把知识串起来的学生;二是需要快速搭一个内部简易备忘工具的后端开发;三是想研究 JSP 时代 MVC 分层写法的从业者。它不追求高并发,也不玩微服务,就是把「浏览器发请求 → Servlet 处理 → JDBC 操作 MySQL → JSP 渲染结果」这条链路走通、走扎实。
2. 把源码跑起来:环境搭建与数据库初始化
2.1 先看清技术栈和目录结构
拿到源码包,别急着往 IDE 里拖。先解压,看根目录。通常这类 JSP 项目会遵循标准 Web 应用结构:WEB-INF下放web.xml、classes、lib,JSP 页面放在 WebRoot 或 webapp 根目录下。从文件列表里的bootstrap.css、ueditor.css、video-js.css可以判断,前端用了 Bootstrap 做响应式布局,UEditor 做富文本编辑,video-js 可能用于笔记里嵌入视频播放。这些静态资源一般放在static或resources目录下。
notes-jsp这个目录名大概率就是项目主模块。进去之后找WEB-INF/web.xml,这是整个应用的入口配置。再看有没有lib目录,里面应该包含 MySQL 的 JDBC 驱动 jar 包,比如mysql-connector-java-8.0.x.jar。如果没有,你得自己补一个,版本要和你的 MySQL 服务端匹配。常见做法是 MySQL 8.0 配 connector 8.0.x,MySQL 5.7 配 connector 5.1.x,版本错配会直接报No suitable driver found或者时区错误。
2.2 数据库建库建表与连接配置
这个系统的数据库设计不会太复杂,核心表通常就两张:用户表和笔记表。用户表存账号密码,笔记表存标题、内容、创建时间、用户 ID 外键。我一般会先手动建库,字符集用utf8mb4,排序规则utf8mb4_general_ci,避免中文乱码和 emoji 存储问题。
-- 创建数据库,字符集必须指定 utf8mb4 CREATE DATABASE note_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE note_db; -- 用户表:存登录信息 CREATE TABLE t_user ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(100) NOT NULL, create_time DATETIME DEFAULT CURRENT_TIMESTAMP ); -- 笔记表:核心业务表,user_id 关联用户 CREATE TABLE t_note ( id INT PRIMARY KEY AUTO_INCREMENT, user_id INT NOT NULL, title VARCHAR(200) NOT NULL, content TEXT, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, FOREIGN KEY (user_id) REFERENCES t_user(id) );建完表,去源码里找数据库连接配置。老项目一般把 JDBC 参数写在db.properties或者直接硬编码在 Java 类里。搜索jdbc:mysql这个关键字,找到后把 URL、用户名、密码改成你本地的。URL 里记得加useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=utf8,这三个参数是 MySQL 8.0 连接失败的常见原因。useSSL=false关掉 SSL 警告,serverTimezone解决时区报错,characterEncoding保证中文不乱码。
提示:如果源码里用的是
com.mysql.jdbc.Driver,而你的 connector 是 8.0 版本,要改成com.mysql.cj.jdbc.Driver,否则启动就抛异常。
2.3 部署到 Tomcat 并验证登录流程
数据库通了之后,把项目部署到 Tomcat。如果你用 Eclipse 或 IDEA,直接配 Tomcat 服务器,把项目 add 进去。命令行方式就是把编译后的war包丢进webapps目录。启动 Tomcat,浏览器访问http://localhost:8080/notes-jsp/,应该能看到登录页。
先注册一个账号,走一遍注册流程。注册成功后用新账号登录,进主页看笔记列表是否为空。然后新建一条笔记,标题写「测试」,内容随便填,保存。刷新列表页,看这条笔记有没有出现。再点编辑,改标题,保存,看是否更新。最后删除,确认列表里消失。这一套走完,说明 Servlet 的doGet/doPost、JDBC 的 CRUD、JSP 的 EL 表达式取值都是通的。
如果登录后页面报 500,看 Tomcat 日志里有没有NullPointerException。常见原因是 Servlet 里获取的userId没存进 session,或者 JSP 里用${user.username}时 user 对象为 null。检查登录成功的 Servlet 里有没有session.setAttribute("user", user)这一句。
3. 拆解核心模块:Servlet 控制器与 JDBC 数据访问
3.1 笔记的增删改查是怎么串起来的
这个项目大概率用了最朴素的 MVC 分层:JSP 当 View,Servlet 当 Controller,JavaBean 当 Model,DAO 类封装 JDBC 操作。你会在源码里找到类似NoteServlet.java的文件,它继承HttpServlet,重写doGet和doPost。doGet里通常根据action参数决定走哪个分支,比如action=list查列表,action=delete删笔记。doPost处理表单提交,比如新增和更新。
我一般会先看web.xml里 Servlet 的映射路径,或者看有没有用@WebServlet("/note")注解。找到映射后,顺着 URL 去追 Servlet 代码。重点看service方法或者doGet里怎么调 DAO。DAO 类里就是标准的 JDBC 模板代码:Connection conn = DriverManager.getConnection(...),然后PreparedStatement ps = conn.prepareStatement(sql),设置参数,执行executeQuery或executeUpdate,最后在finally块里关资源。
// 典型的查询方法,注意用 PreparedStatement 防 SQL 注入 public List<Note> getNotesByUserId(int userId) { List<Note> list = new ArrayList<>(); String sql = "SELECT * FROM t_note WHERE user_id = ? ORDER BY update_time DESC"; try (Connection conn = DBUtil.getConnection(); PreparedStatement ps = conn.prepareStatement(sql)) { ps.setInt(1, userId); try (ResultSet rs = ps.executeQuery()) { while (rs.next()) { Note note = new Note(); note.setId(rs.getInt("id")); note.setTitle(rs.getString("title")); note.setContent(rs.getString("content")); note.setUpdateTime(rs.getTimestamp("update_time")); list.add(note); } } } catch (SQLException e) { e.printStackTrace(); } return list; }这段代码的关键点在于PreparedStatement的占位符?,它把用户输入当参数处理,而不是拼接到 SQL 字符串里,能有效防 SQL 注入。ORDER BY update_time DESC让最新修改的笔记排前面,符合记事本的使用习惯。try-with-resources语法自动关连接,比手写finally块干净,但前提是你的 JDBC 版本支持。
3.2 富文本内容怎么存怎么取
源码包里出现了ueditor.css和ueditor.min.css,说明笔记编辑页集成了 UEditor。UEditor 提交的内容是 HTML 片段,不是纯文本。存进 MySQL 的content字段时,字段类型必须是TEXT或LONGTEXT,用VARCHAR会截断。取出来渲染到 JSP 页面时,不能用<c:out>转义,否则 HTML 标签会变成可见字符。正确做法是用 EL 表达式直接输出${note.content},但这样有 XSS 风险。
常见做法是在后端存之前做一次过滤,把<script>标签去掉,或者用Jsoup库做白名单清洗。如果只是自己本地用,不对外网开放,直接输出也能接受。但如果你要把这个项目改造成多人用的系统,XSS 过滤必须加。我一般会在 DAO 保存前调一个HtmlUtils.clean(content)方法,把危险标签过滤掉。
注意:UEditor 默认会上传图片到本地服务器,路径配置在
config.json里。如果部署后图片显示不出来,检查imageUrlPrefix和imagePathFormat这两个配置项,确保上传目录有写权限。
3.3 分页查询与列表渲染
笔记多了之后,列表页必须分页。这个项目里大概率有分页逻辑,可能用LIMIT和OFFSET实现。你会在 DAO 里找到类似SELECT * FROM t_note WHERE user_id = ? LIMIT ?, ?的 SQL。第一个?是偏移量,第二个?是每页条数。Servlet 里从请求参数拿pageNo,算出offset = (pageNo - 1) * pageSize,传给 DAO。
JSP 页面用 JSTL 的<c:forEach>遍历笔记列表,用<c:if>判断是否有数据。分页导航栏用<c:forEach>生成页码链接,当前页高亮。如果源码里没用 JSTL,而是用 Java 脚本片段<% %>写循环,那说明项目比较老,但逻辑是一样的。你可以顺手把它重构成 JSTL 版本,算是对自己的一次小练习。
<!-- 用 JSTL 遍历笔记列表,比脚本片段清爽 --> <c:forEach items="${noteList}" var="note"> <div class="note-item"> <h3>${note.title}</h3> <p>${note.updateTime}</p> <a href="note?action=edit&id=${note.id}">编辑</a> <a href="note?action=delete&id=${note.id}" onclick="return confirm('确定删除?')">删除</a> </div> </c:forEach>这段 JSP 里,${noteList}是 Servlet 通过request.setAttribute("noteList", list)传过来的。<c:forEach>自动遍历,var="note"是循环变量。删除链接加了onclick确认框,防止误删。这种写法比<% for(Note note : list) { %>可读性高很多,也更容易维护。
4. 避坑排查:从 500 错误到中文乱码的实战记录
4.1 启动就报 ClassNotFoundException: com.mysql.jdbc.Driver
现象:Tomcat 启动后访问页面,控制台抛ClassNotFoundException,提示找不到 MySQL 驱动类。
原因:项目WEB-INF/lib目录下没有放 MySQL 的 JDBC 驱动 jar 包,或者放了但版本不对。老代码里写的是com.mysql.jdbc.Driver,而 MySQL 8.0 的驱动类已经改成com.mysql.cj.jdbc.Driver。
解决:下载对应版本的mysql-connector-javajar 包,丢进WEB-INF/lib。然后全局搜索com.mysql.jdbc.Driver,替换成com.mysql.cj.jdbc.Driver。如果用的是 Maven 项目,在pom.xml里加依赖,scope 设为runtime。
4.2 中文笔记保存后变成问号
现象:新建笔记输入中文,保存成功,但列表页显示全是???。
原因:数据库连接 URL 没指定字符编码,或者数据库、表的字符集不是utf8mb4。MySQL 默认字符集在 5.7 和 8.0 里可能不同,8.0 默认是utf8mb4,但 5.7 默认是latin1。
解决:先检查数据库和表的字符集,用SHOW CREATE TABLE t_note;看CHARSET是不是utf8mb4。如果不是,用ALTER TABLE t_note CONVERT TO CHARACTER SET utf8mb4;改。然后检查 JDBC URL,确保有characterEncoding=utf8。最后检查 Tomcat 的server.xml,Connector标签里加URIEncoding="UTF-8"。
4.3 登录后 session 丢失,刷新页面就退出
现象:登录成功跳转到主页,但一刷新页面就回到登录页,或者点任何链接都提示未登录。
原因:Session 没存住,或者 JSP 页面里判断登录状态的逻辑写错了。常见的是在 Servlet 里用了request.getSession()存 user,但 JSP 里用session.getAttribute("user")取的时候变量名不一致。
解决:统一 session 的 key 名。登录 Servlet 里写session.setAttribute("loginUser", user),那所有 JSP 和 Servlet 里都得用loginUser。建议在web.xml里配一个过滤器,统一拦截请求判断 session,避免每个页面都写重复代码。
4.4 富文本内容里的图片不显示
现象:UEditor 插入图片后,编辑器里能看到,保存后查看笔记,图片裂了。
原因:UEditor 上传图片的路径是相对路径,比如/ueditor/jsp/upload/image/xxx.jpg,但 JSP 页面渲染时,浏览器解析的根路径可能不对。或者上传目录没有写权限,图片根本没存进去。
解决:检查 UEditor 的config.json里imageUrlPrefix配置。如果项目部署在/notes-jsp上下文,前缀要设成/notes-jsp。然后去服务器上看上传目录里有没有文件,没有就是权限问题,给目录加写权限。
4.5 删除笔记后列表没变化
现象:点删除,弹窗确认后页面刷新,但被删的笔记还在列表里。
原因:删除的 SQL 执行了,但事务没提交,或者删除条件写错了。比如DELETE FROM t_note WHERE id = ?里的?没传进去,变成了删除所有或者删除 0 条。
解决:在 DAO 的删除方法里打印 SQL 和参数,看日志。如果是自动提交模式,executeUpdate后应该立即生效。如果用了事务,检查有没有conn.commit()。另外确认删除链接里的id参数有没有正确传到 Servlet。
5. 进阶改造:把 JSP 记事本改造成前后端分离的练手项目
5.1 先给现有系统加一层接口验证
这个项目最直接的进阶玩法,是把 Servlet 改造成 RESTful 接口,返回 JSON 而不是跳转 JSP。你不需要重写整个项目,只需要在现有 Servlet 里加一个分支:如果请求头Accept是application/json,就返回 JSON 数据;否则走原来的 JSP 渲染。这样前端可以用 Ajax 调接口,后端逻辑复用。
// 在 NoteServlet 的 doGet 里加一个 JSON 分支 String format = request.getParameter("format"); if ("json".equals(format)) { List<Note> list = noteDAO.getNotesByUserId(userId); response.setContentType("application/json;charset=UTF-8"); PrintWriter out = response.getWriter(); // 用 Gson 或 Jackson 转 JSON,这里手写简单示例 StringBuilder json = new StringBuilder("["); for (int i = 0; i < list.size(); i++) { Note n = list.get(i); json.append("{\"id\":").append(n.getId()) .append(",\"title\":\"").append(n.getTitle()).append("\"}"); if (i < list.size() - 1) json.append(","); } json.append("]"); out.print(json.toString()); out.flush(); return; } // 原来的 JSP 跳转逻辑继续走这段代码演示了怎么在同一个 Servlet 里兼容两种输出格式。format=json参数是开关,有就走 JSON,没有就走 JSP。实际项目里建议用 Gson 或 Jackson 库,手写 JSON 容易在特殊字符上翻车。改造完之后,你可以用 Postman 测接口,也可以用前端框架写页面调接口,把 JSP 彻底变成纯静态 HTML。
5.2 用连接池替换 DriverManager
现在 DAO 里每次操作都DriverManager.getConnection,频繁创建销毁连接,性能很差。改成连接池是标准做法。常见的是用 Druid 或 HikariCP。以 Druid 为例,加一个druid.properties配置文件,然后在DBUtil里初始化DataSource。
# druid.properties driverClassName=com.mysql.cj.jdbc.Driver url=jdbc:mysql://localhost:3306/note_db?useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=utf8 username=root password=your_password initialSize=5 maxActive=20然后在 Java 里加载这个配置,用DruidDataSourceFactory.createDataSource(properties)拿到DataSource。之后 DAO 里改成dataSource.getConnection(),连接用完close()会归还到池里,而不是真正关闭。这一步改完,系统能扛的并发量会明显提升,虽然个人记事本用不上,但作为练手,这是从「能跑」到「像个正经项目」的关键一步。
5.3 验证改造是否成功的三个检查点
改完之后怎么确认没改坏?我一般走这三步。第一,用原来的 JSP 页面走一遍完整流程:注册、登录、增删改查,确保功能没丢。第二,用 Postman 调note?format=json接口,看返回的 JSON 格式对不对,中文有没有乱码。第三,用ab或jmeter简单压一下接口,看连接池有没有生效,日志里有没有连接泄漏警告。
如果 JSON 接口返回 500,看日志里是不是Gson或Jackson的ClassNotFoundException,那是 jar 包没加。如果连接池启动报url解析错误,检查druid.properties里的&是不是被转义了,properties 文件里&不需要转义,直接写就行。
从那以后我每次拿到这种 JSP 老项目,都强制先跑通数据库连接再动代码,不然改了半天发现是环境问题,血泪经验。希望这份拆解能帮你少走弯路,顺利把这个记事本跑起来、改下去。
本文还有配套的精品资源,点击获取