3

我正在使用以下代码从我的 jsp 中查询数据库,但我想更多地了解幕后发生的事情。

这是我的两个主要问题。

标签是直接访问 ResultSet,还是将查询结果存储在内存中的数据结构中?

连接何时关闭?

<%@ taglib prefix="sql" uri="http://java.sun.com/jsp/jstl/sql" %>

<sql:query var="query" dataSource="${ds}" sql="${listQuery}"></sql:query>
<c:forEach var="row" items="${query.rows}" begin="0">
    ${row.data }
    ${row.more_data }
</c:forEach>

注意:我一直反对在 jsp 中运行查询,但是我的结果集太大而无法存储在我的操作和我的 jsp 之间的内存中。使用这个标签库看起来是最简单的解决方案。

4

2 回答 2

8

基于 org.apache.taglibs.standard.tag.common.sql.QueryTagSupport 来源的观察

taglib 遍历 ResultSet 并将所有数据放入数组、映射和列表中。因此,在您开始循环之前,所有内容都已加载到内存中。

遇到查询开始标记时打开连接(doStartTag 方法)。遇到查询结束标记时检索结果(doEndTag 方法)。连接在 doFinally 方法中关闭。

简而言之,这绝对是可怕的。

于 2008-09-18T19:23:52.063 回答
1

这里的关键是:javax.servlet.jsp.jstl.sql.Result

这就是 JSTL 用作 SQL 查询的结果。如果你看一下界面,它有这个方法:

公共 java.util.SortedMap[] getRows()

c:forEach “知道” javax.servlet.jsp.jstl.sql.Result,因为 Result 不是 forEach 知道的任何其他东西(集合、数组、迭代器等)。

因此,所有这些都意味着 SQL 查询会将整个结果集吸入 RAM。

如果您因为不想将整个结果集加载到集合中而将查询移入 JSP,那么 SQL 标记似乎不会为您解决该问题。

实际上,您应该查找值列表模式。

但是,解决您的问题的“简单”解决方案是创建一个“了解”您的 ResultSet 的自定义迭代器。如果遇到异常或结果运行其过程(就像在 forEach 中一样),它会包装一个结果集并关闭所有内容。一种特殊用途的东西。

public class ResultSetIterator implements Iterator {

Connection con;
Statement s;
ResultSet rs;
Object curObject;
boolean closed;

public ResultSetIterator(Connection con, Statement s, ResultSet rs) {
    this.con = con;
    this.s = s;
    this.rs = rs;
    closed = false;
}

public boolean hasNext() {
    advance();
    return curObject != null;
}

public Object next() {
    advance();
    if (curObject == null) {
        throw new NoSuchElementException();
    } else {
        Object result = curObject;
        curObject = null;
        return result;
    }
}

public void remove() {
    throw new UnsupportedOperationException("Not supported yet.");
}

private void advance() {
    if (closed) {
        curObject = null;
        return;
    }
    if (curObject == null) {
        try {
            if (rs.next()) {
                curObject = bindObject(rs);
            }
        } catch (SQLException ex) {
            shutDown();
            throw new RuntimeException(ex);
        }
    }
    if (curObject == null) {
        // Still no object, must be at the end of the result set
        shutDown();
    }
}

protected Object bindObject(ResultSet rs) throws SQLException {
    // Bind result set row to an object, replace or override this method
    String name = rs.getString(1);
    return name;
}

public void shutDown() {
    closed = true;
    try {
        rs.close();
    } catch (SQLException ex) {
        // Ignored
    }
    try {
        s.close();
    } catch (SQLException ex) {
        // Ignored
    }
    try {
        con.close();
    } catch (SQLException ex) {
        // Ignored
    }
}

}

这自然是未经测试的。但是由于 JSTL forEach 可以与 Iterator 一起使用,因此它是您可以真正传递给它的最简单的对象。这将阻止您将整个结果集加载到内存中。(顺便说一句,值得注意的是,ResultSets 行为与 Iterator 几乎但不完全不同。)

于 2008-09-18T19:07:43.320 回答