【问题标题】:How does jstl's sql tag work?jstl的sql标签是如何工作的?
【发布时间】:2010-09-10 20:13:11
【问题描述】:

我正在使用以下代码从我的 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 之间的内存中。使用这个标签库看起来是最简单的解决方案。

【问题讨论】:

    标签: java jsp web-applications jstl


    【解决方案1】:

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

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

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

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

    【讨论】:

    • 我对 OP 有同样的问题 as close,即 JSTL 的 SQL 标记库中的 when the database connection is closed?。我也查看了 QueryTagSupport 的来源,可以找到doFinally() 方法。但我的问题是,它是从哪里调用的?
    • @Gnanam - doFinally() 方法似乎在“sql:query”标签的末尾执行。这是有道理的,因为 ResultSet 在您有机会对其进行迭代之前已被加载到单独的数据结构中。
    • 我相信close of the "sql:query" tag 你指的是doEndTag() 方法。但是我仍然没有发现 doFinally() 方法也从这里被调用。在QueryTagSupport 类的整个源代码中,我找不到从任何地方调用的方法。有什么想法吗?
    • doEndTag() 方法是从已转换为Java 类的JSP 中调用的。此调用存在于 try/catch/finally 块中。在 finally 块中,对标记对象调用 doFinally。就我而言,该类是 org.apache.taglibs.standard.tag.rt.sql.QueryTag。 QueryTag 扩展了 org.apache.taglibs.standard.tag.common.sql.QueryTagSupport,它实际上包含 doFinally() 方法并执行 close()。
    【解决方案2】:

    这里的关键是: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 几乎完全不同,但并不完全不同。)

    【讨论】:

      猜你喜欢
      • 2011-12-03
      • 1970-01-01
      • 2023-03-21
      • 1970-01-01
      • 1970-01-01
      • 2021-08-31
      • 2015-02-11
      • 2012-11-30
      相关资源
      最近更新 更多