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



我是Java新手。我正在尝试创建一个类,其中包含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类,而不限于特定的任务。。。

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)的缺点:我害怕在对多行进行选择时内存不足。我无法使用LIMIT... OFFSET...进行带有分页的查询,因为我的DB版本低于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泛型和

因此,基于您提供的示例,我们希望提取字符串字段。你可以这样定义你的结果映射器:

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对象怎么样?

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

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块中的那个)只是m_ResultSetResult实例中保持的引用的副本。因此,对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的答案,我找到了另一种关闭资源的方法,只需在类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-外部方法负责关闭资源

你觉得怎么样?感谢大家