以编程方式为 Postgres JDBC 生成一个“DataSource”对象

Posted

技术标签:

【中文标题】以编程方式为 Postgres JDBC 生成一个“DataSource”对象【英文标题】:Produce a `DataSource` object for Postgres JDBC, programmatically 【发布时间】:2017-12-18 21:45:11 【问题描述】:

JDBC Tutorial 建议使用DataSource 对象来获取数据库连接,而不是使用DriverManager 类。引用Connecting with DataSource Objects页面:

DataSource objects ... 连接到数据源的首选方法。

如何为 JDBC 连接到 Postgres 获取这样的对象?我有一个 JDBC 驱动程序。

现在,我不想像 this 或 this 这样摆弄 JNDI。

我可以在我的 Java 应用程序中以编程方式实例化 DataSource 吗?还是我必须自己实现那个DataSource 接口?

【问题讨论】:

类似:Configure a DataSource to connect to a managed Postgres server on Digital Ocean with SSL/TLS encryption 【参考方案1】:

tl;博士

与来自 jdbc.postgresql.org 的 JDBC 驱动程序捆绑在一起的 PGSimpleDataSource 类实现了 DataSource 接口。在PGSimpleDataSource 对象中配置您的数据库连接详细信息,并作为DataSource 对象传递。

PGSimpleDataSource ds = new PGSimpleDataSource() ;  
ds.setServerName( "localhost" );  
ds.setDatabaseName( "your_db_name_here" );   
ds.setUser( "scott" );       
ds.setPassword( "tiger" );   

根据需要使用该对象连接到数据库。使用方便的try-with-resources 语法。

try
(
    Connection conn = ds.getConnection() ;
) 
 … 

JDBC驱动的实现

您的JDBC driver 可能会为您提供DataSource 接口的实现。

此实现的对象包含建立和配置与数据库的连接所需的信息,例如:

数据库用户名和密码 数据库服务器的IP地址和端口号

最多可以提供三种实现方式:

这种实现通常是DriverManager 周围的薄包装。每次对此类实现的对象调用 DataSource::getConnection 时,您都会获得一个新的数据库连接。 或者,一个实现可能在下面使用connection pool 来提供已经存在的连接。这些连接被分发出去并重新签入,就像图书馆里的书一样,可以回收重复使用。 实现可以支持Java Transaction API,支持X/Open XA,以满足复杂的需求,例如跨多个资源(如数据库和消息队列)协调事务。不是很常用,所以我这里忽略了这种类型。

来自 jdbc.postgresql.org 的驱动程序

来自jdbc.postgresql.org 的开源免费驱动程序提供了所有三种类型的DataSource 实现。但是作者不建议在生产中实际使用他们的connection pool type;如果要池化,请使用第三方连接池库。我们忽略了the XA type。

那么让我们看看DataSource的简单的fresh-connection-each-time实现:org.postgresql.ds.PGSimpleDataSource

配置数据源对象

实例化一个空对象,然后调用一系列setter methods 来为您的特定数据库场景进行配置。 setter 方法继承自org.postgresql.ds.common.BaseDataSource

我们还没有向上转换到接口DataSource,所以我们可以调用various setter methods。请参阅Data Sources and JNDI 页面上的示例代码和讨论。

PGSimpleDataSource ds = new PGSimpleDataSource() ;  // Empty instance.
ds.setServerName( "localhost" );  // The value `localhost` means the Postgres cluster running locally on the same machine.
ds.setDatabaseName( "testdb" );   // A connection to Postgres must be made to a specific database rather than to the server as a whole. You likely have an initial database created named `public`.
ds.setUser( "testuser" );         // Or use the super-user 'postgres' for user name if you installed Postgres with defaults and have not yet created user(s) for your application.
ds.setPassword( "password" );     // You would not really use 'password' as a password, would you?

