" />
文章

PostgreSQL JDBC连接的完整生命周期

PostgreSQL JDBC连接的完整生命周期

让我通过pgjdbc源码,详细讲解一个数据库连接从创建到销毁的完整生命周期流程。

1. 连接创建阶段

入口点:Driver类

首先,当你执行DriverManager.getConnection(url, props)时,JVM会找到已注册的JDBC驱动。在PostgreSQL的情况下,这是org.postgresql.Driver类:

// org.postgresql.Driver.java
public class Driver implements java.sql.Driver {
  static {
    try {
      // 向DriverManager注册自己
      DriverManager.registerDriver(new Driver());
    } catch (SQLException e) {
      // 处理异常
    }
  }

  @Override
  public Connection connect(String url, Properties info) throws SQLException {
    // 检查URL是否是PostgreSQL格式
    if (!acceptsURL(url)) {
      return null;
    }

    // 解析连接参数
    Properties props = parseURL(url, info);

    // 创建实际的连接
    return connectImpl(hostSpecs, props, database);
  }
}

URL解析与连接属性处理

Driver类会解析JDBC URL并提取连接参数:

// 从类似jdbc:postgresql://localhost:5432/mydb中提取参数
private static Properties parseURL(String url, Properties defaults) {
  // 分析URL,提取主机、端口、数据库名称等
  // 合并默认属性和用户提供的属性
  return props;
}

连接建立:ConnectionFactory

实际连接创建由ConnectionFactory负责:

// org.postgresql.core.ConnectionFactory.java
public static QueryExecutor openConnection(HostSpec[] hostSpecs, Properties info) throws SQLException {
  // 尝试连接到提供的主机列表中的一个
  ConnectionFactory connectionFactory = new ConnectionFactoryImpl();
  QueryExecutor queryExecutor = connectionFactory.openConnectionImpl(hostSpecs, info);
  return queryExecutor;
}

网络连接与握手:PgStream

ConnectionFactoryImpl建立底层TCP连接并创建PgStream:

// org.postgresql.core.v3.ConnectionFactoryImpl.java
private PGStream tryConnect(HostSpec hostSpec, Properties info) throws SQLException, IOException {
  // 创建Socket连接
  Socket socket = socketFactory.createSocket();
  // 设置TCP参数
  socket.setTcpNoDelay(true);
  socket.setKeepAlive(true);

  // 连接到服务器
  socket.connect(new InetSocketAddress(hostSpec.getHost(), hostSpec.getPort()), timeout);

  // 创建与服务器通信的流
  return new PGStream(socket);
}

身份验证与协议握手

建立连接后,驱动会进行身份验证并与服务器协商协议版本:

// org.postgresql.core.v3.ConnectionFactoryImpl.java
private void doAuthentication(PGStream pgStream, String user, Properties info) throws IOException, SQLException {
  // 发送启动消息
  sendStartupPacket(pgStream, user, database, info);

  // 处理认证请求
  authloop: while (true) {
    int beresp = pgStream.receiveChar();
    switch (beresp) {
      case 'R':
        // 处理各种认证方法
        break;
      case 'K':
        // 认证成功
        break authloop;
    }
  }
}

创建连接对象:PgConnection

最后,一旦认证成功,将创建一个PgConnection对象:

// org.postgresql.Driver.java
private static PgConnection connectImpl(HostSpec[] hostSpecs, Properties info, String url) throws SQLException {
  // 创建查询执行器
  QueryExecutor queryExecutor = ConnectionFactory.openConnection(hostSpecs, info);

  // 使用查询执行器创建实际的连接对象
  return new PgConnection(hostSpecs, user, database, info, url, queryExecutor);
}

PgConnection是用户将与之交互的主要对象,它实现了java.sql.Connection接口。

2. 连接使用阶段

创建Statement

使用连接对象创建语句:

// org.postgresql.jdbc.PgConnection.java
@Override
public Statement createStatement() throws SQLException {
  checkClosed();
  return createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
}

@Override
public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException {
  checkClosed();
  return new PgStatement(this, resultSetType, resultSetConcurrency);
}

创建PreparedStatement

预编译语句的创建:

// org.postgresql.jdbc.PgConnection.java
@Override
public PreparedStatement prepareStatement(String sql) throws SQLException {
  checkClosed();
  return prepareStatement(sql, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
}

@Override
public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
  checkClosed();
  // 预处理SQL,创建查询对象
  return new PgPreparedStatement(this, sql, resultSetType, resultSetConcurrency);
}

执行查询

当执行查询时:

// org.postgresql.jdbc.PgStatement.java
@Override
public ResultSet executeQuery(String sql) throws SQLException {
  try (ResourceLock ignore = lock.obtain()) {
    if (!executeWithFlags(sql, 0)) {
      throw new PSQLException(GT.tr("No results were returned by the query."), PSQLState.NO_DATA);
    }
    return getSingleResultSet();
  }
}

查询执行的核心:QueryExecutorImpl

这里是实际的查询执行机制:

