杰瑞科技汇

Java的Connection究竟该如何高效管理?

Connection 是 Java 数据库连接的核心,它代表了 Java 应用程序与特定数据库之间的一个活动会话,你可以把它想象成是应用程序和数据库之间的一条“电话线”,通过这条线路,双方可以互相发送指令和数据。

Java的Connection究竟该如何高效管理?-图1
(图片来源网络,侵删)

Connection 对象位于 java.sql 包中(在 JDBC 4.0 之后,也位于 javax.sql 包中)。


Connection 的核心作用

一个 Connection 对象主要承担以下几个关键职责:

  1. 创建执行 SQL 语句的对象

    • 通过 connection.createStatement() 可以创建一个 Statement 对象,用于执行不带参数的简单 SQL。
    • 通过 connection.prepareStatement() 可以创建一个 PreparedStatement 对象,用于执行带参数的预编译 SQL,这是防止 SQL 注入的最佳实践。
    • 通过 connection.prepareCall() 可以创建一个 CallableStatement 对象,用于执行数据库存储过程。
  2. 管理事务

    Java的Connection究竟该如何高效管理?-图2
    (图片来源网络,侵删)
    • Connection 是事务的管理单元,你可以通过它来设置事务的隔离级别(setTransactionIsolation())、提交事务(commit())或回滚事务(rollback())。
    • 默认情况下,JDBC 的连接是自动提交模式,这意味着每一条 executeUpdate() 执行后,数据库会立即自动提交,在需要进行多步操作的业务逻辑中(如银行转账),必须关闭自动提交,手动控制事务。
  3. 获取数据库的元数据

    • 通过 connection.getMetaData() 方法,可以获取一个 DatabaseMetaData 对象,该对象包含了关于数据库本身的信息,如数据库版本、支持的 SQL 语法、表名、列名等。
  4. 管理连接本身

    • 通过 connection.close() 方法,可以显式地关闭数据库连接,将其释放回连接池(如果使用了连接池)或直接关闭底层套接字。

如何获取 Connection 对象?

获取 Connection 的标准步骤如下:

步骤 1:加载 JDBC 驱动

在 JDBC 4.0 (Java 6) 之前,需要手动加载驱动类,JDBC 规范要求驱动实现者使用 Service Provider Mechanism,驱动会自动注册,所以这一步通常是可选的。

Java的Connection究竟该如何高效管理?-图3
(图片来源网络,侵删)
// 旧式方法,现代JDBC中通常不需要
// Class.forName("com.mysql.cj.jdbc.Driver");

步骤 2:提供数据库连接 URL

这是一个字符串,用于定位数据库,其格式通常为: jdbc:子协议://主机名:端口号/数据库名

  • MySQL: jdbc:mysql://localhost:3306/mydb
  • PostgreSQL: jdbc:postgresql://localhost:5432/mydb
  • Oracle: jdbc:oracle:thin:@localhost:1521:orcl
  • SQL Server: jdbc:sqlserver://localhost:1433;databaseName=mydb

步骤 3:获取 Connection 对象

