Question

J'utilise le code suivant pour interroger une base de données depuis mon jsp, mais j'aimerais en savoir plus sur ce qui se passe dans les coulisses.

Ce sont mes deux questions principales.

La balise accède-t-elle directement au ResultSet ou le résultat de la requête est-il stocké dans une structure de données en mémoire?

Quand la connexion est-elle fermée?

<%@ 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>

Remarque: je me suis toujours opposé à l'exécution de requêtes dans le jsp, mais mon jeu de résultats est trop volumineux pour être stocké en mémoire entre mon action et mon jsp. L'utilisation de cette bibliothèque de balises semble être la solution la plus simple.

Était-ce utile?

La solution

Observations basées sur le source de org.apache.taglibs.standard.tag.common.sql.QueryTagSupport

La balise tag parcourt le ResultSet et place toutes les données dans des tableaux, des cartes et des listes. Donc, tout est chargé en mémoire avant même de commencer à boucler.

La connexion est ouverte lorsque la balise de début de la requête est rencontrée (méthode doStartTag). Les résultats sont récupérés lorsque la balise de fin de requête est rencontrée (méthode doEndTag). La connexion est fermée dans la méthode doFinally.

C’est bref, c’est affreux.

Autres conseils

La chose clé ici est la suivante: javax.servlet.jsp.jstl.sql.Result

C’est ce que JSTL utilise à la suite d’une requête SQL. Si vous regardez l’interface, elle a cette méthode:

public java.util.SortedMap [] getRows ()

c: pourChaque " sait " à propos de javax.servlet.jsp.jstl.sql.Result, puisque le résultat n’est rien d’autre que forEach n’en connaît (Collections, tableaux, itérateurs, etc.).

Donc, tout cela implique que la requête SQL absorbe l'intégralité du résultat dans la RAM.

Si vous avez déplacé votre requête dans le fichier JSP parce que vous ne voulez pas charger l'intégralité du jeu de résultats dans une collection, il ne semble pas que la balise SQL résolve ce problème pour vous.

En vérité, vous devriez rechercher un modèle de liste de valeurs.

Mais un "simple" Une solution à votre problème consisterait à créer un itérateur personnalisé qui "sait". à propos de votre ResultSet. Celui-ci enveloppe un jeu de résultats et ferme tout s'il rencontre une exception ou si le résultat suit son cours (comme dans un forEach). Une sorte de but spécial.

la classe publique ResultSetIterator implémente 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
    }
}

}

Ceci est, naturellement, non testé. Mais comme JSTLs forEach peut fonctionner avec un Iterator, c'est l'objet le plus simple que vous puissiez réellement lui transmettre. Cela vous empêchera de charger tout le jeu de résultats en mémoire. (Il est intéressant de noter à quel point le comportement de ResultSets est différent, mais pas tout à fait, de celui d'Iterator.)

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top