Redrock Postgres 文档
主页 切换暗/亮/自动模式 切换暗/亮/自动模式 切换暗/亮/自动模式 返回首页

连接池和数据源

JDBC 2 在称为 JDBC 2.0 可选包的附加 API 中引入了标准连接池功能 (也称为 JDBC 2.0 标准扩展)。此后,这些功能已包含在核心 JDBC 3 API 中。

JDBC API 为连接池提供客户端和服务端接口。客户端接口为javax.sql.DataSource,这是应用程序通常用于获取池化的数据库连接的方法。服务端接口是javax.sql.ConnectionPoolDataSource,这是大多数应用服务器与 PostgreSQL JDBC 驱动程序的交互方式。

在应用服务器环境中,应用服务器配置通常引用 PostgreSQL 的ConnectionPoolDataSource实现,而应用组件代码通常获取应用服务器(而不是 PostgreSQL)提供的DataSource实现。

对于没有应用服务器的环境,PostgreSQL 提供了两种DataSource的实现,应用程序可以直接使用。一个实现执行连接的池化,而另一个实现仅提供通过DataSource接口进行数据库连接,没有任何连接的池化机制。同样,这些实现不应该在应用服务器环境中使用,除非应用服务器不支持ConnectionPoolDataSource接口。

应用服务器 ConnectionPoolDataSource

PostgreSQL 包含一个名为org.postgresql.ds.PGConnectionPoolDataSourceConnectionPoolDataSource实现。

JDBC 要求通过 JavaBean 属性进行ConnectionPoolDataSource的配置,如表 11.1 “ConnectionPoolDataSource 配置属性”所示, 因此,每个属性都有 get 和 set 方法。

表 11.1.ConnectionPoolDataSource配置属性

Property Type Description
serverName STRING PostgreSQL database server host name
databaseName STRING PostgreSQL database name
portNumber INT TCP port which the PostgreSQL database server is listening on (or 0 to use the default port)
user STRING User used to make database connections
password STRING Password used to make database connections
ssl BOOLEAN If true , use SSL encrypted connections (default false )
sslfactory STRING 自定义的javax.net.ssl.SSLSocketFactory类名(请参阅名为“自定义 SSLSocketFactory”的部分)
defaultAutoCommit BOOLEAN Whether connections should have autocommit enabled or disabled when they are supplied to the caller. The default is false , to disable autocommit.

许多应用服务器使用属性样式的语法来配置这些属性,因此作为文本块输入这些属性并不罕见。如果应用服务器提供单个区域来输入所有属性,那么它们可能会像这样列出:

serverName=localhost

databaseName=test

user=testuser

password=testpassword

或者,如果使用分号替代换行符作为分隔符,则可能如下所示:

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

应用程序 DataSource

PostgreSQL 包含了两个DataSource实现,如表 11.2 “DataSource实现” 所示。

一个进行池化,另一个不执行池化。在客户端调用close()方法时,池化实现实际上不会关闭连接,而是改为将连接返回到可用的连接池,以供其他客户端使用。这避免了重复打开和关闭连接的任何开销,并允许大量客户端共享少量数据库连接。

此处提供的池化数据源并不是世界上功能最丰富的实现。除其他外,在池本身关闭之前,连接永远不会关闭;没有办法缩小池。另外,为默认配置用户以外的用户请求的连接不会进行池化。它的错误处理有时无法从连接池删除异常断开的连接。通常不建议使用 PostgreSQL 提供的连接池。请检查您的应用服务器或查看优秀的 jakarta commons DBCP 项目。

表 11.2.DataSource实现

池化 实现类
不支持 org.postgresql.ds.PGSimpleDataSource
支持 org.postgresql.ds.PGPoolingDataSource

两种实现都使用相同的配置模式。JDBC 要求通过 JavaBean 属性进行DataSource的配置,如表 11.3 “DataSource配置属性” 所示, 因此,每个属性都有 get 和 set 方法。

表 11.3.DataSource配置属性

Property Type Description
serverName STRING PostgreSQL database server host name
databaseName STRING PostgreSQL database name
portNumber INT TCP port which the PostgreSQL database server is listening on (or 0 to use the default port)
user STRING User used to make database connections
password STRING Password used to make database connections
ssl BOOLEAN If true, use SSL encrypted connections (default false)
sslfactory STRING 自定义的javax.net.ssl.SSLSocketFactory类名(请参阅名为“自定义 SSLSocketFactory”的部分)

池化实现需要一些其他配置属性,如表 11.4 “其他池化DataSource配置属性” 所示。

表 11.4.其他池化DataSource配置属性

Property Type Description
dataSourceName STRING Every pooling DataSource must have a unique name.
initialConnections INT The number of database connections to be created when the pool is initialized.
maxConnections INT The maximum number of open database connections to allow. When more connections are requested, the caller will hang until a connection is returned to the pool.