使用 DriverManager 类的静态方法 getConnection() 来建立连接。

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
public class BasicConnectionExample {
    // 数据库URL、用户名和密码
    static final String DB_URL = "jdbc:mysql://localhost:3306/your_database";
    static final String USER = "your_username";
    static final String PASS = "your_password";
    public static void main(String[] args) {
        Connection conn = null;
        try {
            // 1. (可选) 加载驱动
            // Class.forName("com.mysql.cj.jdbc.Driver");
            // 2. 获取连接
            System.out.println("Connecting to database...");
            conn = DriverManager.getConnection(DB_URL, USER, PASS);
            System.out.println("Connection successful!");
        } catch (SQLException e) {
            System.out.println("Connection failed!");
            e.printStackTrace();
        } finally {
            // 3. 关闭连接
            if (conn != null) {
                try {
                    conn.close();
                    System.out.println("Connection closed.");
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

Connection 的最佳实践

直接使用 DriverManager 来管理连接在现代应用中是不推荐的,因为它效率低下且无法有效管理连接资源,最佳实践是使用连接池

为什么需要连接池?

  • 性能提升:创建和销毁数据库连接是一个非常耗时和消耗资源的操作,连接池在应用启动时预先创建一组连接,应用程序需要时从池中“借用”,用完后“归还”,避免了频繁创建和销毁的开销。
  • 资源管理:可以限制应用同时打开的连接数量,防止数据库因连接过多而崩溃。
  • 稳定性:提供了连接的自动回收、检测和重连机制。

如何使用连接池?

  1. 选择连接池实现

    • HikariCP: 目前性能最好、最流行的连接池,是 Spring Boot 2.x 的默认选择。
    • Apache DBCP: Apache 下的经典连接池。
    • C3P0: 另一个老牌的连接池实现。
  2. 配置连接池:通常通过配置文件(如 application.propertiesyaml)来设置连接池参数。

  3. 获取 Connection:从连接池中获取 Connection 对象,用法和从 DriverManager 获取基本一致。

示例(使用 HikariCP 和 Spring Boot 的 application.properties

# application.properties
spring.datasource.url=jdbc:mysql://localhost:3306/your_database
spring.datasource.username=your_username
spring.datasource.password=your_password
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
# HikariCP 配置
spring.datasource.hikari.connection-timeout=30000
spring.datasource.hikari.maximum-pool-size=10
spring.datasource.hikari.minimum-idle=5
spring.datasource.hikari.idle-timeout=600000
spring.datasource.hikari.max-lifetime=1800000

在 Spring Boot 应用中,你只需要注入 DataSource,然后通过它获取 Connection

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class MyRepository {
    @Autowired
    private DataSource dataSource; // Spring Boot 会自动配置好 HikariCP 的 DataSource
    public void doSomething() {
        Connection conn = null;
        try {
            // 从连接池中获取一个连接
            conn = dataSource.getConnection();
            System.out.println("Got connection from pool: " + conn);
            // ... 执行数据库操作 ...
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            // 将连接归还给连接池,而不是关闭它!
            if (conn != null) {
                try {
                    conn.close(); // 对于连接池,close() 是归还操作
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

事务管理示例

事务是保证数据一致性的关键,下面是一个手动管理事务的例子。

场景:从账户 A 转账 100 元到账户 B。

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class TransactionExample {
    static final String DB_URL = "jdbc:mysql://localhost:3306/your_database";
    static final String USER = "your_username";
    static final String PASS = "your_password";
    public static void transferMoney(int fromAccountId, int toAccountId, double amount) {
        Connection conn = null;
        try {
            conn = DriverManager.getConnection(DB_URL, USER, PASS);
            // 关闭自动提交,开启事务
            conn.setAutoCommit(false);
            // SQL 语句
            String sql1 = "UPDATE accounts SET balance = balance - ? WHERE id = ?";
            String sql2 = "UPDATE accounts SET balance = balance + ? WHERE id = ?";
            // 创建 PreparedStatement
            PreparedStatement pstmt1 = conn.prepareStatement(sql1);
            PreparedStatement pstmt2 = conn.prepareStatement(sql2);
            // 设置参数
            pstmt1.setDouble(1, amount);
            pstmt1.setInt(2, fromAccountId);
            pstmt2.setDouble(1, amount);
            pstmt2.setInt(2, toAccountId);
            // 执行更新
            int rowsAffected1 = pstmt1.executeUpdate();
            int rowsAffected2 = pstmt2.executeUpdate();
            // 检查两条 SQL 是否都成功执行
            if (rowsAffected1 > 0 && rowsAffected2 > 0) {
                // 提交事务
                conn.commit();
                System.out.println("Transfer successful!");
            } else {
                // 回滚事务
                conn.rollback();
                System.out.println("Transfer failed, transaction rolled back.");
            }
        } catch (SQLException e) {
            // 发生异常,回滚事务
            if (conn != null) {
                try {
                    conn.rollback();
                } catch (SQLException ex) {
                    ex.printStackTrace();
                }
            }
            e.printStackTrace();
        } finally {
            if (conn != null) {
                try {
                    // 恢复自动提交(可选,但推荐)
                    conn.setAutoCommit(true);
                    conn.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

特性 描述
是什么 代表 Java 应用与数据库的一个活动会话。
核心作用 创建 Statement/PreparedStatement、管理事务、获取元数据。
如何获取 DriverManager.getConnection() 或从 DataSource(连接池)获取。
最佳实践 必须使用连接池(如 HikariCP)来管理 Connection,而不是直接使用 DriverManager
事务管理 通过 setAutoCommit(false) 开启事务,commit() 提交,rollback() 回滚。
资源释放 必须finally 块中调用 connection.close() 来关闭连接(归还给连接池)。

理解并正确使用 Connection 是进行 Java 数据库编程的基础和关键。

分享:
扫描分享到社交APP
上一篇
下一篇