繁体   English   中英

java中SELECT的方法:返回什么以及如何关闭资源?

[英]Method for SELECT in java: what to return and how to close resources?

我是 Java 的新手。 我正在尝试创建一个 class ,其中包含一些用于 Java 1.6 中 SQL 操作的实用方法,以用于一般目的。

我写了一个selectMethod来获取 DB 上SELECT的结果。

问题:如果我的selectMethod方法返回一个ResultSet类型,那么当我调用该方法时,其相关资源( ResultSetStatement )将不可避免地保持打开状态:我无法从另一个方法关闭它们,因为它们已被创建到selectMethod ...另一方面,我不能在selectMethod中关闭它们,否则后者不会返回任何东西。

所以我的观点是:==> 我怎样才能关闭资源? <==

我无法使用try-with-resource ,因为我使用的是早期版本的 Java。

在类似的问题中,我还没有找到克服这个问题的“一般方法”。

解决方案:目前我只知道两种方法:

A)避免创建返回ResultSet类型的selectMethod ,只创建一个在内部执行查询的方法,以及对查询结果的其他操作。 然后将所有资源关闭到方法中。

例子:

public String selectMethod(String query, Connection conn) {
    Statement stmt = null;
    ResultSet rset = null;
    String myOutput = "";
    try {
        stmt = conn.PreparedStatement(query);
        rset = st.executeQuery();
        myOutput = rs.getString(2);   // the "particular task" I perform on the data retrieved
    } catch (SQLException e) {
        System.out.println(e);
    } finally {
        rset.close();
        stmt.close();
    }
    return myOutput;
}
...
...
// method call:
String myQuery = "SELECT colA FROM table_name WHERE table_id = 192837465";
String theDataINeeded = selectMethod(myQuery, myConn);
myConn.close();

A)的缺点:我想要一个通用的 SQL class 并且不限于特定任务......

B)进入selectMethod ,将ResultSet数据复制到CachedRowSet并返回CachedRowSet

例子:

public CachedRowSet selectMethod(String query, Connection conn) {
    Statement stmt = null;
    ResultSet rset = null;
    CachedRowSetImpl crset = null;
    try {
        stmt = conn.PreparedStatement(query);
        rset = st.executeQuery();
        crset = new CachedRowSetImpl();
        crset.populate(rset);
    } catch (SQLException e) {
        System.out.println(e);
    } finally {
        rset.close();
        stmt.close();
    }
    return crset;
}
...
...
// method call:
String myQuery = "SELECT colA FROM table_name WHERE table_id = 192837465";
CachedRowSetImpl theDataINeeded = new CachedRowSetImpl();
theDataINeeded = selectMethod(myQuery, myConn);
myConn.close();

B)的缺点:我怕在做多行的 select 时 memory 用完。 我无法使用LIMIT... OFFSET...进行分页查询,因为我的数据库版本低于 Oracle 12g,并且我不想进行查询操作以row_number() between... and... 我希望我的实用程序可以处理任何类型的查询。

有谁知道其他解决方案?

提前致谢。

另一种选择是为如下方法提供结果映射器;

public interface ResultMapper<T> {

    /**
     * Extract row from the columns of the {@link ResultSet}.
     * Implementors should just get the values for the columns and not call {@link ResultSet#next()} or {@link ResultSet#close()}
     *
     * @param rs the rs
     * @return the t
     */
    T extractRow(ResultSet rs);
}

//
// see ResultMapper
// Be aware that this method returns list of type <T>
public <T> List<T> selectMethod(final String query, final Connection conn, final ResultMapper<T> resultMapper) {
        final List<T> results = new LinkedList<>();

        Statement stmt = null;
        ResultSet rset = null;
        try {

            stmt = conn.createStatement();
            rset = stmt.executeQuery(query);
            while (rset.next()) {
                results.add(resultMapper.extractRow(rset));
            }
            return results;
        } catch (final SQLException e) {
            // handle sql exceprion
        } finally {
            try {
                rset.close();
                stmt.close();
                conn.close();
            } catch (SQLException throwables) {
                // log error while closing
            }
        }
        
        return results;
    }

由于您是 Java 的新手,您可能需要查看java generics

因此,根据您提供的示例,我们要提取字符串字段。 您可以像这样定义结果映射器:

public class MyResultMapper implements ResultMapper<String> {
    @Override
    public String extractRow(final ResultSet rs) {
        return rs.getString(2);   // the "particular task" I perform on the data retrieved
    }
}

然后你可以像这样执行查询:

String output = SqlUtils.selectMethod(sql, conn, new MyResultMapper()).stream()
                .findFirst();

创建一个实现AutoClosable (或Closable我不记得这些是什么时候引入 Java)的Result object 怎么样?

StatementResultSet对象是该Result实例的属性,而您的selectMethod()只是它的工厂。 然后你可以这样做:

Connection connection = …
Result result = null;
String query = "…";
try
{
     result = selectMethod( query, connection );
     ResultSet resultSet = result.getResultSet();
     myOutput = resultSet.getString(2);   // the "particular task" I perform on the data retrieved
}
catch( …Exception e )
{
    // Handle the exception
    …
}
finally
{
    if( result != null ) result.close();
}

class Result大致如下所示:

public final class Result implements Closeable
{
    private final Statement m_Statement;
    private final ResultSet m_ResultSet;

    public Result( final Statement statement, final ResultSet resultSet )
    {
        m_Statement = statement;
        m_ResultSet = resultSet;
    }

    public final ResultSet getResultSet() { return m_ResultSet; }

    @Override
    public final void close() throws SQLException
    {
        m_ResultSet.close();
        m_Statement.close();
    }
}

当然,我的错误处理很差,需要改进……

connection不是你的,你不能关闭它……

resultSettry-catch-finally块中的那个)只是Result实例中m_ResultSet持有的引用的副本。 因此,对resultSet.close()的调用是多余的(或已过时——甚至因我糟糕的错误处理而变得危险)。

你的selectMethod()看起来像这样:

public final Result selectMethod( String query, Connection connection ) throws SQLException
{
    Statement statement = connection.PreparedStatement( query );
    ResultSet resultSet = statement.executeQuery();
    return new Result( statement, resultSet );
}

根据 tquadrat 的回答,我找到了另一种关闭资源的方法,只需在 class SQLUtil中定义属性:

public class SQLUtil {
    
    //attributes:
    Connection connection;
    ResultSet resultSet;
    Statement statement;

    public void connectToMyDB {
        // populate the connection attribute
        ...
    }
    
    public ResultSet selectMethod (String query) {
        // here I populate the statement and resultSet attribute
        ...
        return resultSet;
    }
    
    ...
    public void cleanUpRes () {
        if (resultSet != null) resultSet.close();
        if (statement != null) resultSet.close();
    }
}

然后,当我调用selectMethod时,我将获得resultSet ,而不会将其存储在 memory 中。 处理后,我将通过调用cleanUpRes()将其关闭。

我看到的缺点:

1 - 只有一个与SQLUtil object 相关的resultSet ,所以如果我必须一起处理两个或更多查询,我必须实例化许多不同的SQLUtil对象......也许使用resultSet属性数组可以代替只有一个? 顺便说一句,这超出了问题的 scope :)

2 - 外部方法负责关闭资源

你怎么看? 谢谢大家

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM