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