簡體   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