Java 数据库编程 (JDBC)
几乎所有的企业级应用都需要与数据库打交道。Java 通过 JDBC (Java Database Connectivity) API 提供了一套标准的、独立于具体数据库厂商的接口,用于执行 SQL 语句并与关系型数据库进行交互。
什么是 JDBC?
JDBC 是一套由 java.sql 和 javax.sql 包组成的 Java API。它定义了 Java 应用程序如何连接和操作数据库。要连接到一个特定的数据库(如 MySQL, PostgreSQL, Oracle),您需要该数据库厂商提供的 JDBC 驱动程序。这个驱动程序是一个实现了 JDBC 接口的库,它负责将标准的 JDBC 调用转换为特定数据库的协议。
JDBC 核心组件
DriverManager: 管理一组 JDBC 驱动程序。它的主要作用是使用getConnection()方法,根据数据库 URL 创建一个数据库连接。Connection: 代表与数据库的一个物理连接。所有与数据库的通信都在Connection的上下文中进行。Statement: 用于执行静态的 SQL 语句并返回结果。它有几个子接口,其中最常用的是PreparedStatement。PreparedStatement:Statement的子接口。它代表一个预编译的 SQL 语句,可以高效地多次执行。更重要的是,它能有效防止 SQL 注入攻击。ResultSet: 代表 SQL 查询的结果集。它维护一个指向其数据行的游标,您可以通过移动游标来逐行读取数据。
JDBC 操作步骤
使用 JDBC 连接数据库通常遵循以下六个步骤:
- 加载 JDBC 驱动:在现代 JDBC (4.0+) 中,驱动程序通常会自动加载,此步骤可省略。
- 建立连接:使用
DriverManager.getConnection()获取Connection对象。 - 创建语句对象:通过
Connection对象创建Statement或PreparedStatement。 - 执行 SQL 语句:使用
executeQuery()(用于 SELECT) 或executeUpdate()(用于 INSERT, UPDATE, DELETE) 执行 SQL。 - 处理结果集:如果执行的是查询,则遍历
ResultSet对象获取数据。 - 关闭资源:按相反的顺序(
ResultSet->Statement->Connection)关闭所有资源,以释放数据库连接。
示例:使用 JDBC 查询数据
假设我们有一个 MySQL 数据库,并且已经在项目的依赖中添加了 MySQL JDBC 驱动。
java
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class JdbcExample {
// 数据库连接信息
static final String DB_URL = "jdbc:mysql://localhost:3306/mydatabase?useSSL=false&serverTimezone=UTC";
static final String USER = "root";
static final String PASS = "password";
public static void main(String[] args) {
// 使用 try-with-resources 语句自动关闭资源
try (Connection conn = DriverManager.getConnection(DB_URL, USER, PASS)) {
System.out.println("数据库连接成功!");
String sql = "SELECT id, name, email FROM users WHERE id = ?";
try (PreparedStatement pstmt = conn.prepareStatement(sql)) {
// 设置 SQL 语句中的参数 (第一个 ? 的索引是 1)
pstmt.setInt(1, 101);
// 执行查询
try (ResultSet rs = pstmt.executeQuery()) {
// 遍历结果集
while (rs.next()) {
// 通过列名或索引获取数据
int id = rs.getInt("id");
String name = rs.getString("name");
String email = rs.getString("email");
System.out.printf("ID: %d, Name: %s, Email: %s%n", id, name, email);
}
}
}
} catch (SQLException e) {
System.err.println("数据库操作失败: " + e.getMessage());
e.printStackTrace();
}
}
}Statement vs. PreparedStatement
Statement: 每次执行都会重新编译 SQL 语句。如果 SQL 中包含用户输入,容易引发 SQL 注入。java// 不安全的示例 String userId = "101 OR 1=1"; // 恶意输入 statement.executeQuery("SELECT * FROM users WHERE id = " + userId);PreparedStatement: SQL 语句只在创建时编译一次。它将 SQL 结构和参数分开,参数值会经过转义处理,从而杜绝了 SQL 注入的风险。性能更好,代码更清晰,是首选方式。
事务管理
事务是一组必须作为一个整体成功或失败的 SQL 操作。默认情况下,JDBC 连接处于自动提交模式 (auto-commit),即每条 SQL 语句都是一个独立的事务。
可以手动管理事务:
java
Connection conn = null;
try {
conn = DriverManager.getConnection(...);
// 1. 关闭自动提交
conn.setAutoCommit(false);
// 2. 执行多个 SQL 操作...
// statement.executeUpdate(...);
// statement.executeUpdate(...);
// 3. 如果所有操作都成功,提交事务
conn.commit();
} catch (SQLException e) {
// 4. 如果发生任何错误,回滚事务
if (conn != null) {
try {
conn.rollback();
} catch (SQLException ex) {
ex.printStackTrace();
}
}
} finally {
// 恢复自动提交模式并关闭连接
if (conn != null) {
conn.setAutoCommit(true);
conn.close();
}
}