DataSource
首先,最好将访问您的数据库放在DataSource 接口后面。这使您能够更改获取数据库连接的策略,而无需更改代码中使用这些数据库连接的所有位置。见Oracle Tutorial。
您的JDBC driver 应提供DataSource 的实现。例如,考虑来自https://jdbc.postgresql.org 的 Postgres JDBC 驱动程序。该驱动程序supplies two implementations:一个用于直接连接到数据库,另一个用于pooling connections。通过使用DataSource 作为外观,您可以在代码中的一个位置在其中任何一个位置之间切换,甚至使用第3 方连接池解决方案,而无需更改您调用DataSource::getConnection() 的许多位置。
您可能想要替换的第三种实现是distributed transactions。
另一个交换接口后面的具体类的例子:也许你决定切换 JDBC 驱动程序。也许对于您的 Postgres 数据库,您决定从 jdbc.postgresql.org driver 切换到 pgjdbc-ng driver。您希望能够在一处更改此配置,而不会破坏所有现有的应用程序代码。
这是在DataSource 接口后面使用具体实现的示例。
public javax.sql.DataSource obtainDataSource() {
org.postgresql.ds.PGSimpleDataSource dataSource = new PGSimpleDataSource() ;
dataSource("AcmeInvoices database data source") ;
source.setServerName( "localhost" ) ;
source.setDatabaseName( "invoicing" ) ;
source.setUser( "Scott" ) ;
source.setPassword( "tiger" ) ;
return dataSource ; // Returning a reference to the object of this concrete class `PGSimpleDataSource` as an object of the interface `DataSource`.
}
您在上面的代码 sn-p 中看到的调用方法会因您的数据库和 JDBC driver 的功能而有很大差异。例如,如果您使用与数据库的加密连接,您将需要调用其他方法来配置加密密钥。
您很早就设置了这个DataSource 对象,可能是您应用启动的一部分。在某处保留参考。或者保留几个:对于应用程序的不同部分,您可能有不同的连接类型(不同的用户名和密码),其中需要不同的安全性或不同的用户角色。
将您的代码库周围的DataSource 对象传递给在您的数据库中插入/选择/删除的许多方法。在每个方法中,他们调用myDataSource.getConnection(),然后继续执行他们的SQL。当该方法完成其工作时,它应该通过调用Connection::close 来关闭连接。更好的是,使用 try-with-resources 自动关闭连接。
如果连接来自连接池,则连接并没有真正关闭。而是将连接返回到池中。这是使用接口的另一个例子,这里是Connection。接口后面的具体类可以选择如何响应调用close 方法——并且该具体类可以在以后换出而不会破坏调用代码。
打开连接
我是否应该在 DatabaseController 构造函数中建立数据库连接,使其打开一次并保持打开状态
不,保持连接打开而不做进一步的工作通常不是一个好主意。
基本上,您将创建自己的连接池。而连接池并不是一件简单的事情。池化有很多问题需要考虑,你不太可能认为它们更有能力解决它们。我见过的所有连接池工具都有各自的错误、问题和限制。所以不要自己承担这个任务。如果您确实选择使用池化,请找到一个健壮且陈旧的现有实现。并彻底研究其文档,以确保您了解如何正确配置和使用池。
坦率地说,我自己停止使用连接池。在我看来,对于用户/连接频率较低的系统来说,各种风险和复杂性是不值得的。例如,查看各种池解决方案中设置的默认超时,其中池连接在一段时间不使用后关闭。如果您在池中的活动大多低于超时,那么您实际上不会从池中受益。此外,我发现大多数关于打开与数据库的连接昂贵的说法都被夸大了,尤其是对于同一台机器上的本地数据库。
示例
我应该在插入/删除/选择方法之前放置一个连接数据库的方法
以下是一些示例场景,展示了您应该如何在将连接用于某些特定数据库任务的位置附近获取和关闭数据库连接。
在您通过查找存储在数据库中的用户和组来验证用户身份的登录屏幕中,当用户单击Login 按钮时,您的代码应检索对DataSource 对象的引用,调用getConnection ,进行用户和组查找,然后立即关闭连接。
当用户继续查找过期发票时,请执行相同操作。检索DataSource,调用getConnection,运行SQL 查找任何过期发票,将该发票数据作为缓存加载到内存中,调用Connection::close 关闭与数据库的连接,然后继续构建GUI 以显示缓存的发票数据。
当用户单击按钮以对其中一张发票执行某些操作时,请执行相同的过程。检索DataSource,调用getConnection,运行SQL 以获取所需主键值的特定发票行,检索其他发票字段和相关行,如发票项目,调用Connection::close 以关闭与数据库,然后继续您的操作,例如生成包含缓存发票详细信息及其发票项目行的电子邮件。请注意,您可能如何在此操作方法中多次使用该连接,首先点击 invoice 表,然后再次点击 invoice-item 表。当手头的所有直接工作最终完成时,关闭连接。
请记住,在每次获得连接并关闭连接的情况下,如果池化:
- 您获得的连接可能是新的,也可能不是新的。
- “关闭”将连接返回到池,而不是实际关闭它。
池可能会在某个时候关闭连接,并且在某个时候可能会用新的连接替换它。但这对您的所有用户身份验证代码和发票查找代码都是透明的。