5.9. 连接池和 DataSources

5.9.1. JDBC,JDK 版本支持

JDBC 2 以一个附加的 API 的方式引入了标准连接池的特性,这个 附加的 API 叫做 JDBC 2.0 可选包(也称作 JDBC 2.0 标准扩展)。因此这些特性就被包含到核心的 JDBC 3 API 中了。 PostgreSQL JDBC 驱动和 JDK 1.3.x 一起支持这些特性,以及 JDBC 2.0 可选包,或者是和 JDK 1.4+ (JDBC 3)一起,也支持这个特性。大多数应用服务器包含 JDBC 2.0 可选包,但我们也可以从 SUN 单独获得这些东西: JDBC下载站

5.9.2. JDBC 连接池 API

JDBC API 为连接池提供了一个客户端和 一个服务器端的接口。客户端接口是 javax.sql.DataSource, 通常就是应用代码用来请求一个缓冲了的数据库连接的东西。 服务器接口是 javax.sql.ConnectionPoolDataSource, 通常是大多数应用服务器和 PostgreSQL JDBC 驱动打交道的接口。

在应用服务器环境里,应用服务器配置通常将指向 PostgreSQL ConnectionPoolDataSource 实现, 而应用组件代码将通常要求一个由应用服务器实现的 DataSource(不是由 PostgreSQL)。

在一个没有应用服务器的环境里,PostgreSQL 提供了 两种应用可以直接使用的 DataSource 的实现。 一种实现执行连接池,另外一种只是简单的通过 DataSource 接口 提供访问数据库的连接,而不使用任何缓冲池。同样,这些实现不应该 在一个应用服务器环境里使用,除非应用服务器不支持 ConnectionPoolDataSource 接口。

5.9.3. 应用服务器:ConnectionPoolDataSource

PostgreSQL 包含了一个用于 JDBC 2 的 ConnectionPoolDataSource 的实现,以及一个用于 JDBC 3 的实现:

Table 5-1. ConnectionPoolDataSource 实现

JDBC实现类
2org.postgresql.jdbc2.optional.ConnectionPool
3org.postgresql.jdbc3.Jdbc3ConnectionPool

两种实现都使用了同样的配置模式。JDBC 要求一个 ConnectionPoolDataSource 通过 JavaBean 属性来实现, 因此每个这样的属性都存在获取和设置方法:

Table 5-2. ConnectionPoolDataSource 配置属性

属性类型描述
serverNameStringPostgreSQL 数据库服务器主机名
databaseNameStringPostgreSQL 数据库名
portNumberint PostgreSQL 数据库服务器监听的 TCP/IP 端口 (为 0 则使用缺省端口)
userString用来进行数据库连接的用户
passwordString用来进行数据库连接的口令
defaultAutoCommitboolean提交给调用者的时候连接是应该打开 autoCommit 还是应该关闭。 缺省是 false,关闭 autoCommit。

许多应用服务器使用属性风格的语法来配置这些属性, 因此把属性当作一块文本输入应该并不罕见。

Example 5-5. ConnectionPoolDataSource 配置例子

如果应用服务器提供了单一的区域用于输入所有属性, 那么它们通常会象下面这样列出:

serverName=localhost
databaseName=test
user=testuser
password=testpassword

或者是用分号,而不是换行来分隔,象这样:

serverName=localhost;databaseName=test;user=testuser;password=testpassword

5.9.4. 应用:DataSource

PostgreSQL 包含两个用于 JDBC 2 的 DataSource,以及两个用于 JDBC 3的。 连接池的实现在客户端调用 close 方法的时候 实际上并不关闭连接,而是把连接返回到一个可用连接的连接池中 给其它客户端使用。这样就避免了任何重复打开和关闭连接造成的 开销,并且允许大量的客户端分享相对较少的数据库连接。

这里提供的连接池 datasource 实现并非世上特性最丰富的。 比如,连接在池本身关闭之前绝对不会关闭;而且也没有办法缩小连接池。 同样,非缺省配置的用户的连接请求无法如池。许多应用服务器提供 更加高级的连接池特性,并且使用的是 ConnectionPoolDataSource 实现。

