正如他人所述,您的代码基本上是正确的,只是外部的try
是不必要的。以下是一些更多的想法。
DataSource
其他答案都是正确且好的,例如bpgergo的被接受的答案。但是其中没有一个显示使用DataSource
,在现代Java中通常建议使用DriverManager
的使用。
因此,为了完整起见,这里是一个完整的示例,从数据库服务器获取当前日期。此处使用的数据库是Postgres。任何其他数据库都可以类似地工作。您将使用org.postgresql.ds.PGSimpleDataSource
来替换适合您的数据库的DataSource
的实现。如果您走这条路,您的特定驱动程序或连接池可能会提供实现。
DataSource
的实现不需要关闭,因为它从未被“打开”。DataSource
不是资源,没有连接到数据库,因此它不会在数据库服务器上保持网络连接或资源。 DataSource
只是在连接到数据库时所需的信息,包括数据库服务器的网络名称或地址,用户名,用户密码以及在最终建立连接时要指定的各种选项。因此,您的DataSource
实现对象不应该放在try-with-resources括号内。
DataSource
的目的是将数据库连接信息外部化。如果在源代码中硬编码用户名、密码等信息,则更改数据库服务器配置意味着必须重新编译和部署代码,这并不好玩。相反,这些数据库配置细节应该存储在源代码
外部,然后在运行时检索。您可以通过
JNDI从命名和目录服务器(如
LDAP)检索配置详细信息。或者您可以从运行应用程序的
Servlet容器或
Jakarta EE服务器检索。
嵌套try-with-resources
您的代码正确使用了嵌套try-with-resources语句。
请注意下面的示例代码,我们还使用了两次try-with-resources语法,其中一个嵌套在另一个内部。外部try
定义了两个资源:Connection
和PreparedStatement
。内部try
定义了ResultSet
资源。这是一种常见的代码结构。
如果从内部抛出异常并且没有在那里捕获,则ResultSet
资源将自动关闭(如果存在且不为null)。随后,PreparedStatement
将被关闭,最后关闭Connection
。资源会按照它们在try-with-resource语句中声明的相反顺序自动关闭。
此处的示例代码过于简单。按照当前编写的方式,可以使用单个try-with-resources语句执行。但是在实际工作中,您可能会在嵌套的try
调用对之间执行更多的工作。例如,您可能会从用户界面或POJO中提取值,然后通过调用PreparedStatement::set…
方法将这些值传递以满足SQL中的?
占位符。
语法说明
分号
请注意,在try-with-resources括号内的最后一个资源语句后面的分号是可选的。我在自己的工作中包含它有两个原因:一致性和看起来完整,这使得复制和粘贴混合行变得更容易,而无需担心行尾分号。您的IDE可能会将最后一个分号标记为多余的,但保留它没有任何危害。
Java 9 - 在try-with-resources中使用现有vars
Java 9新特性是try-with-resources语法的增强。现在我们可以在try
语句的括号外声明和填充资源。我还没有发现它对JDBC资源有用,但要记住在您自己的工作中使用。
ResultSet
应该关闭自己,但可能不会
在理想情况下,ResultSet
会像文档承诺的那样自动关闭:
当生成ResultSet对象的Statement对象关闭、重新执行或用于检索多个结果序列中的下一个结果时,ResultSet对象会自动关闭。
不幸的是,过去一些JDBC驱动程序臭名昭著地未能实现此承诺。因此,许多JDBC程序员学会了显式关闭所有JDBC资源,包括Connection
、PreparedStatement
和ResultSet
。现代的try-with-resources语法使得这样做更加容易,并且可以使用更紧凑的代码。请注意,Java团队费心将ResultSet
标记为AutoCloseable
,我建议我们利用它。在所有JDBC资源周围使用try-with-resources可以使您的代码更具自说明性,以表明您的意图。
代码示例
package work.basil.example;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.time.LocalDate;
import java.util.Objects;
public class App
{
public static void main ( String[] args )
{
App app = new App();
app.doIt();
}
private void doIt ( )
{
System.out.println( "Hello World!" );
org.postgresql.ds.PGSimpleDataSource dataSource = new org.postgresql.ds.PGSimpleDataSource();
dataSource.setServerName( "1.2.3.4" );
dataSource.setPortNumber( 5432 );
dataSource.setDatabaseName( "example_db_" );
dataSource.setUser( "scott" );
dataSource.setPassword( "tiger" );
dataSource.setApplicationName( "ExampleApp" );
System.out.println( "INFO - Attempting to connect to database: " );
if ( Objects.nonNull( dataSource ) )
{
String sql = "SELECT CURRENT_DATE ;";
try (
Connection conn = dataSource.getConnection() ;
PreparedStatement ps = conn.prepareStatement( sql ) ;
)
{
… make `PreparedStatement::set…` calls here.
try (
ResultSet rs = ps.executeQuery() ;
)
{
if ( rs.next() )
{
LocalDate ld = rs.getObject( 1 , LocalDate.class );
System.out.println( "INFO - date is " + ld );
}
}
}
catch ( SQLException e )
{
e.printStackTrace();
}
}
System.out.println( "INFO - all done." );
}
}
try (ResultSet rs = ps.executeQuery()) {
,因为A ResultSet对象由生成它的Statement对象自动关闭。 - Alexander Farber