例 11.1 “DataSource代码示例” 显示了一个典型的使用池化DataSource的应用程序代码示例。

例 11.1.DataSource代码示例

初始化池化DataSource的代码可能如下所示:

PGPoolingDataSource source = new PGPoolingDataSource();
source.setDataSourceName("A Data Source");
source.setServerNames(new String[] {
    "localhost"
});
source.setDatabaseName("test");
source.setUser("testuser");
source.setPassword("testpassword");
source.setMaxConnections(10);

Note

setServerName has been deprecated in favour of setServerNames. This was done to support multiple hosts.

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

Note

it is critical that the connections are eventually closed. Otherwise, the pool will “leak” connections and will eventually lock all the clients out.

try (Connection conn = source.getConnection()) {
    // use connection
} catch (SQLException e) {
    // log error
}

数据源和 JNDI

All the ConnectionPoolDataSource and DataSource implementations can be stored in JNDI. In the case of the non-pooling implementations, a new instance will be created every time the object is retrieved from JNDI, with the same settings as the instance that was stored. For the pooling implementations, the same instance will be retrieved as long as it is available (e.g., not a different JVM retrieving the pool from JNDI), or a new instance with the same settings created otherwise.

In the application server environment, typically the application server’s DataSource instance will be stored in JNDI, instead of the PostgreSQL ConnectionPoolDataSource implementation.

在应用程序环境中,应用程序可以将DataSource存储在 JNDI 中,这样它就不必引用可能需要使用到的DataSource,这些DataSource可供所有应用程序组件使用。例 11.2 “DataSource JNDI 代码示例” 中显示了这方面的一个示例。

例 11.2.DataSource JNDI 代码示例

Application code to initialize a pooling DataSource and add it to JNDI might look like this:

PGPoolingDataSource source = new PGPoolingDataSource();
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 conn = null;
try {
    DataSource source = (DataSource) new InitialContext().lookup("DataSource");
    conn = source.getConnection();
    // use connection
} catch (SQLException e) {
    // log error
} catch (NamingException e) {
    // DataSource wasn't found in JNDI
} finally {
    if (con != null) {
        try {
            conn.close();
        } catch (SQLException e) {}
    }
}

Tomcat 设置

NOTE

The postgresql.jar file must be placed in $CATALINA_HOME/common/lib in both Tomcat 4 and 5.

The absolute easiest way to set this up in either tomcat instance is to use the admin web application that comes with Tomcat, simply add the datasource to the context you want to use it in.

Setup for Tomcat 4 place the following inside the < Context> tag inside conf/server.xml

<Resource name="jdbc/postgres" scope="Shareable" type="javax.sql.DataSource"/>
<ResourceParams name="jdbc/postgres">
	<parameter>
		<name>validationQuery</name>
		<value>select version();</value>
	</parameter>
	<parameter>
		<name>url</name>
		<value>jdbc:postgresql://localhost/davec</value>
	</parameter>
	<parameter>
		<name>password</name>
		<value>davec</value>
	</parameter>
	<parameter>
		<name>maxActive</name>
		<value>4</value>
	</parameter>
	<parameter>
		<name>maxWait</name>
		<value>5000</value>
	</parameter>
	<parameter>
		<name>driverClassName</name>
		<value>org.postgresql.Driver</value>
	</parameter>
	<parameter>
		<name>username</name>
		<value>davec</value>
	</parameter>
	<parameter>
		<name>maxIdle</name>
		<value>2</value>
	</parameter>
</ResourceParams>

Setup for Tomcat 5, you can use the above method, except that it goes inside the < DefaultContext> tag inside the < Host> tag. eg. < Host> … < DefaultContext> …

Alternatively there is a conf/Catalina/hostname/context.xml file. For example http://localhost:8080/servlet-example has a directory $CATALINA_HOME/conf/Catalina/localhost/servlet-example.xml file. Inside this file place the above xml inside the < Context> tag

Then you can use the following code to access the connection.

import javax.naming.*;
import javax.sql.*;
import java.sql.*;
public class DBTest {

    String foo = "Not Connected";
    int bar = -1;

    public void init() {
        try {
            Context ctx = new InitialContext();
            if (ctx == null)
                throw new Exception("Boom - No Context");

            // /jdbc/postgres is the name of the resource above
            DataSource ds = (DataSource) ctx.lookup("java:comp/env/jdbc/postgres");

            if (ds != null) {
                Connection conn = ds.getConnection();

                if (conn != null) {
                    foo = "Got Connection " + conn.toString();
                    Statement stmt = conn.createStatement();
                    ResultSet rst = stmt.executeQuery("select id, foo, bar from testdata");

                    if (rst.next()) {
                        foo = rst.getString(2);
                        bar = rst.getInt(3);
                    }
                    conn.close();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public String getFoo() {
        return foo;
    }

    public int getBar() {
        return bar;
    }
}