// org.postgresql.core.v3.QueryExecutorImpl.java
public void execute(Query query, ParameterList parameters, ResultHandler handler,
      int maxRows, int fetchSize, int flags) throws SQLException {
  // 发送查询
  sendQuery(query, (V3ParameterList) parameters, maxRows, fetchSize, flags, handler, null);

  // 发送同步消息
  sendSync();

  // 处理结果
  processResults(handler, flags);
}

处理结果集

查询结果由PgResultSet处理:

// org.postgresql.jdbc.PgResultSet.java
// 结果集实现,用于处理从服务器返回的数据
public class PgResultSet implements ResultSet {
  // 存储结果数据
  private final List<Tuple> tuples;

  // 当前行位置
  private int currentRow = -1;

  // 获取下一行数据
  @Override
  public boolean next() throws SQLException {
    checkClosed();
    if (onInsertRow) {
      throw new PSQLException(GT.tr("Can''t use relative move methods while on the insert row."),
          PSQLState.INVALID_CURSOR_STATE);
    }

    if (++currentRow >= rows.size()) {
      currentRow = rows.size();
      return false;
    }
    return true;
  }

  // 获取列值的方法
  @Override
  public String getString(int columnIndex) throws SQLException {
    // 从当前行获取指定列的值
  }
}

事务管理

事务处理关键方法:

// org.postgresql.jdbc.PgConnection.java
@Override
public void setAutoCommit(boolean autoCommit) throws SQLException {
  // 改变事务模式
  if (this.autoCommit == autoCommit) {
    return;
  }

  if (autoCommit) {
    // 提交当前事务
    commit();
  }

  this.autoCommit = autoCommit;
}

@Override
public void commit() throws SQLException {
  checkClosed();

  if (autoCommit) {
    throw new PSQLException("Commit called on connection in autoCommit mode",
        PSQLState.NO_ACTIVE_SQL_TRANSACTION);
  }

  // 发送COMMIT命令
  execSQLUpdate("COMMIT");
}

3. 连接销毁阶段

关闭连接

当用户调用connection.close()时:

// org.postgresql.jdbc.PgConnection.java
@Override
public void close() throws SQLException {
  if (isClosed()) {
    return;
  }

  try {
    // 强制终止所有正在进行的查询
    cancelQuery();

    // 关闭所有打开的语句
    closeAllStatements();

    // 关闭底层网络连接
    queryExecutor.close();
  } finally {
    // 设置状态为已关闭
    openStackTrace = null;
    closed = true;
  }
}

资源清理

QueryExecutorImpl负责清理底层资源:

// org.postgresql.core.v3.QueryExecutorImpl.java
@Override
public void close() {
  try (ResourceLock ignore = lock.obtain()) {
    // 处理还在队列中的消息
    processTermination();

    // 关闭通信流
    if (pgStream != null) {
      pgStream.close();
      pgStream = null;
    }

    // 清理各种缓存和引用
    cleanupCachedStatements();
  }
}

连接池回收与资源释放

在使用连接池时,连接不会真正关闭,而是返回池中:

// 典型的连接池处理(非pgjdbc代码)
@Override
public void close() throws SQLException {
  if (pooledConnection != null) {
    // 不是真正关闭,而是返回池
    pooledConnection.recycleConnection(this);
  } else {
    // 实际关闭连接
    reallyClose();
  }
}

处理未关闭的资源

pgjdbc会跟踪未关闭的资源,并在发现时进行清理:

// org.postgresql.jdbc.PgConnection.java
// 检测泄漏的连接
private void openStackTrace() {
  if (PGProperty.LOG_UNCLOSED_CONNECTIONS.getBoolean(info)) {
    openStackTrace = new Throwable("Connection was created at this point:");
  }
}

// 在finalize或GC前记录未关闭的连接
@Override
protected void finalize() throws Throwable {
  try {
    if (!isClosed() && openStackTrace != null) {
      LOGGER.log(Level.WARNING, "Finalizing connection that was never closed:", openStackTrace);
    }
    close();
  } finally {
    super.finalize();
  }
}

完整生命周期图示

1. 连接创建
   |
   +--> Driver.connect()
        |
        +--> parseURL()       [解析JDBC URL]
        |
        +--> ConnectionFactory.openConnection()
             |
             +--> 创建Socket连接
             |
             +--> 进行身份验证
             |
             +--> 创建PgConnection

2. 连接使用
   |
   +--> 创建Statement/PreparedStatement
   |    |
   |    +--> PgStatement/PgPreparedStatement
   |
   +--> 执行SQL
   |    |
   |    +--> QueryExecutorImpl.execute()
   |         |
   |         +--> 发送查询到服务器
   |         |
   |         +--> 处理服务器响应
   |
   +--> 处理结果集
   |    |
   |    +--> PgResultSet.next()/getString()/etc
   |
   +--> 事务管理
        |
        +--> commit()/rollback()/setAutoCommit()

3. 连接销毁
   |
   +--> PgConnection.close()
        |
        +--> 关闭所有Statement
        |
        +--> 关闭网络连接
        |
        +--> 释放所有资源
        |
        +--> 如果使用连接池,返回池中而不是真正关闭

这就是PostgreSQL JDBC驱动中连接的完整生命周期流程,从创建、使用到最终销毁的各个阶段和关键类的交互。理解这个流程有助于更好地使用和调试JDBC应用程序。

License:  CC BY 4.0