MySQL Connector/J 完全教程
Connector/J 是 MySQL 官方提供的 Java JDBC 驱动程序,它允许 Java 应用程序通过标准的 JDBC API 与 MySQL 数据库进行交互。

目录
- 环境准备
- 安装 Java Development Kit (JDK)
- 安装 MySQL 服务器
- 获取 Connector/J 驱动
- 第一个连接程序
- 添加依赖
- 编写 Java 代码
- 运行与排错
- 核心操作:增删改查 (CRUD)
- 查询数据
- 插入数据
- 更新数据
- 删除数据
- 使用
PreparedStatement防止 SQL 注入- 什么是 SQL 注入
PreparedStatement的优势- 代码示例
- 处理结果集 (ResultSet)
- 遍历结果
- 获取不同类型的数据
- 事务管理
- 什么是事务
- ACID 特性
- 代码示例
- 连接池 (Connection Pooling)
- 为什么需要连接池
- 主流连接池库
- 使用 HikariCP 示例
- 高级配置与最佳实践
- 配置 URL 参数
- 资源管理 (关闭
Connection,Statement,ResultSet) - 使用
try-with-resources(推荐) - 错误处理
环境准备
在开始之前,请确保你的系统上已安装以下软件:
a. 安装 Java Development Kit (JDK)
Connector/J 是一个 Java 库,所以你需要 JDK,推荐使用 JDK 8 或更高版本。
验证安装: 打开终端或命令提示符,输入:
java -version javac -version
如果显示版本号,则表示安装成功。

b. 安装 MySQL 服务器
你需要一个 MySQL 数据库来连接,你可以从 MySQL 官网 下载并安装。
验证安装并创建用户/数据库:
- 登录到 MySQL:
mysql -u root -p
- 创建一个用于测试的数据库:
CREATE DATABASE mytestdb;
- 创建一个专门的用户并授予权限(这是最佳实践):
CREATE USER 'javauser'@'localhost' IDENTIFIED BY 'your_strong_password'; GRANT ALL PRIVILEGES ON mytestdb.* TO 'javauser'@'localhost'; FLUSH PRIVILEGES;
c. 获取 Connector/J 驱动
你可以通过以下两种方式获取驱动:
手动下载 (适合传统项目)
访问 MySQL Connector/J 下载页面,下载最新的平台独立的 ZIP 压缩包(Platform Independent),解压后,你会得到一个 .jar 文件(mysql-connector-j-8.0.xx.jar),在你的 Java 项目中,需要将此 JAR 文件添加到类路径中。

