Jon*_*nas 135 java jdbc java-7 try-with-resources
我有一种方法可以使用JDBC从数据库中获取用户:
public List<User> getUser(int userId) {
String sql = "SELECT id, name FROM users WHERE id = ?";
List<User> users = new ArrayList<User>();
try {
Connection con = DriverManager.getConnection(myConnectionURL);
PreparedStatement ps = con.prepareStatement(sql);
ps.setInt(1, userId);
ResultSet rs = ps.executeQuery();
while(rs.next()) {
users.add(new User(rs.getInt("id"), rs.getString("name")));
}
rs.close();
ps.close();
con.close();
} catch (SQLException e) {
e.printStackTrace();
}
return users;
}
Run Code Online (Sandbox Code Playgroud)
我应该如何使用Java 7 try-with-resources来改进此代码?
我已尝试使用下面的代码,但它使用了许多try块,并且不会提高可读性.我应该try-with-resources以其他方式使用吗?
public List<User> getUser(int userId) {
String sql = "SELECT id, name FROM users WHERE id = ?";
List<User> users = new ArrayList<>();
try {
try (Connection con = DriverManager.getConnection(myConnectionURL);
PreparedStatement ps = con.prepareStatement(sql);) {
ps.setInt(1, userId);
try (ResultSet rs = ps.executeQuery();) {
while(rs.next()) {
users.add(new User(rs.getInt("id"), rs.getString("name")));
}
}
}
} catch (SQLException e) {
e.printStackTrace();
}
return users;
}
Run Code Online (Sandbox Code Playgroud)
Jea*_*sky 181
我意识到这很久以前就已经回答了,但是想要建议一种避免嵌套的try-with-resources双块的额外方法.
public List<User> getUser(int userId) {
try (Connection con = DriverManager.getConnection(myConnectionURL);
PreparedStatement ps = createPreparedStatement(con, userId);
ResultSet rs = ps.executeQuery()) {
// process the resultset here, all resources will be cleaned up
} catch (SQLException e) {
e.printStackTrace();
}
}
private PreparedStatement createPreparedStatement(Connection con, int userId) throws SQLException {
String sql = "SELECT id, username FROM users WHERE id = ?";
PreparedStatement ps = con.prepareStatement(sql);
ps.setInt(1, userId);
return ps;
}
Run Code Online (Sandbox Code Playgroud)
bpg*_*rgo 71
在您的示例中不需要外部try,因此您至少可以从3下降到2,并且您也不需要;在资源列表的末尾关闭.使用两个try块的优点是您的所有代码都预先存在,因此您不必引用单独的方法:
public List<User> getUser(int userId) {
String sql = "SELECT id, username FROM users WHERE id = ?";
List<User> users = new ArrayList<>();
try (Connection con = DriverManager.getConnection(myConnectionURL);
PreparedStatement ps = con.prepareStatement(sql)) {
ps.setInt(1, userId);
try (ResultSet rs = ps.executeQuery()) {
while(rs.next()) {
users.add(new User(rs.getInt("id"), rs.getString("name")));
}
}
} catch (SQLException e) {
e.printStackTrace();
}
return users;
}
Run Code Online (Sandbox Code Playgroud)
小智 5
创建一个额外的包装类怎么样?
package com.naveen.research.sql;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public abstract class PreparedStatementWrapper implements AutoCloseable {
protected PreparedStatement stat;
public PreparedStatementWrapper(Connection con, String query, Object ... params) throws SQLException {
this.stat = con.prepareStatement(query);
this.prepareStatement(params);
}
protected abstract void prepareStatement(Object ... params) throws SQLException;
public ResultSet executeQuery() throws SQLException {
return this.stat.executeQuery();
}
public int executeUpdate() throws SQLException {
return this.stat.executeUpdate();
}
@Override
public void close() {
try {
this.stat.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
Run Code Online (Sandbox Code Playgroud)
然后在调用类中,您可以将 prepareStatement 方法实现为:
try (Connection con = DriverManager.getConnection(JDBC_URL, prop);
PreparedStatementWrapper stat = new PreparedStatementWrapper(con, query,
new Object[] { 123L, "TEST" }) {
@Override
protected void prepareStatement(Object... params) throws SQLException {
stat.setLong(1, Long.class.cast(params[0]));
stat.setString(2, String.valueOf(params[1]));
}
};
ResultSet rs = stat.executeQuery();) {
while (rs.next())
System.out.println(String.format("%s, %s", rs.getString(2), rs.getString(1)));
} catch (SQLException e) {
e.printStackTrace();
}
Run Code Online (Sandbox Code Playgroud)
正如其他人所说,虽然try不需要外部代码,但您的代码基本上是正确的。这里还有一些想法。
DataSource这里的其他答案是正确和好的,例如bpgergo接受的答案。但是没有显示 的使用DataSource,DriverManager在现代 Java 中通常推荐过度使用。
因此,为了完整起见,这里有一个从数据库服务器获取当前日期的完整示例。这里使用的数据库是Postgres。任何其他数据库都可以类似地工作。您可以将使用替换org.postgresql.ds.PGSimpleDataSource为DataSource适合您的数据库的实现。您的特定驱动程序或连接池可能会提供一个实现,如果您走那条路的话。
一DataSource,在实现中不被关闭,因为它永远不会“打开”。ADataSource不是资源,未连接到数据库,因此它不持有网络连接或数据库服务器上的资源。ADataSource是连接数据库时所需的简单信息,包括数据库服务器的网络名称或地址、用户名、用户密码以及最终建立连接时要指定的各种选项。所以,你的DataSource执行对象不不走你的尝试,与资源括号内。
您的代码正确使用了嵌套的 try-with-resources 语句。
请注意,在下面的示例代码中,我们还两次使用了 try-with-resources 语法,一个嵌套在另一个中。外部try定义了两个资源:Connection和PreparedStatement。内部try定义ResultSet资源。这是一种常见的代码结构。
如果从内部抛出异常,并且没有在那里捕获,则ResultSet资源将自动关闭(如果存在,则不为空)。之后,PreparedStatement将关闭,最后Connection关闭。资源会按照它们在 try-with-resource 语句中声明的相反顺序自动关闭。
这里的示例代码过于简单。正如所写的那样,它可以使用单个 try-with-resources 语句执行。但在实际工作中,您可能会在嵌套的一对try调用之间做更多的工作。例如,您可能从用户界面或 POJO 中提取值,然后?通过调用PreparedStatement::set…方法将这些值传递给SQL 中的占位符。
请注意,try-with-resources 括号内的最后一个资源语句后面的分号是可选的。我将它包含在我自己的工作中有两个原因:一致性和它看起来完整,并且它使复制粘贴混合行变得更容易,而不必担心行尾分号。您的 IDE 可能会将最后一个分号标记为多余,但保留它也无妨。
Java 9中的新功能是对 try-with-resources 语法的增强。我们现在可以声明和填充try语句括号外的资源。我还没有发现这对 JDBC 资源有用,但请在您自己的工作中牢记这一点。
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." );
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
103968 次 |
| 最近记录: |