概览
Java 操作数据库主要遵循一个标准规范,叫做 JDBC (Java Database Connectivity),你可以把 JDBC 理解为一套“规则”或“接口”,而各个数据库厂商(如 Oracle, MySQL, SQL Server)会提供这套接口的“实现”,也就是 驱动程序。

整个流程可以概括为:
- 加载驱动:让 JVM 加载 MySQL 提供的 JDBC 驱动。
- 建立连接:通过 JDBC URL、用户名和密码,与 MySQL 数据库建立一个网络连接。
- 创建语句对象:通过连接对象,创建一个用于执行 SQL 语句的对象。
- 执行 SQL:使用语句对象执行 SQL(查询、更新、删除等)。
- 处理结果集:如果是查询操作,会返回一个结果集,你需要遍历并处理这个结果集。
- 关闭资源:按照“后开先关”的原则,关闭所有打开的资源(结果集、语句、连接)。
第一步:环境准备
安装 MySQL 数据库
确保你的电脑上已经安装并运行了 MySQL 数据库,如果没有,请从 MySQL 官网 下载并安装。
创建测试数据库和表
登录到你的 MySQL 客户端(如 MySQL Workbench, Navicat, 或命令行 mysql -u root -p),执行以下 SQL 语句来创建一个用于测试的数据库和表。
-- 创建一个名为 `java_test` 的数据库
CREATE DATABASE IF NOT EXISTS java_test;
-- 使用这个数据库
USE java_test;
-- 创建一个 `users` 表
CREATE TABLE IF NOT EXISTS users (
id INT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(50) NOT NULL,
password VARCHAR(50) NOT NULL,
email VARCHAR(100),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 插入一些测试数据
INSERT INTO users (username, password, email) VALUES
('zhangsan', '123456', 'zhangsan@example.com'),
('lisi', '654321', 'lisi@example.com');
添加 MySQL JDBC 驱动依赖
这是最关键的一步,你的 Java 项目需要知道如何与 MySQL 通信,这就需要 MySQL 的 JDBC 驱动。
使用 Maven (推荐)
如果你使用 Maven 来管理项目,在你的 pom.xml 文件中添加以下依赖:
<dependencies>
<!-- MySQL Connector/J 驱动 -->
<!-- 版本号可能会更新,建议去 Maven Central 查找最新版本 -->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>8.0.33</version> <!-- 使用一个较新的稳定版本 -->
</dependency>
</dependencies>
手动下载 JAR 包
- 去 Maven Central Repository 查找
mysql-connector-j。 - 下载对应版本的 JAR 文件(
mysql-connector-j-8.0.33.jar)。 - 将下载的 JAR 文件添加到你的项目的类路径中,在 IDE(如 IntelliJ IDEA 或 Eclipse)中,通常右键项目 -> Build Path / Project Structure -> Libraries -> Add External JARs...
第二步:编写 Java 代码
下面我们通过几个完整的示例来演示 Java 如何操作 MySQL。
示例 1:查询数据 (SELECT)
这个示例会连接数据库,查询 users 表中的所有数据,并打印出来。
import java.sql.*;
public class SelectExample {
// 数据库连接信息
// 注意:如果你的 MySQL 版本 >= 8.0,通常需要指定时区 serverTimezone=UTC
private static final String URL = "jdbc:mysql://localhost:3306/java_test?serverTimezone=UTC";
private static final String USER = "root"; // 你的 MySQL 用户名
private static final String PASSWORD = "your_password"; // 你的 MySQL 密码
public static void main(String[] args) {
// 使用 try-with-resources 语句,可以自动关闭资源,非常推荐!
// Connection, Statement, ResultSet 都实现了 AutoCloseable 接口
try (Connection conn = DriverManager.getConnection(URL, USER, PASSWORD);
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT id, username, email FROM users")) {
// 检查是否有数据
if (!rs.isBeforeFirst()) {
System.out.println("表中没有数据。");
return;
}
System.out.println("用户列表:");
System.out.println("---------------------------------");
// 遍历结果集
while (rs.next()) {
// 通过列名获取数据,更具可读性,且不易因列顺序改变而出错
int id = rs.getInt("id");
String username = rs.getString("username");
String email = rs.getString("email");
// 打印数据
System.out.printf("ID: %d, 用户名: %s, 邮箱: %s%n", id, username, email);
}
} catch (SQLException e) {
System.err.println("数据库查询出错!");
e.printStackTrace();
}
}
}
代码解释:
DriverManager.getConnection(): 尝试建立与数据库的连接。conn.createStatement(): 创建一个Statement对象,用于执行静态 SQL 语句。stmt.executeQuery(): 执行查询,返回一个ResultSet对象,结果集包含了查询返回的数据。rs.next(): 将光标从当前位置向下移动一行,如果新的一行有效,则返回true,否则返回false。rs.getInt("id"),rs.getString("username"): 根据列名获取当前行的数据,并转换为对应的 Java 类型。try-with-resources: 这是 Java 7 引入的一个语法糖,它会自动在try代码块执行完毕后关闭括号内声明的资源,避免了手动关闭时可能出现的资源泄漏问题。
示例 2:插入数据 (INSERT)
import java.sql.*;
public class InsertExample {
private static final String URL = "jdbc:mysql://localhost:3306/java_test?serverTimezone=UTC";
private static final String USER = "root";
private static final String PASSWORD = "your_password";
public static void main(String[] args) {
String sql = "INSERT INTO users (username, password, email) VALUES (?, ?, ?)";
// 使用 try-with-resources 确保 Connection, PreparedStatement 被关闭
try (Connection conn = DriverManager.getConnection(URL, USER, PASSWORD);
// 使用 PreparedStatement 可以防止 SQL 注入,并且能预编译 SQL,提高性能
PreparedStatement pstmt = conn.prepareStatement(sql)) {
// 设置参数 (索引从 1 开始)
pstmt.setString(1, "wangwu");
pstmt.setString(2, "wangwu123");
pstmt.setString(3, "wangwu@example.com");
// 执行更新操作,返回受影响的行数
int affectedRows = pstmt.executeUpdate();
if (affectedRows > 0) {
System.out.println("成功插入新用户!");
} else {
System.out.println("插入失败。");
}
} catch (SQLException e) {
System.err.println("数据库插入出错!");
e.printStackTrace();
}
}
}
代码解释:
PreparedStatement: 这是Statement的一个子接口,用于执行预编译的 SQL 语句。- 优点:
- 防止 SQL 注入:通过使用 作为占位符,并将用户输入作为参数传入,可以避免恶意 SQL 代码的执行。
- 性能更高:如果同一个 SQL 被多次执行(只是参数不同),数据库可以预编译它,后续执行只需传入参数即可,速度更快。
pstmt.setString(1, "wangwu"): 将第一个 占位符替换为字符串"wangwu",索引从 1 开始。pstmt.executeUpdate(): 执行INSERT,UPDATE,DELETE等更新操作,返回一个整数,表示受影响的行数。
示例 3:更新数据 (UPDATE) 和删除数据 (DELETE)
这两个操作与 INSERT 非常相似,都使用 PreparedStatement.executeUpdate()。
更新数据示例:
String sql = "UPDATE users SET email = ? WHERE username = ?";
try (Connection conn = DriverManager.getConnection(URL, USER, PASSWORD);
PreparedStatement pstmt = conn.prepareStatement(sql)) {
pstmt.setString(1, "new_lisi@example.com");
pstmt.setString(2, "lisi");
int affectedRows = pstmt.executeUpdate();
System.out.println(affectedRows + " 行数据被更新。");
} catch (SQLException e) {
e.printStackTrace();
}
删除数据示例:
String sql = "DELETE FROM users WHERE username = ?";
try (Connection conn = DriverManager.getConnection(URL, USER, PASSWORD);
PreparedStatement pstmt = conn.prepareStatement(sql)) {
pstmt.setString(1, "zhangsan");
int affectedRows = pstmt.executeUpdate();
System.out.println(affectedRows + " 行数据被删除。");
} catch (SQLException e) {
e.printStackTrace();
}
最佳实践和高级主题
使用连接池
在真实的应用中,频繁地创建和销毁数据库连接是非常消耗性能的。连接池 应运而生。
连接池会在初始化时创建一组数据库连接,并将它们缓存起来,当需要连接时,从池中获取一个用完后再放回池中,而不是关闭,这极大地提高了性能。
常用连接池库:
- HikariCP: 目前性能最好的连接池,是 Spring Boot 2.x 的默认选择。
- Apache DBCP
- C3P0
使用 HikariCP 的示例:
在 pom.xml 中添加 HikariCP 依赖:
<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) {
HikariConfig config = new HikariConfig();
config.setJdbcUrl("jdbc:mysql://localhost:3306/java_test?serverTimezone=UTC");
config.setUsername("root");
config.setPassword("your_password");
// 连接池配置
config.setMaximumPoolSize(10); // 最大连接数
config.setMinimumIdle(5); // 最小空闲连接数
config.setConnectionTimeout(30000); // 连接超时时间 (ms)
// 创建数据源 (连接池)
try (HikariDataSource dataSource = new HikariDataSource(config);
Connection conn = dataSource.getConnection();
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT username FROM users")) {
System.out.println("从连接池获取连接成功!");
while (rs.next()) {
System.out.println("用户名: " + rs.getString("username"));
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
注意:在实际项目中,数据源(连接池)通常是全局唯一的,应该作为单例来管理。
使用 JPA / MyBatis 等 ORM 框架
对于复杂的项目,直接写 JDBC 代码会变得非常繁琐(需要手动映射结果集到 Java 对象),这时会使用 ORM (Object-Relational Mapping) 框架。
- JPA (Java Persistence API): Java 官方的 ORM 规范,通过注解或 XML 描述对象和数据库表的映射关系,Hibernate 是其最流行的实现。
- MyBatis: 一个优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射,相比全自动的 ORM,MyBatis 更加灵活,可以让你更好地控制 SQL。
这些框架能帮你把数据库操作从繁琐的 JDBC 代码中解放出来,让你更专注于业务逻辑。
| 操作 | 核心类/方法 | 说明 |
|---|---|---|
| 加载驱动 | Class.forName("com.mysql.cj.jdbc.Driver") |
JDBC 4.0+ 后通常不需要手动加载 |
| 建立连接 | DriverManager.getConnection(url, user, password) |
获取 Connection 对象 |
| 创建语句 | conn.createStatement() 或 conn.prepareStatement(sql) |
获取 Statement 或 PreparedStatement |
| 执行查询 | stmt.executeQuery(sql) |
返回 ResultSet |
| 执行更新 | stmt.executeUpdate(sql) |
返回受影响的行数 |
| 处理结果 | rs.next(), rs.getXxx() |
遍历和获取结果集数据 |
| 关闭资源 | rs.close(), stmt.close(), conn.close() |
或使用 try-with-resources 自动关闭 |
对于初学者,掌握 PreparedStatement 和 try-with-resources 是最重要的两个好习惯,对于实际项目,强烈建议使用 连接池 和 ORM 框架。
