序章:当Web程序遇上"数据焦虑"
各位小伙伴好呀~今天咱们要聊的可是JavaWeb开发里的"灵魂伴侣"——JDBC数据库连接!想象一下这个场景:你兴冲冲地写了个网页程序,用户输入了姓名年龄,结果数据只能存在内存里,服务器一重启就"失忆"了。是不是像极了金鱼脑男友?这时候就需要给咱们的Web程序找个"持久化"的另一半啦!
第一章:JDBC的前世今生
在远古的Java 1.0时代,程序员们想要操作数据库可费劲了——每个数据库厂商都有自己的驱动API,写套MySQL代码换个Oracle就得全盘重构。直到Sun公司推出JDBC(Java Database Connectivity),就像给数据库世界制定了"普通话"标准。现在不管是MySQL、Oracle还是SQLite,统统都能用同一套API搞定,是不是突然感觉代码"兼容性"buff叠满了?
📌 技术冷知识:
JDBC其实是个"翻译官",它把Java代码翻译成数据库能听懂的SQL方言。不同数据库的JDBC驱动就像不同国家的翻译,虽然语言不同,但接口是统一的。
第二章:搭建"相亲"环境
工欲善其事必先利其器,咱们先来准备三件套:
- 数据库:推荐新手村必备MySQL(毕竟免费又好用),记得装个图形化工具如Navicat(或者JetBrains家的DataGrip甚至IDEA已经内置了,学生可以去看我之前的免费JetBrains全家桶教育免费的申请教程: 【JetBrains正版激活】IDEA正版软件学生免费获取,申请指南 ),操作起来像在数据库里"逛街"
- 驱动包:去MySQL官网下载最新JDBC驱动(现在叫Connector/J),就像给程序装个"翻译器": https://downloads.mysql.com/archives/c-j/
在页面选择对应的驱动版本根据你的数据库版本而定,MySQL8.0后的数据库驱动程序请选择后缀带8.x的驱动,我这里用的是MySQL 5.7所以还是选择后缀5.x的驱动版本 😀 注意,这里下载下来的是一个独立的压缩包,解压后找到驱动对应名字的jar包,一般是有两个的,选带bin后缀的jar包复制进项目lib目录 - 开发环境:Eclipse/IDEA任选,推荐IDEA(智能提示简直开挂)
💡 安装小剧场:
把驱动jar包放进项目的WEB-INF/lib目录时,记得要"右键→Add as Library",不然就像给程序准备了满汉全席却忘了递筷子~
第三章:JDBC连接五部曲(带弹幕解说版)
Step1:加载驱动(破冰环节)
Class.forName("com.mysql.jdbc.Driver");
// 新版驱动类名变了哦,别记错"身份证号",8.0以后的驱动是com.mysql.cj.jdbc.Driver
// 弹幕:驱动类加载失败?检查MySQL版本是否匹配!
这行代码就像给数据库寄了封介绍信,告诉它:"有个Java程序想认识你~"
Step2:建立连接(初次约会)
String url = "jdbc:mysql://localhost:3306/mydb?useSSL=false&serverTimezone=UTC";
String user = "root";
String password = "123456";
Connection conn = DriverManager.getConnection(url, user, password);
// 弹幕:连接超时?检查防火墙是否放行3306端口!
// 弹幕:时区报错?在url里明确指定serverTimezone=Asia/Shanghai
URL就像约会地点,记得把mydb改成你的数据库名。时区设置是常见坑点,就像跨国恋要倒时差~
Step3:创建Statement(准备话题)
Statement stmt = conn.createStatement();
// 弹幕:Statement有两种类型:TYPE_SCROLL_INSENSITIVE(可滚动)和TYPE_FORWARD_ONLY(默认)
Statement就像聊天时的"开场白",接下来要说什么SQL语句由它传递。
Step4:执行SQL(深入交流)
// 查询示例(带分页)
ResultSet rs = stmt.executeQuery("SELECT * FROM users LIMIT 10 OFFSET 20");
while(rs.next()) {
String name = rs.getString("name");
int age = rs.getInt("age");
// 弹幕:列名不存在?检查数据库字段名是否匹配!
// 弹幕:结果集遍历要用next()方法,不能用for循环!
}
// 更新示例(带事务)
conn.setAutoCommit(false);
try {
stmt.executeUpdate("UPDATE users SET age=26 WHERE name='小王'");
stmt.executeUpdate("INSERT INTO logs VALUES('年龄更新')");
conn.commit();
} catch (SQLException e) {
conn.rollback();
}
ResultSet就像数据库返回的"情书",用next()方法逐行读取,是不是像极了拆盲盒?
Step5:关闭资源(礼貌告别)
// 传统写法(容易漏关)
rs.close();
stmt.close();
conn.close();
// 现代写法(try-with-resources,自动关闭)
try (Connection conn = DriverManager.getConnection(url, user, password);
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(sql)) {
// 操作代码
} // 自动关闭资源
// 弹幕:关闭顺序要反过来:ResultSet→Statement→Connection
第四章:常见"翻车"现场(带解决方案)
ClassNotFoundException:
- 🔍 检查驱动包是否在WEB-INF/lib
- 🔍 检查驱动类名是否正确(MySQL8+用
com.mysql.cj.jdbc.Driver
) - 🔍 是否执行了Class.forName()加载驱动?
Access denied for user:
- 🔑 检查用户名密码是否正确
- 🔑 登录MySQL执行
SELECT user,host FROM mysql.user
,确认用户有远程访问权限 - 🔑 尝试用
GRANT ALL PRIVILEGES ON mydb.* TO 'root'@'%'
授权
SSL连接问题:
- 🔒 在URL中添加
useSSL=false
- 🔒 或者配置MySQL的SSL证书(生产环境推荐)
- 🔒 在URL中添加
中文乱码:
- 🌐 URL添加
characterEncoding=UTF-8
- 🌐 确保数据库、表、字段都设置为utf8mb4
- 🌐 连接后执行
SET NAMES utf8mb4
- 🌐 URL添加
ResultSet关闭异常:
- 💡 使用try-with-resources自动关闭
- 💡 单独关闭时检查是否为null
第五章:进阶玩法(感情升温)
PreparedStatement防SQL注入:
String sql = "SELECT * FROM users WHERE name = ? AND age > ?"; PreparedStatement pstmt = conn.prepareStatement(sql); pstmt.setString(1, userInput); // 索引从1开始! pstmt.setInt(2, 18); // 弹幕:参数化查询就像给SQL语句戴"安全套" // 弹幕:预编译语句还能提升性能(数据库会缓存执行计划)
连接池优化(以HikariCP为例):
# 配置文件:src/main/resources/hikari.properties dataSourceClassName=com.mysql.cj.jdbc.MysqlDataSource dataSource.url=jdbc:mysql://localhost:3306/mydb dataSource.user=root dataSource.password=123456 maximumPoolSize=10
HikariConfig config = new HikariConfig("/hikari.properties"); HikariDataSource ds = new HikariDataSource(config); Connection conn = ds.getConnection(); // 弹幕:连接池就像给数据库"排班",避免频繁创建销毁连接 // 弹幕:HikariCP是目前最快的连接池,Spring Boot默认使用
事务管理(实战版):
conn.setAutoCommit(false); Savepoint svpt = conn.setSavepoint("updateStart"); try { // 转账操作 stmt.executeUpdate("UPDATE accounts SET balance=balance-100 WHERE id=1"); stmt.executeUpdate("UPDATE accounts SET balance=balance+100 WHERE id=2"); conn.commit(); } catch (SQLException e) { conn.rollback(svpt); // 回滚到保存点 // 处理异常... } // 弹幕:保存点就像游戏存档,可以部分回滚 // 弹幕:默认隔离级别是REPEATABLE_READ,可能需要根据业务调整
第六章:实战演练(约会实战)
任务:做个简单用户管理系统,包含增删改查功能
步骤:
建表SQL:
CREATE TABLE users ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(50) NOT NULL, age INT, create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
JSP页面(list.jsp):
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <table> <tr><th>ID</th><th>姓名</th><th>年龄</th><th>操作</th></tr> <c:forEach items="${userList}" var="user"> <tr> <td>${user.id}</td> <td>${user.name}</td> <td>${user.age}</td> <td> <a href="edit.jsp?id=${user.id}">编辑</a> <a href="delete?id=${user.id}">删除</a> </td> </tr> </c:forEach> </table> <a href="add.jsp">新增用户</a>
Servlet代码(UserServlet.java):
@WebServlet("/user") public class UserServlet extends HttpServlet { private static final String URL = "jdbc:mysql://localhost:3306/mydb?useSSL=false"; private static final String USER = "root"; private static final String PASS = "123456"; protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String action = request.getParameter("action"); Connection conn = null; try { conn = DriverManager.getConnection(URL, USER, PASS); if ("list".equals(action)) { listUsers(conn, request, response); } else if ("delete".equals(action)) { deleteUser(conn, request, response); } } catch (SQLException e) { throw new ServletException("数据库错误", e); } finally { if (conn != null) try { conn.close(); } catch (SQLException ignore) {} } } private void listUsers(Connection conn, HttpServletRequest request, HttpServletResponse response) throws SQLException, ServletException, IOException { Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery("SELECT * FROM users"); List<User> userList = new ArrayList<>(); while (rs.next()) { userList.add(new User( rs.getInt("id"), rs.getString("name"), rs.getInt("age") )); } request.setAttribute("userList", userList); request.getRequestDispatcher("list.jsp").forward(request, response); } // 其他方法(add/edit/delete)类似... }
🛠️ 调试技巧:
- 在catch块里打印完整异常堆栈:
e.printStackTrace()
- 使用IDEA的Database工具直接查看数据库变化
- 在SQL语句前加
SELECT NOW()
测试连接是否成功 - 用Postman测试RESTful接口
🎯 性能优化建议:
- 查询时使用
SELECT id,name FROM users
代替SELECT *
- 对频繁查询的字段(如name)加索引
- 使用连接池管理数据库连接
- 考虑使用缓存(如Redis)存储热点数据
结语:与数据库的"长久之计"
通过JDBC连接数据库就像谈场细水长流的恋爱:开始时要小心翼翼建立连接,过程中要妥善处理各种异常,最后还要优雅地关闭资源。随着技术深入,你会遇到ORM框架(如MyBatis、Hibernate),就像给这场恋爱找个"红娘",让数据操作变得更优雅高效。但无论如何,理解JDBC原理都是打好JavaWeb基础的关键一课。
快去动手实践吧!遇到问题记得回来看看这篇笔记,咱们下期聊聊Servlet~ 🚀
评论 (0)