Skip to content

Java 数据库编程 (JDBC)

几乎所有的企业级应用都需要与数据库打交道。Java 通过 JDBC (Java Database Connectivity) API 提供了一套标准的、独立于具体数据库厂商的接口,用于执行 SQL 语句并与关系型数据库进行交互。

什么是 JDBC?

JDBC 是一套由 java.sqljavax.sql 包组成的 Java API。它定义了 Java 应用程序如何连接和操作数据库。要连接到一个特定的数据库(如 MySQL, PostgreSQL, Oracle),您需要该数据库厂商提供的 JDBC 驱动程序。这个驱动程序是一个实现了 JDBC 接口的库,它负责将标准的 JDBC 调用转换为特定数据库的协议。

JDBC 核心组件

  1. DriverManager: 管理一组 JDBC 驱动程序。它的主要作用是使用 getConnection() 方法,根据数据库 URL 创建一个数据库连接。
  2. Connection: 代表与数据库的一个物理连接。所有与数据库的通信都在 Connection 的上下文中进行。
  3. Statement: 用于执行静态的 SQL 语句并返回结果。它有几个子接口,其中最常用的是 PreparedStatement
  4. PreparedStatement: Statement 的子接口。它代表一个预编译的 SQL 语句,可以高效地多次执行。更重要的是,它能有效防止 SQL 注入攻击。
  5. ResultSet: 代表 SQL 查询的结果集。它维护一个指向其数据行的游标,您可以通过移动游标来逐行读取数据。

JDBC 操作步骤

使用 JDBC 连接数据库通常遵循以下六个步骤:

  1. 加载 JDBC 驱动:在现代 JDBC (4.0+) 中,驱动程序通常会自动加载,此步骤可省略。
  2. 建立连接:使用 DriverManager.getConnection() 获取 Connection 对象。
  3. 创建语句对象:通过 Connection 对象创建 StatementPreparedStatement
  4. 执行 SQL 语句:使用 executeQuery() (用于 SELECT) 或 executeUpdate() (用于 INSERT, UPDATE, DELETE) 执行 SQL。
  5. 处理结果集:如果执行的是查询,则遍历 ResultSet 对象获取数据。
  6. 关闭资源:按相反的顺序(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();
    }
}

本站内容仅供学习和研究使用。