使用构建工具 (强烈推荐) 在现代 Java 项目中,我们通常使用 Maven 或 Gradle 来管理依赖。
Maven (pom.xml):
在 pom.xml 文件中添加以下依赖:
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>8.0.33</version> <!-- 建议使用最新稳定版 -->
</dependency>
Gradle (build.gradle):
implementation 'com.mysql:mysql-connector-j:8.0.33' // 建议使用最新稳定版
构建工具会自动帮你下载并管理依赖。
第一个连接程序
让我们编写一个简单的 Java 程序来连接到我们刚刚创建的 mytestdb 数据库。
a. 添加依赖
如果你使用 Maven 或 Gradle,请确保已添加上述依赖,如果你是手动管理,请将下载的 JAR 文件添加到你的 IDE 项目库中。
b. 编写 Java 代码
创建一个 JdbcExample.java 文件。
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
public class JdbcExample {
// 数据库连接信息
private static final String DB_URL = "jdbc:mysql://localhost:3306/mytestdb";
private static final String USER = "javauser";
private static final String PASS = "your_strong_password";
public static void main(String[] args) {
// try-with-resources 语句,确保连接在完成后自动关闭
try (Connection conn = DriverManager.getConnection(DB_URL, USER, PASS)) {
if (conn != null) {
System.out.println("成功连接到数据库!");
// 可以在这里执行其他数据库操作
}
} catch (SQLException e) {
System.err.println("连接数据库失败!");
e.printStackTrace();
}
}
}
代码解释:
DB_URL: 连接字符串。jdbc:mysql://: 协议和子协议,表示使用 MySQL 的 JDBC 驱动。localhost:3306: 数据库服务器的地址和端口。/mytestdb: 要连接的数据库名称。
USER和PASS: 你之前创建的数据库用户名和密码。DriverManager.getConnection(): 这是获取数据库连接的核心方法,它会尝试加载所有可用的 JDBC 驱动,并使用提供的 URL、用户名和密码建立连接。try-with-resources: 这是一个 Java 7 引入的语法糖,任何实现了AutoCloseable接口的资源(如Connection)都可以放在try后面的括号中,当try块执行完毕(无论是否发生异常),这个资源都会被自动关闭,有效防止资源泄漏。
c. 运行与排错
- 成功: 如果看到 "成功连接到数据库!",说明一切正常。
- 失败: 如果出现
SQLException,请检查:- MySQL 服务器是否正在运行?
- 数据库名称 (
mytestdb)、用户名 (javauser)、密码是否正确? - 防火墙是否阻止了连接?
- Connector/J 的 JAR 文件是否在类路径中?
核心操作:增删改查 (CRUD)
成功连接后,我们就可以执行 SQL 语句了,执行 SQL 的核心接口是 Statement 和 PreparedStatement。
a. 查询数据
使用 Statement.executeQuery() 来执行 SELECT 语句,它会返回一个 ResultSet 对象。
import java.sql.*;
public class SelectExample {
private static final String DB_URL = "jdbc:mysql://localhost:3306/mytestdb";
private static final String USER = "javauser";
private static final String PASS = "your_strong_password";
public static void main(String[] args) {
String sql = "SELECT id, name, email FROM users";
try (Connection conn = DriverManager.getConnection(DB_URL, USER, PASS);
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(sql)) {
System.out.println("用户列表:");
System.out.println("----------------------------");
// 遍历结果集
while (rs.next()) {
// 通过列名获取数据,更具可读性且不易出错
int id = rs.getInt("id");
String name = rs.getString("name");
String email = rs.getString("email");
System.out.println("ID: " + id + ", 姓名: " + name + ", 邮箱: " + email);
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
ResultSet 方法:
rs.next(): 将光标移动到下一行,如果存在下一行,则返回true,否则返回false,这是遍历结果集的标准方式。rs.getXxx("column_name"): 根据列名获取指定类型的数据。Xxx可以是String,Int,Double,Date等。rs.getXxx(int columnIndex): 根据列的索引(从 1 开始)获取数据,不推荐使用,因为当 SQL 顺序改变时,代码容易出错。
b. 插入数据
使用 Statement.executeUpdate() 来执行 INSERT, UPDATE, DELETE 语句,它返回一个整数,表示受影响的行数。
import java.sql.*;
public class InsertExample {
// ... DB_URL, USER, PASS 同上 ...
public static void main(String[] args) {
String sql = "INSERT INTO users (name, email) VALUES ('张三', 'zhangsan@example.com')";
try (Connection conn = DriverManager.getConnection(DB_URL, USER, PASS);
Statement stmt = conn.createStatement()) {
int affectedRows = stmt.executeUpdate(sql);
if (affectedRows > 0) {
System.out.println("成功插入 " + affectedRows + " 行数据。");
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
c. 更新数据
// ... DB_URL, USER, PASS 同上 ... String sql = "UPDATE users SET email = 'new.email@example.com' WHERE name = '张三'"; // ... 其余代码与 InsertExample 类似 ...
d. 删除数据
// ... DB_URL, USER, PASS 同上 ... String sql = "DELETE FROM users WHERE name = '张三'"; // ... 其余代码与 InsertExample 类似 ...
使用 PreparedStatement 防止 SQL 注入
直接拼接 SQL 字符串是非常危险的,会导致 SQL 注入 攻击。
a. 什么是 SQL 注入
假设你的登录逻辑是这样写的:
// 危险的代码!
String username = request.getParameter("username");
String password = request.getParameter("password");
String sql = "SELECT * FROM users WHERE username = '" + username + "' AND password = '" + password + "'";
攻击者可以在用户名输入框中输入:' OR '1'='1,最终执行的 SQL 会变成:
SELECT * FROM users WHERE username = '' OR '1'='1' AND password = '...'
这个 WHERE 条件永远为真,攻击者就能轻易登录。
b. PreparedStatement 的优势
PreparedStatement 是预编译的 SQL 语句,它将 SQL 语句和数据分开处理,从而从根本上杜绝了 SQL 注入的风险。
- 安全性高: 数据被作为参数传递,数据库驱动会对其进行转义,不会被视为 SQL 代码的一部分。
- 性能高: 如果需要多次执行同一条 SQL(只是参数不同),数据库可以重用预编译的计划,提高效率。
- 代码更清晰: 使用 作为占位符,代码更整洁。
c. 代码示例
import java.sql.*;
public class PreparedStatementExample {
// ... DB_URL, USER, PASS 同上 ...
public static void main(String[] args) {
// 使用 ? 作为占位符
String sql = "INSERT INTO users (name, email) VALUES (?, ?)";
try (Connection conn = DriverManager.getConnection(DB_URL, USER, PASS);
// 创建 PreparedStatement 对象
PreparedStatement pstmt = conn.prepareStatement(sql)) {
// 设置参数 (index 从 1 开始)
pstmt.setString(1, "李四");
pstmt.setString(2, "lisi@example.com");
// 执行更新
int affectedRows = pstmt.executeUpdate();
System.out.println("成功插入 " + affectedRows + " 行数据。");
// 可以重用同一个 pstmt 来插入另一条数据
pstmt.setString(1, "王五");
pstmt.setString(2, "wangwu@example.com");
affectedRows = pstmt.executeUpdate();
System.out.println("成功插入 " + affectedRows + " 行数据。");
} catch (SQLException e) {
e.printStackTrace();
}
}
}
对于查询,PreparedStatement 的用法类似:
String sql = "SELECT * FROM users WHERE name = ?";
try (Connection conn = ...;
PreparedStatement pstmt = conn.prepareStatement(sql)) {
pstmt.setString(1, "张三");
try (ResultSet rs = pstmt.executeQuery()) {
// 处理结果集
}
}
处理结果集 (ResultSet)
ResultSet 是一个指向 SQL 查询结果集数据的游标,默认情况下,这个游标位于第一行之前。
rs.next(): 移动到下一行,这是最常用的方法。rs.previous(): 移动到上一行。rs.first()/rs.last(): 移动到第一行/最后一行。rs.beforeFirst()/rs.afterLast(): 移动到第一行之前/最后一行之后。rs.isFirst()/rs.isLast(): 判断是否在第一行/最后一行。rs.getMetaData(): 获取结果集的元数据(如列名、列类型等),可用于动态处理结果。
事务管理
事务是一组原子性的 SQL 操作,要么全部成功,要么全部失败。
a. 什么是事务
ACID 特性:
- 原子性: 事务是一个不可分割的工作单位。
- 一致性: 事务必须使数据库从一个一致性状态变换到另一个一致性状态。
- 隔离性: 一个事务的执行不能被其他事务干扰。
- 持久性: 一个事务一旦提交,它对数据库中数据的改变就是永久性的。
b. 代码示例
默认情况下,JDBC 的每个 SQL 语句都在一个单独的事务中自动提交,要管理事务,需要手动关闭自动提交模式。
import java.sql.*;
public class TransactionExample {
// ... DB_URL, USER, PASS 同上 ...
public static void main(String[] args) {
Connection conn = null;
try {
conn = DriverManager.getConnection(DB_URL, USER, PASS);
// 关闭自动提交,开启事务
conn.setAutoCommit(false);
Statement stmt = conn.createStatement();
// 操作1:从账户A转出100元
stmt.executeUpdate("UPDATE accounts SET balance = balance - 100 WHERE name = 'Alice'");
// 模拟一个错误
// if (true) throw new RuntimeException("模拟出错!");
// 操作2:向账户B转入100元
stmt.executeUpdate("UPDATE accounts SET balance = balance + 100 WHERE name = 'Bob'");
// 如果所有操作都成功,则提交事务
conn.commit();
System.out.println("事务提交成功!");
} catch (SQLException e) {
// 如果发生任何异常,则回滚事务
try {
if (conn != null) {
conn.rollback();
System.out.println("事务已回滚!");
}
} catch (SQLException ex) {
ex.printStackTrace();
}
e.printStackTrace();
} finally {
// 恢复自动提交模式(可选,但推荐)
if (conn != null) {
try {
conn.setAutoCommit(true);
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
关键点:
conn.setAutoCommit(false);: 关闭自动提交。conn.commit();: 提交事务,使更改永久生效。conn.rollback();: 回滚事务,撤销所有未提交的更改。- 通常在
catch块中调用rollback()。
连接池 (Connection Pooling)
创建和销毁数据库连接是一个非常耗费资源的操作,连接池通过预先创建一组连接并缓存起来,应用程序需要时从中获取,用完后归还给池,而不是销毁,从而极大地提高了性能。
a. 为什么需要连接池
- 性能提升: 避免了频繁创建和销毁连接的开销。
- 资源控制: 限制了数据库连接的最大数量,防止因连接过多而压垮数据库。
- 稳定性: 提供了更可靠的连接管理。
b. 主流连接池库
- HikariCP: 目前性能最好的连接池,是 Spring Boot 2.x 的默认选择。
- Apache DBCP: 老牌连接池,功能稳定。
- C3P0: 另一个老牌连接池。
c. 使用 HikariCP 示例
添加 HikariCP 依赖 (Maven):
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
<version>5.0.1</version> <!-- 使用最新版本 -->
</dependency>
编写代码
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
public class HikariCPExample {
public static void main(String[] args) {
// 1. 配置 HikariCP
HikariConfig config = new HikariConfig();
config.setJdbcUrl("jdbc:mysql://localhost:3306/mytestdb");
config.setUsername("javauser");
config.setPassword("your_strong_password");
// 连接池配置 (可选)
config.addDataSourceProperty("cachePrepStmts", "true");
config.addDataSourceProperty("prepStmtCacheSize", "250");
config.addDataSourceProperty("prepStmtCacheSqlLimit", "2048");
config.setMaximumPoolSize(10); // 最大连接数
config.setMinimumIdle(5); // 最小空闲连接数
config.setConnectionTimeout(30000); // 连接超时时间 (毫秒)
// 2. 创建数据源
HikariDataSource dataSource = new HikariDataSource(config);
// 3. 从连接池获取连接
try (Connection conn = dataSource.getConnection();
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT 'Hello from HikariCP!' as message")) {
if (rs.next()) {
System.out.println(rs.getString("message"));
}
} catch (Exception e) {
e.printStackTrace();
} finally {
// 4. 应用关闭时,关闭数据源
if (dataSource != null && !dataSource.isClosed()) {
dataSource.close();
}
}
}
}
关键点:
HikariDataSource是连接池的管理者,它应该在整个应用生命周期内只创建一次(通常作为单例)。- 通过
dataSource.getConnection()获取连接,这个方法非常快,因为连接是预先创建好的。 - 重要: 调用
conn.close()不是真的关闭连接,而是将连接归还给连接池,以便下次使用。 - 在应用关闭时,调用
dataSource.close()来关闭整个连接池,释放所有资源。
高级配置与最佳实践
a. 配置 URL 参数
你可以在 JDBC URL 后面添加参数来配置连接行为。
jdbc:mysql://host:port/database?param1=value1¶m2=value2
常用参数:
useSSL=false: 在开发环境中可以禁用 SSL(生产环境应启用)。serverTimezone=UTC: 设置服务器时区,防止时区警告。allowPublicKeyRetrieval=true: 在某些环境下(如使用 AWS RDS),需要此参数来获取公钥。useUnicode=true&characterEncoding=UTF-8: 设置字符集,防止乱码。
示例:
String DB_URL = "jdbc:mysql://localhost:3306/mytestdb?useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true";
b. 资源管理
必须记住: 使用完 Connection, Statement, ResultSet 后,必须调用 close() 方法将它们关闭,以释放数据库资源,忘记关闭会导致连接泄漏,最终耗尽数据库连接。
c. 使用 try-with-resources (强烈推荐)
这是 Java 7+ 推荐的最佳实践,可以自动关闭实现了 AutoCloseable 接口的资源,从根本上忘记关闭的问题。
// 推荐
try (Connection conn = dataSource.getConnection();
PreparedStatement pstmt = conn.prepareStatement("SELECT ...");
ResultSet rs = pstmt.executeQuery()) {
// 处理逻辑
} // conn, pstmt, rs 在这里会自动关闭
// 不推荐 (容易出错)
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
conn = dataSource.getConnection();
pstmt = conn.prepareStatement(...);
rs = pstmt.executeQuery();
// ...
} finally {
if (rs != null) try { rs.close(); } catch (SQLException e) { /* ignored */ }
if (pstmt != null) try { pstmt.close(); } catch (SQLException e) { /* ignored */ }
if (conn != null) try { conn.close(); } catch (SQLException e) { /* ignored */ }
}
d. 错误处理
SQLException: 这是所有 JDBC 操作都可能抛出的异常,它包含了一些有用的信息,如 SQL 状态码 (getSQLState()) 和错误代码 (getErrorCode()),可以帮助你定位问题。- 不要忽略异常: 至少要打印堆栈跟踪
e.printStackTrace()或使用日志框架(如 SLF4J + Logback)记录错误。
本教程涵盖了使用 MySQL Connector/J 进行数据库编程的方方面面,从建立第一个连接,到执行 CRUD 操作,再到使用 PreparedStatement 保证安全,以及管理事务和使用连接池提升性能,这些都是构建健壮、高效 Java 应用的基石。
核心要点回顾:
- 依赖管理: 优先使用 Maven 或 Gradle。
- 连接: 使用
DriverManager或连接池(推荐 HikariCP)获取Connection。 - 执行操作:
- 查询:
Statement.executeQuery()->ResultSet。 - 增/删/改:
Statement.executeUpdate()-> 受影响的行数。 - 优先使用
PreparedStatement,以防止 SQL 注入并提高性能。
- 查询:
- 资源管理: 始终关闭
Connection,Statement,ResultSet。强烈推荐使用try-with-resources。 - 事务: 通过
setAutoCommit(false),commit(),rollback()来管理。 - 性能: 在生产环境中,必须使用连接池。
掌握了这些知识,你就可以自信地在 Java 项目中与 MySQL 数据库进行高效、安全的交互了。
