Вопрос

Я использую следующий код для запроса базы данных из моего jsp, но я хотел бы узнать больше о том, что происходит за кулисами.

Это мои два основных вопроса.

Обращается ли тег непосредственно к результирующему набору или результат запроса сохраняется в структуре данных в памяти?

Когда соединение закрывается?

<%@ 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.Использование этой библиотеки тегов выглядит как самое простое решение.

Это было полезно?

Решение

Наблюдения, основанные на источнике org.apache.taglibs.standard.tag.common.sql.QueryTagSupport

taglib выполняет обход результирующего набора и помещает все данные в массивы, Карты и списки.Таким образом, все загружается в память еще до того, как вы начнете выполнять цикл.

Соединение открывается при обнаружении начального тега запроса (метод doStartTag).Результаты извлекаются при обнаружении конечного тега запроса (метод doEndTag).Соединение закрывается методом doFinally.

В двух словах, это абсолютно ужасно.

Другие советы

Ключевым моментом здесь является вот что:javax.сервлет.jsp.jstl.sql.Результат

Это то, что JSTL использует в качестве результата SQL-запроса.Если вы посмотрите на интерфейс, то увидите, что в нем есть такой метод:

общедоступный java.util.SortedMap[] GetRows()

c: forEach "знает" о javax.servlet.jsp.jstl.sql.Result, поскольку Result - это не что-либо еще, о чем forEach знает (коллекции, массивы, итераторы и т.д.).

Таким образом, все это подразумевает, что SQL-запрос загрузит весь результирующий набор в оперативную память.

Если вы перенесли свой запрос в JSP, потому что не хотели загружать весь результирующий набор в коллекцию, то не похоже, что тег SQL решит эту проблему за вас.

По правде говоря, вам следует поискать шаблон списка значений.

Но "простым" решением вашей проблемы было бы создать пользовательский итератор, который "знает" о вашем результирующем наборе.Этот завершает результирующий набор и закрывает все, если он встречает исключение или если результат выполняется своим чередом (как это было бы в 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
    }
}

}

Это, естественно, непроверено.Но поскольку JSTLs forEach может работать с итератором, это самый простой объект, который вы действительно могли бы передать ему.Это предотвратит загрузку всего результирующего набора в память.(В качестве интересного отступления, примечательно, насколько поведение результирующих наборов почти, но не совсем, совершенно не похоже на поведение итератора.)

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top