![](/img/trans.png)
[英]How to avoid sonar error “use try-with-resources or close this … in a ”finally“ clause” if custom close method is used (java 6)?
[英]Method for SELECT in java: what to return and how to close resources?
我是 Java 的新手。 我正在嘗試創建一個 class ,其中包含一些用於 Java 1.6 中 SQL 操作的實用方法,以用於一般目的。
我寫了一個selectMethod
來獲取 DB 上SELECT
的結果。
問題:如果我的selectMethod
方法返回一個ResultSet
類型,那么當我調用該方法時,其相關資源( ResultSet
, Statement
)將不可避免地保持打開狀態:我無法從另一個方法關閉它們,因為它們已被創建到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 怎么樣?
Statement
和ResultSet
對象是該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
不是你的,你不能關閉它……
resultSet
( try-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.