3

次のコードを使用してjspからデータベースにクエリを実行していますが、舞台裏で何が起こっているのかについて詳しく知りたいです。

これらは私の2つの主要な質問です。

タグは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クエリの結果として使用するものです。インターフェイスを見ると、次のメソッドがあります。

public 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の動作とはまったく異なりますが、完全ではありませんが、注目に値します。)

于 2008-09-18T19:07:43.320 に答える