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