1

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

我已经写下了 a以在数据库上selectMethod获取 a 的结果。SELECT

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

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

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

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

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

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

例子:

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 类,并且不限于特定任务......

B)进入selectMethod,将数据复制ResultSet到aCachedRowSet并返回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)的缺点:我害怕在多行选择时内存不足。我无法使用分页进行查询,因为LIMIT... OFFSET...我的数据库版本低于 Oracle 12g,并且我不想对 insert 进行查询操作row_number() between ... and ...。我希望我的实用程序可以处理任何类型的查询。

有谁知道其他解决方案?

提前致谢。

4

3 回答 3

1

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

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

Connection connection = …
Result result = null;
String query = "…&quot;;
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();
}

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

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

connection不是你的,你不能关闭它……</p>

并且resultSet(块内的那个try-catch-finally)只是实例m_ResultSet内由 持有的引用的副本Result。因此,调用 toresultSet.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 );
}
于 2021-02-15T11:06:19.517 回答
1

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

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 泛型

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

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();
于 2021-02-15T10:36:33.797 回答
0

根据 tquadrat 的回答,我找到了另一种通过在类中定义属性来关闭资源的方法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而不将它存储在内存中。处理完之后,我会通过调用来关闭它cleanUpRes()

我看到的缺点:

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

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

你怎么看?谢谢大家

于 2021-02-15T16:59:24.753 回答