java - jtls - jstl+el
¿Cómo funciona la etiqueta sql de jstl? (2)
La clave aquí es esta: javax.servlet.jsp.jstl.sql.Result
Eso es lo que JSTL usa como resultado de una consulta SQL. Si miras la interfaz, tiene este método:
public java.util.SortedMap [] getRows ()
c: para Cada "sabe" sobre javax.servlet.jsp.jstl.sql.Result, ya que el Resultado no es algo más que lo que se sabe de Eaach (Colecciones, matrices, iteradores, etc.).
Entonces, todo eso implica que la consulta SQL chupará todo el conjunto de resultados en la RAM.
Si movió su consulta al JSP porque no quería cargar todo el conjunto de resultados en una colección, entonces no parece que la etiqueta SQL le solucione ese problema.
En verdad, deberías buscar el Patrón de lista de valores.
Pero una solución "simple" para su problema sería crear un iterador personalizado que "conozca" su ResultSet. Éste envuelve un conjunto de resultados y cierra todo si encuentra una excepción o si el resultado sigue su curso (como lo haría en un evento para cada uno). Algo especial.
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
}
}
}
Esto es, naturalmente, no probado. Pero como JSTLs forEach puede funcionar con un iterador, es el objeto más simple que realmente podría pasarle. Esto evitará que cargue todo el conjunto de resultados en la memoria. (Como un lado interesante, es notable cómo casi, pero no del todo, a diferencia de Iterator es un comportamiento ResultSets).
Estoy usando el siguiente código para consultar una base de datos desde mi jsp, pero me gustaría saber más sobre lo que está sucediendo detrás de escena.
Estas son mis dos preguntas principales.
¿La etiqueta accede al ResultSet directamente, o el resultado de la consulta se almacena en una estructura de datos en la memoria?
¿Cuándo se cierra la conexión?
<%@ 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>
Nota: siempre he estado en contra de ejecutar consultas en jsp, pero mi conjunto de resultados es demasiado grande para almacenar en la memoria entre mi acción y mi jsp. Usar esta biblioteca de etiquetas parece ser la solución más fácil.
Observaciones basadas en la fuente de org.apache.taglibs.standard.tag.common.sql.QueryTagSupport
El taglib atraviesa el ResultSet y coloca todos los datos en matrices, mapas y listas. Entonces, todo se carga en la memoria incluso antes de que comiences a hacer bucles.
La conexión se abre cuando se encuentra la etiqueta de inicio de la consulta (método doStartTag). Los resultados se recuperan cuando se encuentra la etiqueta de finalización de la consulta (método doEndTag). La conexión se cierra en el método doFinally.
En pocas palabras, es absolutamente horrible.