JDBC(Java Database Connectivity)是 Java 官方定义的数据库访问标准接口,通过它可以在 Java 程序中连接 MySQL,执行 SQL、处理结果集。
说明:旧驱动
com.mysql.jdbc.Driver已废弃。MySQL 8+ 使用驱动类com.mysql.cj.jdbc.Driver,驱动包为mysql-connector-j(原 mysql-connector-java)。
本教程使用到的 Websites 表 SQL 文件:websites.sql。
一、环境准备
1. 引入驱动包
Maven(pom.xml)
<!-- MySQL 8.x 驱动,推荐 -->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>8.4.0</version>
</dependency>
Gradle
implementation 'com.mysql:mysql-connector-j:8.4.0'
2. 准备测试数据表
沿用前面教程的 websites 表,方便统一测试:
CREATE DATABASE IF NOT EXISTS test; USE test; DROP TABLE IF EXISTS websites; CREATE TABLE websites ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(20), url VARCHAR(255), alexa INT, country VARCHAR(10) );INSERT INTO websites(name,url,alexa,country) VALUES
('Google','https://www.google.com',1,'USA'),
('淘宝','https://www.taobao.com',13,'CN'),
('runoops教程','http://runoops.com',6988,'CN'),
('微博','http://weibo.com',22,'CN'),
('Facebook','https://www.facebook.com',3,'USA');
二、基础连接(原生JDBC,不推荐生产)
原生写法:手动获取连接、Statement、结果集,务必关闭资源;生产环境不要直接用,优先使用连接池 HikariCP。
import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException;public class JdbcConnectDemo {
public static void main(String[] args) {
// 连接URL
String url = "jdbc:mysql://localhost:3306/test"
+ "?useSSL=false"
+ "&serverTimezone=Asia/Shanghai"
+ "&characterEncoding=utf8mb4";
String user = "root";
String password = "你的数据库密码"; Connection conn = null; try { // MySQL8.0+ 可省略 Class.forName,驱动自动注册 // Class.forName("com.mysql.cj.jdbc.Driver"); conn = DriverManager.getConnection(url, user, password); System.out.println("数据库连接成功"); } catch (SQLException e) { e.printStackTrace(); } finally { // 关闭连接 if (conn != null) { try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } }}
URL 参数说明:
serverTimezone=Asia/Shanghai:必须配置,解决时区报错characterEncoding=utf8mb4:完整支持中文、EmojiuseSSL=false:本地开发关闭SSL;生产环境建议开启
三、CRUD 增删改查(推荐 PreparedStatement 预处理,防SQL注入)
✅ 禁止使用 Statement 拼接SQL,极易产生SQL注入;一律使用
PreparedStatement占位符?。
3.1 查询 SELECT
import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException;public class JdbcSelect {
public static void main(String[] args) {
String url = "jdbc:mysql://localhost:3306/test?useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=utf8mb4";
String user = "root";
String pwd = "你的数据库密码"; String sql = "SELECT id,name,url,alexa,country FROM websites WHERE alexa < ?"; try (Connection conn = DriverManager.getConnection(url, user, pwd); PreparedStatement pstmt = conn.prepareStatement(sql)) { // 设置占位符参数,下标从1开始 pstmt.setInt(1, 50); ResultSet rs = pstmt.executeQuery(); // 遍历结果集 while (rs.next()) { int id = rs.getInt("id"); String name = rs.getString("name"); String urlVal = rs.getString("url"); int alexa = rs.getInt("alexa"); String country = rs.getString("country"); System.out.printf("id:%d, name:%s, url:%s, alexa:%d, country:%s%n", id, name, urlVal, alexa, country); } rs.close(); } catch (SQLException e) { e.printStackTrace(); } }}
3.2 插入 INSERT
public class JdbcInsert { public static void main(String[] args) { String url = "jdbc:mysql://localhost:3306/test?useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=utf8mb4"; String user = "root"; String pwd = "你的数据库密码"; String sql = "INSERT INTO websites(name,url,alexa,country) VALUES (?,?,?,?)"; try (Connection conn = DriverManager.getConnection(url, user, pwd); PreparedStatement pstmt = conn.prepareStatement(sql)) { pstmt.setString(1, "Java教程"); pstmt.setString(2, "[https://demo.java.com](https://demo.java.com)"); pstmt.setInt(3, 12345); pstmt.setString(4, "CN"); int affectedRows = pstmt.executeUpdate(); System.out.println("新增影响行数:" + affectedRows); } catch (SQLException e) { e.printStackTrace(); } }}
3.3 更新 UPDATE
String sql = "UPDATE websites SET name = ?, url = ? WHERE id = ?";
try (Connection conn = DriverManager.getConnection(url, user, pwd);
PreparedStatement pstmt = conn.prepareStatement(sql)) {
pstmt.setString(1, "新网站名称");
pstmt.setString(2, "[https://new-demo.com](https://new-demo.com)");
pstmt.setInt(3, 6);
int affectedRows = pstmt.executeUpdate();
System.out.println("更新行数:" + affectedRows);
} catch (SQLException e) {
e.printStackTrace();
}
3.4 删除 DELETE
String sql = "DELETE FROM websites WHERE id = ?";
try (Connection conn = DriverManager.getConnection(url, user, pwd);
PreparedStatement pstmt = conn.prepareStatement(sql)) {
pstmt.setInt(1, 6);
int affectedRows = pstmt.executeUpdate();
System.out.println("删除行数:" + affectedRows);
} catch (SQLException e) {
e.printStackTrace();
}
注意:
executeQuery()用于查询(返回ResultSet);executeUpdate()用于 INSERT / UPDATE / DELETE,返回受影响行数。
四、事务处理
JDBC 默认自动提交(每一条SQL自动commit);事务场景需要手动关闭自动提交,成功则commit,异常rollback。
try (Connection conn = DriverManager.getConnection(url, user, pwd)) { // 关闭自动提交,开启事务 conn.setAutoCommit(false);String sql1 = "UPDATE xxx SET ... WHERE id=?"; String sql2 = "UPDATE xxx SET ... WHERE id=?"; try (PreparedStatement p1 = conn.prepareStatement(sql1); PreparedStatement p2 = conn.prepareStatement(sql2)) { // 设置参数、执行SQL p1.executeUpdate(); p2.executeUpdate(); // 全部成功,提交事务 conn.commit(); System.out.println("事务提交成功"); } catch (SQLException e) { // 异常回滚 conn.rollback(); System.err.println("事务回滚"); throw e; }} catch (SQLException e) {
e.printStackTrace();
}
五、生产推荐:HikariCP 连接池
DriverManager 每次新建连接性能极差,Java 项目标准方案:HikariCP(SpringBoot默认连接池)。
Maven 引入 HikariCP
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
<version>5.0.1</version>
</dependency>
HikariCP 简单示例
import com.zaxxer.hikari.HikariConfig; import com.zaxxer.hikari.HikariDataSource; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet;public class HikariDemo {
public static void main(String[] args) throws Exception {
HikariConfig config = new HikariConfig();
config.setJdbcUrl("jdbc:mysql://localhost:3306/test?useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=utf8mb4");
config.setUsername("root");
config.setPassword("你的数据库密码");
config.setMinimumIdle(5); // 最小空闲连接
config.setMaximumPoolSize(10);// 最大连接数 try (HikariDataSource ds = new HikariDataSource(config); Connection conn = ds.getConnection(); PreparedStatement pstmt = conn.prepareStatement("SELECT * FROM websites")) { ResultSet rs = pstmt.executeQuery(); while (rs.next()) { System.out.println(rs.getString("name")); } } }}
六、错误处理与调试
- 使用 try-with-resources(
try()语法),自动关闭 Connection、PreparedStatement、ResultSet,避免资源泄漏。 - SQLException 包含错误码
e.getErrorCode()、SQL状态码e.getSQLState()。Access denied for user:账号密码错误、授权问题Unknown database:数据库名错误Table doesn't exist:表不存在Communications link failure:网络、防火墙、MySQL服务未启动、时区配置错误
- 开发环境打印完整异常栈;生产环境禁止把数据库原始异常直接返回前端,仅记录日志,返回通用提示。
- 调试:打印完整执行SQL、参数;在MySQL开启慢查询日志定位慢SQL。
七、性能优化与安全建议
安全
- 必须使用
PreparedStatement预处理,禁止字符串拼接SQL,防止SQL注入。 - 数据库账号最小权限原则,业务账号不允许 DROP、ALTER。
- 数据库密码不要硬编码写代码,放到配置文件/环境变量,配置文件禁止提交到代码仓库。
SQL 优化
- 避免
SELECT *,只查询需要的字段,减少网络传输。 - 查询务必加 LIMIT,防止一次性读取海量数据 OOM。
- WHERE、ORDER BY、GROUP BY 字段建立索引;
%前缀模糊查询会索引失效。 - 批量操作使用
addBatch()+executeBatch()批量执行,不要循环单条执行。
连接池优化
- 使用 HikariCP,不要使用 DriverManager 原生连接。
- 合理设置
maximumPoolSize,不要过大,防止打满MySQL连接。 - 事务尽量简短,缩短持有连接时间,减少锁竞争、死锁。
- 及时释放连接;try-with-resources 自动释放资源,杜绝连接泄漏。
字符与时区
统一 utf8mb4;URL 必须配置 serverTimezone=Asia/Shanghai,避免时间异常。
八、JDBC 核心接口简要说明
| 接口 | 作用 |
|---|---|
| Connection | 数据库连接对象 |
| PreparedStatement | 预编译SQL,推荐,防注入 |
| Statement | 静态SQL,不推荐,存在注入风险 |
| ResultSet | SQL查询结果集 |
| DriverManager | 驱动管理器(原生,生产不推荐) |
0 条笔记