资讯动态

JSP+Servlet+JDBC实战:从环境搭建到增删改查完整链路

发布时间:2026/10/8 3:50:01 来源:尧图企业网站定制
简介面向Java Web开发者的数据库系统应用开发资料由曾凌、沈应逵编写围绕Servlet、JSP、JDBC及Spring MVC等框架展开涵盖Java基础、MVC设计、事务处理、存储过程等数据库高级特性适合初学者系统提升也对有经验者具有参考价值。资源包共884个文件包含163个java、140个jsp、181个class等源码文件以及48个avi教学视频、17个sql脚本和多个jar/xml配置文件可直接对照运行压缩包大小222.5MB目录结构便于模块化检索。已有470人学习使用。借助源码分析与操作录屏可掌握JDBC连接数据库、ORM映射、用户认证、Ajax异步通信等实战技能并理解共通Servlet、报表打印等典型Web模块的实现方式。1. 一套 JSP Servlet JDBC 的数据库实战不是看完视频就丢是能真跑起来的整套源码曾凌、沈应逵这套《Java Web 数据库系统应用开发与实例》我拆过不止一遍每次带新人或者自己复习数据库增删改查底层链路时都会把它翻出来当底稿。资源本体是配套视频加完整工程源码覆盖面从建表、JDBC 连接、DAO 封装一路到 Servlet 控制器和 JSP 页面展示全是一条业务链路走到底没有那种「讲完原理就断片」的割裂感。它适合两类人刚入行、准备 java 面试、想把「浏览器请求怎么穿过 Servlet 落到 MySQL 再回显 JSP」讲清楚的新人以及用惯了 MyBatis-Plus、Spring Boot需要回头补 JDBC 原始模型的工作党。今天这篇我把这套资源的目录逻辑、环境坑位、核心代码写法挨个拆开照着做一次比刷三遍视频有用。2. 环境准备JDK、Tomcat、MySQL 怎么搭老资源能不能在新电脑上跑顺拿到这种带视频的源码包第一反应不是打开 IDE是先把运行环境对齐。这套资源默认是 JSP Servlet JDBC 的老三层跟现在 Spring Boot 一键启动完全不同环境配不对视频里每集都能跑你本地每集都在翻车。2.1 JDK、Tomcat、MySQL 的版本搭配经典组合和隐藏陷阱先看一套我实测过的版本对照这是老 Java Web 资源落地最容易卡死的第一关。组件资源视频里常见我现在推荐需要注意的坑JDK1.7 / 1.8JDK 8LTSJDK 11 以上跑 Tomcat 8 偶尔出现模块访问报错Tomcat7 / 8.08.5 或 9.0Tomcat 10 把 javax 改成 jakarta 命名空间老 JSP 直接编译不过MySQL5.5 / 5.75.7 或 8.0MySQL 8 的认证插件和旧驱动不兼容驱动必须换新IDEEclipse J2EEIntelliJ IDEA 2021老工程导入 IDEA 后要补 Web Facet否则没有 Run on Server为什么这套资源默认绑在老版本上因为视频录制时 Spring Boot 还没普及JSP 是主流视图技术大家连数据库用的都是DriverManager.getConnection()这一套原生的东西。今天的电脑完全能跑老代码关键是把 MySQL 驱动换成新版本URL 里补上serverTimezone再把 Tomcat 压在 9 及以下问题就解决一大半。2.2 导入源码的第一步先把工程结构读明白老式 Java Web 工程不是 Maven 结构没有pom.xml依赖全靠WEB-INF/lib下的 jar 包。我一般拿到资源包后先不急着启动把目录树拉出来对照视频目录确认每一层代码对应视频哪一集。src/ ├── com/book/dao/ # DAO 接口与实现增删改查都在这 ├── com/book/model/ # 实体类对应数据库表字段 ├── com/book/servlet/ # 控制器层接收请求、调 DAO、转发页面 ├── com/book/util/ # 数据库连接工具封装 └── com/book/filter/ # 有些版本有编码过滤器 WebContent/ ├── jsp/ # 页面列表、表单、展示页 ├── WEB-INF/ │ ├── web.xml # 部署描述符配置 Servlet 映射 │ └── lib/ # mysql-connector-java 等依赖 jar └── index.jsp逻辑说明这个结构体现的正是经典三层——model是数据载体dao负责和 MySQL 打交道servlet接 HTTP 请求再调度 DAO最后把结果交给 JSP 渲染。看源码时第一件事是去WEB-INF/lib里看一眼mysql-connector-java的版本这决定了你等会儿写 JDBC 驱动类名用com.mysql.jdbc.Driver还是com.mysql.cj.jdbc.Driver。这一步确认完后面所有代码都能对上。2.3 建库建表先跑 SQL 脚本再开项目顺序反了必踩坑视频里通常会带着建表语句但资源包里的 SQL 脚本不一定单独给一个.sql文件有时散落在某一集的笔记里。我的习惯是先按视频把表结构建出来自己存一份db.sql。下面的脚本是这套资源里典型的表结构写法CREATE DATABASE bookstore DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE bookstore; CREATE TABLE t_book ( id INT PRIMARY KEY AUTO_INCREMENT COMMENT 主键, name VARCHAR(100) NOT NULL COMMENT 书名, author VARCHAR(50) COMMENT 作者, price DECIMAL(10,2) COMMENT 定价, stock INT DEFAULT 0 COMMENT 库存 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT图书表; INSERT INTO t_book(name, author, price, stock) VALUES (Java Web程序设计, 曾凌, 59.00, 100);逻辑说明建库用utf8mb4而不是老教程里的utf8是为了让中文不乱码的同时兼容 emoji 字符COLLATE utf8mb4_general_ci指定了不区分大小写的排序规则做条件查询时更宽松。先跑脚本再开项目的理由很简单源码里实体类的字段、DAO 里的 SQL 全是按这张表设计的表结构和代码对不上一启动就是Unknown column报错到时候你根本分不清是代码问题还是数据问题白折腾一小时。3. DAO 层与 JDBC 封装把增删改查写成能复用的模块不是散落一地的 SQL这套资源真正值钱的部分在 DAO 层。视频会用好几集讲一件事为什么连接不能散落在每个方法里为什么 SQL 要交给PreparedStatement而不是拼字符串。这一章我把核心代码和选型理由拆开讲顺手把面试常问的几个点也覆盖掉。3.1 连接管理把DriverManager从业务代码里解放出来最原始的写法是每个方法里Class.forName加DriverManager.getConnection这套资源里的进阶版本会把这些收拢到一个工具类里。我用一个 DBUtil 还原这套资源的常见做法public class DBUtil { private static final String URL jdbc:mysql://localhost:3306/bookstore ?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai; private static final String USER root; private static final String PASSWORD 123456; static { try { Class.forName(com.mysql.cj.jdbc.Driver); } catch (ClassNotFoundException e) { e.printStackTrace(); } } public static Connection getConnection() throws SQLException { return DriverManager.getConnection(URL, USER, PASSWORD); } public static void close(Connection conn, PreparedStatement ps, ResultSet rs) { if (rs ! null) { try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } } if (ps ! null) { try { ps.close(); } catch (SQLException e) { e.printStackTrace(); } } if (conn ! null) { try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } }逻辑说明静态代码块里Class.forName负责注册驱动URL 里的useUnicodetruecharacterEncodingutf8是防止中文写入数据库变问号的关键参数serverTimezoneAsia/Shanghai是连接 MySQL 8 必须加的时区声明不加会报The server time zone value异常。close方法把三个资源的关闭逻辑收拢避免每个 DAO 方法里重复写三遍 try-catch。这一层封装的价值在换连接池时立刻体现业务代码全部无感只改这一个类。3.2 PreparedStatement 与 ResultSet 映射增删改查的完整模板DAO 里的每个方法都有固定套路拿连接 → 写 SQL 模板 → 绑定参数 → 执行 → 遍历结果集 → 关资源。下面是这套资源里最常见的查询方法public Book findById(int id) { String sql SELECT id, name, author, price, stock FROM t_book WHERE id ?; try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setInt(1, id); try (ResultSet rs ps.executeQuery()) { if (rs.next()) { Book book new Book(); book.setId(rs.getInt(id)); book.setName(rs.getString(name)); book.setAuthor(rs.getString(author)); book.setPrice(rs.getBigDecimal(price)); book.setStock(rs.getInt(stock)); return book; } } } catch (SQLException e) { e.printStackTrace(); } return null; }参数说明?占位符配合ps.setInt(1, id)做的是预编译SQL 结构先固定再传值这是防 SQL 注入的基本手段也是 java 面试题里最常追问的一点。setBigDecimal对应数据库DECIMAL类型别用setString传价格否则排序和计算会出问题。能放进 try-with-resources 的资源尽量放Connection、PreparedStatement、ResultSet全部自动关闭比手动finally更稳。插入和更新的写法只是换了 SQL 模板和参数绑定核心骨架完全一致public int addBook(Book book) { String sql INSERT INTO t_book(name, author, price, stock) VALUES(?, ?, ?, ?); try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setString(1, book.getName()); ps.setString(2, book.getAuthor()); ps.setBigDecimal(3, book.getPrice()); ps.setInt(4, book.getStock()); return ps.executeUpdate(); } catch (SQLException e) { e.printStackTrace(); return 0; } }逻辑说明executeUpdate返回受影响行数大于 0 表示插入成功。这套资源里所有写操作包括删除、修改全是这个结构。实战里最常翻车的地方不是 SQL 写错而是Book实体类的属性类型和ResultSet取出的类型对不上比如数据库DECIMAL用了getFloat或者DATE类型用了getString跑起来会发现数据对、类型怪然后莫名报转换异常。所以每写一个映射方法第一件事核对实体类字段类型和表结构一一对应。3.3 升级连接池只改 DBUtil不动业务层的换法老资源用的是每次DriverManager.getConnection直连开发环境能跑但一上生产就会被资源耗尽这个问题教做人。工作项目里一般要求连接池常见做法是 DBCP2 或 C3P0。这套资源的 DAO 封装在这里体现出最大的价值业务层完全不感知只改 DBUtil 内部实现。我常用的 DBCP2 配置是private static BasicDataSource dataSource new BasicDataSource(); static { dataSource.setDriverClassName(com.mysql.cj.jdbc.Driver); dataSource.setUrl(jdbc:mysql://localhost:3306/bookstore?serverTimezoneAsia/Shanghai); dataSource.setUsername(root); dataSource.setPassword(123456); dataSource.setInitialSize(5); dataSource.setMaxTotal(20); dataSource.setMaxWaitMillis(3000); } public static Connection getConnection() throws SQLException { return dataSource.getConnection(); }参数说明initialSize是启动时初始化的连接数maxTotal是最大连接数这两个值决定并发峰值下系统扛得住多少请求maxWaitMillis是拿不到连接时的等待时间设太短高并发下疯狂报错设太长请求会堆积。换成连接池后getConnection不再是新建物理连接而是从池里借用完调用close是归还而不是销毁。这套资源的 DAO 代码能做到「换池不改业务」正是因为所有连接都从 DBUtil 进出。你回头把DBUtil换成 MyBatis 的SqlSessionFactory思路也是同一个这也解释了为什么框架学习不能跳 JDBC。4. Servlet JSP 数据流打通从表单提交到数据库回显一条链路走完DAO 只是数据层真正让系统活起来的是 Servlet 和 JSP 之间的数据流转。这套资源的后半部分视频全部围绕「页面提交 → 控制器接收 → DAO 入库 → 列表页查询回显」这条链路展开。这一章我把链路拆成三段每段都有可直接抄的代码。4.1 Servlet 控制器接收表单参数、调 DAO、页面跳转的标准姿势Servlet 是这层链路的调度中枢。一个新增图书的表单提交过来控制器要做三件事设置请求编码、取参数、调 DAO然后决定是转发还是重定向。WebServlet(/addBook) public class BookAddServlet extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding(UTF-8); String name request.getParameter(name); String author request.getParameter(author); String price request.getParameter(price); String stock request.getParameter(stock); Book book new Book(); book.setName(name); book.setAuthor(author); book.setPrice(new BigDecimal(price)); book.setStock(Integer.parseInt(stock)); BookDao dao new BookDao(); dao.addBook(book); response.sendRedirect(request.getContextPath() /bookList); } }参数说明request.setCharacterEncoding(UTF-8)必须放在读取第一个参数之前否则 POST 请求的中文已经按默认编码解析完了再设也没用。sendRedirect是重定向浏览器地址栏会变触发一次新的 GET 请求如果这里用request.getRequestDispatcher(bookList.jsp).forward()地址栏不变表单重复提交的风险立刻出现。这套资源里新增成功后一般用重定向刷新列表页也不会重复插入数据这个习惯我会保留到现在的项目里。4.2 JSP 展示页EL 表达式和 JSTL 取代脚本片段老视频里常见的是在 JSP 里写% ... %脚本片段但那只是教学需要。工作里 JSP 页面要求尽量不做逻辑用 EL 和 JSTL 渲染。这里给一个列表页的推荐写法% taglib prefixc urihttp://java.sun.com/jsp/jstl/core % table border1 tr thID/thth书名/thth作者/thth价格/thth库存/th /tr c:forEach items${bookList} varbook tr td${book.id}/td td${book.name}/td td${book.author}/td td${book.price}/td td${book.stock}/td /tr /c:forEach /table逻辑说明${bookList}是 Servlet 里通过request.setAttribute(bookList, list)塞进去的数据EL 表达式按属性名自动调用Book的 getter省掉手写getAttribute再强转的代码。c:forEach items遍历集合每次循环的var变量直接绑定一个 Book 对象。如果 JSTL 标签库没引入页面会整页报错记得确认WEB-INF/lib里有jstl-1.2.jar和standard.jarIDEA 下这是老资源最容易漏的一步。4.3 从表单到数据库再到列表页结合视频目录按顺序看完整链路的实际走向是book_add.jsp的表单 POST 到/addBook→BookAddServlet取参并构建 Book →BookDao.addBook执行 INSERT → 重定向/bookList→BookListServlet调listBooks查询 → 结果塞进 request → forward 到book_list.jsp渲染。看视频时建议按这个顺序过滤先看建表和 DBUtil 那几集再看 DAO 增删改查最后看 Servlet 和 JSP 联调。这套资源里 Servlet 映射有两种写法老的用web.xml的servlet-mapping新的用WebServlet注解视频早期版本多半讲解前者代码里两者共存时不冲突但熟悉WebServlet上手更快因为不用在配置文件里来回找映射关系。5. 常见问题与避坑编译报错、中文乱码、连接不通4 条最真实的踩坑记录这套资源的本质是教学工程跑起来最大的障碍往往不是代码本身而是环境和版本差异。下面这几条是我用这套资源复现时真踩过的每一条都能对号入座。5.1 先建立排查顺序把问题分成请求层、数据层、环境层三层遇到报错别急着改代码先判断问题发生在哪一层。页面 404 或者 500查 Tomcat 部署和 Servlet 映射乱码区分是请求乱码还是数据库乱码连接报错看驱动版本和 URL 参数。这个分层排查的习惯比背具体报错更耐用因为换台机器、换个项目版本报错形式都会变但分层思路永远适用。5.2 四条高频踩坑记录现象、原因、解决第一条Tomcat 启动后访问页面 404。现象项目能正常启动Tomcat 控制台没报错但浏览器访问http://localhost:8080/bookstore/index.jsp一直 404。原因WAR 包或项目没部署到 Tomcat 的webapps下或者部署名不是bookstore。IDEA 里常见原因是 Artifact 没配好部署的路径和实际访问路径不一致。解决打开 Run Configurations 的 Deployment 页签确认 Artifact 是bookstore:war explodedApplication context 是/bookstore。建议直接访问http://localhost:8080/看 Tomcat 首页能否打开能打开说明服务正常问题只出在部署路径。第二条插入的中文数据变成问号。现象页面表单填写中文点提交后数据库里存的是???或者 JSP 页面回显乱码。原因三层中有任何一层没统一编码。第一层是 JSP 页面本身的pageEncoding第二层是 Servlet 读取请求参数的编码第三层是数据库表字符集。这三处只要一处不是 UTF-8数据就乱。解决JSP 顶部写% page contentTypetext/html;charsetUTF-8 %Servlet 里request.setCharacterEncoding(UTF-8)放在读参数之前建表语句用DEFAULT CHARSETutf8mb4。最保险的做法是加一个统一编码的 Filter在第 6 章给完整代码。第三条连接 MySQL 8 报Public Key Retrieval is not allowed。现象程序连 MySQL 8 时抛异常SQLNonTransientConnectionException提示不允许获取公钥。原因MySQL 8 默认认证插件caching_sha2_password旧版 Connector/J 协商时要求获取公钥但连接 URL 里没允许这个操作。解决URL 追加两个参数jdbc:mysql://localhost:3306/bookstore?useSSLfalseallowPublicKeyRetrievaltrueserverTimezoneAsia/ShanghaiuseSSLfalse是本地开发连不上时最常用的开关allowPublicKeyRetrievaltrue是专门解决这个公钥获取异常的。顺便把驱动换到mysql-connector-java-8.0.x同时把驱动类名改成com.mysql.cj.jdbc.Driver老驱动配 MySQL 8 会出现一套连环问题。第四条IDEA 里 import 不进来很多类标红。现象源码导入 IDEA 后servlet、jsp相关的类全部标红代码运行不起来。原因老工程是 Eclipse 结构和 classpath 体系IDEA 需要重新识别 Web Facet另外 Tomcat 提供的servlet-api.jar没有加入项目依赖编译器找不到这些类。解决Project Structure 里找到 Facets把 Web 模块指向WebContent目录再在 Dependencies 里添加 Tomcat 的servlet-api.jar。标红消失后去 Artifacts 里把 Web 目录的对应关系确认一遍才能正常 Deployment。这个坑我见过太多次有人误以为是源码有问题弃用了一套好资源其实是打包方式在作怪。6. 验证与进阶从「能跑」到「敢改」把这套源码变成自己的 CRUD 底座项目跑通不算结束我有个习惯拿到这类源码先做一轮完整的功能验证再往里面加自己的代码确认扩展点是否健康。验证用的是最笨但最有效的方法把增删改查全部手动走一遍。打开页面连续插入 10 条带中文的图书数据再修改其中 3 条的库存删除 2 条最后重新打开列表页拿 MySQL 客户端执行SELECT COUNT(*) FROM t_book核对数量。数量对得上说明 JDBC 连接、事务提交、页面渲染整条链路是通的。测试完顺手再对着mybatisplus根据实体类生成创建表的sql语句这类资料做对比你会发现老 JDBC 的手写 SQL 和框架的自动化生成本质是同一件事只是封装程度不同。验证通过后我建议做两个小改造。第一个是加统一编码过滤器彻底告别中文乱码WebFilter(/*) public class CharacterEncodingFilter implements Filter { public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { request.setCharacterEncoding(UTF-8); response.setCharacterEncoding(UTF-8); chain.doFilter(request, response); } }这个过滤器加在src下比在每一个 Servlet 里写setCharacterEncoding干净得多加了它之后前面乱码问题直接少 80%。第二个改造是把重复的 DAO 方法抽一个BaseDao泛型基类把findById、findAll这类通用操作下沉新表接入只需要实体类和 SQL代码量减半。拿这套资源时我也栽过跟头当时急着在 IDEA 里启动跳过了建表脚本结果项目起来后Book实体和表结构差一个字段页面一直报Unknown column我以为是源码错误排查了一下午才发现是自己偷懒。从那以后我每次拿到视频配套源码都强制先跑数据库脚本、核对实体类字段再谈启动这个动作被我从 Java Web 沿用到后来的 Spring Boot 项目省掉的调试时间不计其数。希望这轮的梳理能帮你在同样的路上少耗几个小时。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