通常我会使用这些单独的 setter 方法。或者,您可以构造一个字符串,一个 URL,其中包含要在 DataSource 上一次性设置的各种信息。如果你想走这条路,请致电setUrl

这涵盖了基础知识。但是您可能想要或需要其他一些二传手。其中大部分是在服务器上设置Postgres property 值。这些属性都有智能默认值,但您可能希望在特殊情况下覆盖。

ds.setPortNumber( 6787 ) ;  // If not using the default '5432'.
ds.setApplicationName( "whatever" ) ;   // Identify the application making this connection to the database. Also a clever way to back-door some information to the Postgres server, as you can encode small values into this string for later parsing. 
ds.setConnectTimeout( … ) ;  // The timeout value used for socket connect operations, in whole seconds. If connecting to the server takes longer than this value, the connection is broken.
ds.setSocketTimeout( … ) ;  // The timeout value used for socket read operations. If reading from the server takes longer than this value, the connection is closed. This can be used as both a brute force global query timeout and a method of detecting network problems.
ds.setReadOnly( boolean ) ;  // Puts this connection in read-only mode.

如果使用TLS(以前称为 SSL)来加密数据库连接以防止窃听或恶意操纵,请为此使用多个 setter。

对于任何没有特定 setter 方法的 Postgres 属性,您可以调用 setProperty( PGProperty property, String value )

您可以通过调用许多 getter 方法中的任何一个来检查或验证此数据源的设置。

配置PGSimpleDataSource 后,您可以将DataSource 对象传递给代码库的其余部分。这可以使您的代码库免受更改为另一个 DataSource 实现或更改为 another JDBC driver 的冲击。

DataSource dataSource = ds ;  // Upcasting from concrete class to interface.
return dataSource ; 

使用数据源

使用DataSource 非常简单,因为它只提供了两种方法,即getConnection 的一对变体,为您的数据库工作获取Connection 对象。

Connection conn = dataSource.getConnection() ; 

完成Connection 后,最佳做法是确保将其关闭。要么使用try-with-resources syntax 自动关闭连接,要么显式关闭它。

conn.close() ;

请记住,DataSource 实际上并不是数据源。 DataSource 实际上是生成/访问数据库连接的来源。在我看来,这是用词不当,因为我认为它是ConnectionSourceDataSource 与您的数据库对话的时间仅足以使用用户名和密码登录。登录后,您可以使用Connection 对象与数据库进行交互。

存储您的DataSource

配置后,您希望将 DataSource 对象保留在缓存中。无需反复重新配置。 implementation should be written to be thread-safe。您可以随时随地拨打getConnection

对于一个简单的小型 Java 应用程序,您可能希望将其作为字段存储在单例或静态全局变量中。

对于基于Servlet 的应用程序(例如Vaadin 应用程序),您将创建一个实现ServletContextListener 接口的类。在该类中,您将在 Web 应用程序启动时建立您的 DataSource 对象。从那里您可以通过传递给setAttribute 将对象存储在ServletContext 对象中。 Context 是“网络应用”的技术术语。通过调用getAttribute 并转换为DataSource 进行检索。

在企业场景中,DataSource 可以存储在符合JNDI 的实现中。一些Servlet containers(例如Apache Tomcat)可能会提供JNDI 实现。一些组织使用诸如LDAP server 之类的服务器。使用 JNDI 注册和检索您的 DataSource 对象在 Stack Overflow 上的许多其他问答中都有介绍。

【讨论】:

以上是关于以编程方式为 Postgres JDBC 生成一个“DataSource”对象的主要内容,如果未能解决你的问题,请参考以下文章

JDBC编程的方式

以编程方式检查 JDBC 中的打开连接

Postgres/JDBC/逻辑复制 - 内存不足问题

以编程方式定义 SQL 列的大小

如何以编程方式为 URL 数据库生成相关标签?

以编程方式生成的 HTML 电子邮件被 Outlook 归类为垃圾邮件 [重复]