Table 5-3. DataSource 实现

JDBC连接池实现类
2org.postgresql.jdbc2.optional.SimpleDataSource
2org.postgresql.jdbc2.optional.PoolingDataSource
3org.postgresql.jdbc3.Jdbc3SimpleDataSource
3org.postgresql.jdbc3.Jdbc3PoolingDataSource

所有实现使用同样的配置模式。JDBC 要求 DataSource 通过 JavaBean 属性配置,因此每种这个 属性都有获取和设置属性。

Table 5-4. DataSource 配置属性

属性类型描述
serverNameStringPostgreSQL 数据库服务器主机名
databaseNameStringPostgreSQL 数据库名
portNumberint PostgreSQL 数据库服务器监听的 TCP/IP 端口 (或者 0 表示使用缺省端口)
userString用于连接数据库的用户
passwordString用于连接数据库的口令

连接池实现要求一些额外的配置属性:

Table 5-5. 额外的连接池 DataSource 配置属性

属性类型描述
dataSourceNameString每一个连接池 DataSource 必须有一个唯一的名字
initialConnectionsint连接池初始化的时候要创建的数据库连接数目
maxConnectionsint允许打开的最大数据库连接个数。如果请求了更多的连接, 那么调用者将挂起,直到有一个连接返回给连接池。

下面是一个使用连接池 DataSource 的典型应用的代码:

Example 5-6. DataSource 代码例子

初始化连接池 DataSource 的代码例子看起来会象这样:

Jdbc3PoolingDataSource source = new Jdbc3PoolingDataSource();
source.setDataSourceName("A Data Source");
source.setServerName("localhost");
source.setDatabaseName("test");
source.setUser("testuser");
source.setPassword("testpassword");
source.setMaxConnections(10);

然后使用来自连接池的代码看起来会象这样。 请注意关闭连接是非常关键的,否则这个池子就会“泄漏”连接, 最后把所有客户端都锁在外面。

Connection con = null;
try {
    con = source.getConnection();
    // use connection
} catch(SQLException e) {
    // log error
} finally {
    if(con != null) {
        try {con.close();}catch(SQLException e) {}
    }
}

5.9.5. DataSources 和 JNDI

所有 ConnectionPoolDataSourceDataSource 实现都可以存储在 JNDI 里。在非连接池的实现中, 每次从 JNDI 中检索对象都将创建一个新的实例, 带有和存储的实例同样的设置。对于连接池实现而言,同一个实例 是在可得的情况下检索出来的(也就是说,没有其它 JVMJNDI 中检索连接池),否则就创建同样设置的新的实例。

在应用服务器环境,通常是应用服务器的 DataSource 实例将存储在 JNDI 中,而不是 PostgreSQL ConnectionPoolDataSource 的实现。

在应用服务器环境,应用可以在 JNDI 中存储 DataSource, 这样它就不用制作一个指向 DataSource 的引用提供给 所有可能需要的应用组件使用它:

Example 5-7. DataSource JNDI 代码例子

初始化连接池 DataSource 并且把它加到 JNDI 的代码 看起来可能象这样:

Jdbc3PoolingDataSource source = new Jdbc3PoolingDataSource();
source.setDataSourceName("A Data Source");
source.setServerName("localhost");
source.setDatabaseName("test");
source.setUser("testuser");
source.setPassword("testpassword");
source.setMaxConnections(10);
new InitialContext().rebind("DataSource", source);

Then code to use a connection from the pool might look like this:

Connection con = null;
try {
    DataSource source = (DataSource)new InitialContext().lookup("DataSource");
    con = source.getConnection();
    // use connection
} catch(SQLException e) {
    // log error
} catch(NamingException e) {
    // DataSource wasn't found in JNDI
} finally {
    if(con != null) {
        try {con.close();}catch(SQLException e) {}
    }
}

5.9.6. 特定的应用服务器配置

在这里包含特定的应用服务器的配置信息。