Java 我应该如何使用JDBC的try-with资源?

Java 我应该如何使用JDBC的try-with资源?,java,jdbc,java-7,try-with-resources,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(

我有一种使用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;
}

在您的示例中,不需要外部尝试,因此您至少可以从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;
}
公共列表getUser(int userId){
String sql=“从id=?”的用户中选择id、用户名”;
列表用户=新建ArrayList();
try(Connection con=DriverManager.getConnection(myConnectionURL);
PreparedStatement ps=con.prepareStatement(sql)){
ps.setInt(1,用户ID);
try(ResultSet rs=ps.executeQuery()){
while(rs.next()){
添加(新用户(rs.getInt(“id”)、rs.getString(“name”));
}
}
}捕获(SQLE异常){
e、 printStackTrace();
}
返回用户;
}

我意识到这个问题很久以前就得到了回答,但我想建议一种额外的方法,以避免嵌套的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;
}
公共列表getUser(int userId){
try(Connection con=DriverManager.getConnection(myConnectionURL);
PreparedStatement ps=createPreparedStatement(con,userId);
结果集rs=ps.executeQuery()){
//在此处处理结果集,将清理所有资源
}捕获(SQLE异常){
e、 printStackTrace();
}
}
private PreparedStatement createPreparedStatement(连接con,int userId)引发SQLException{
String sql=“从id=?”的用户中选择id、用户名”;
PreparedStatement ps=con.prepareStatement(sql);
ps.setInt(1,用户ID);
返回ps;
}

创建一个额外的包装类怎么样?

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();
        }
    }
}

然后在调用类中,您可以实现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();
}

以下是一种使用lambdas和JDK 8 Supplier的简洁方法,以适应外部环境的所有要求:

try(Connection con=DriverManager.getConnection(JDBC_URL,prop);
编制的报表stmt=((供应商)()->{
试一试{
PreparedStatement s=con.prepareStatement(“从userid=?”的用户中选择userid、name和features”);
s、 setInt(1,userid);
返回s;
}catch(SQLException e){抛出新的RuntimeException(e);}
}).get();
ResultSet ResultSet=stmt.executeQuery()){
}

正如其他人所说,您的代码基本上是正确的,尽管不需要外部的
try
。这里还有一些想法

DataSource
这里的其他答案是正确的和好的,比如bpgergo。但这些都没有显示出的使用,在现代Java中,通常建议过度使用

为了完整起见,下面是一个从数据库服务器获取当前日期的完整示例。这里使用的数据库是。任何其他数据库都将以类似方式工作。您可以使用适合您的数据库的
DataSource
实现来替代。实现可能是由您的特定驱动程序提供的,如果您选择该路径,则可能是由连接池提供的

数据源
实现不需要关闭,因为它从未“打开”。
DataSource
不是资源,没有连接到数据库,因此它没有在数据库服务器上保持网络连接或资源。
DataSource
只是连接到数据库时所需的信息,包括数据库服务器的网络名称或地址、用户名、用户密码以及最终连接时需要指定的各种选项。因此,
DataSource
implementation对象不会放在try的括号内

嵌套资源尝试 您的代码正确地使用了嵌套的try with resources语句

请注意,在下面的示例代码中,我们还两次使用try with resources语法,一次嵌套在另一次中。外部
try
定义了两个资源:
Connection
PreparedStatement
。内部
try
定义
ResultSet
资源。这是一种常见的代码结构

如果从内部抛出异常,但未在那里捕获,则
ResultSet
资源将自动关闭(如果存在,则不为null)。然后,将关闭,最后关闭。资源将按与try with resource语句中声明的顺序相反的顺序自动关闭

这里的示例代码过于简单。如前所述,它可以通过一个try with resources语句执行。但是在实际工作中,您可能会在嵌套的
try
调用对之间做更多的工作。例如,您可能从用户界面或POJO中提取值,然后通过调用
PreparedStatement::set…
方法将这些值传递给SQL中的
占位符

语法注释 尾随分号 请注意,try with resources括号内最后一条resource语句后面的分号是可选的。我将它包含在自己的工作中有两个原因:一致性和完整性,它使混合行的复制粘贴更容易,而不必担心行尾分号。您的IDE可能会将最后一个分号标记为多余,但保留它并没有坏处

Java 9–在try with resources中使用现有变量 是一个增强,可以尝试使用资源语法。我们现在可以声明并填充
try
语句括号外的资源。我还没有发现这对JDBC资源有用,但请在您自己的工作中记住它

ResultSet
应自行关闭,但可能无法关闭 在理想情况下,将关闭自己,因为文档承诺:

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();
}