![](/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.