jstl的sql标记如何工作?
我正在使用以下代码从我的jsp中查询数据库,但我想了解更多有关幕后情况的信息。
这是我的两个主要问题。
标签是直接访问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之间的内存中。使用此标记库看起来是最简单的解决方案。
解决方案
这里的关键是:javax.servlet.jsp.jstl.sql.Result
这就是JSTL使用SQL查询的结果。如果我们查看该接口,则它具有以下方法:
公共java.util.SortedMap [] getRows()
c:forEach"知道"有关javax.servlet.jsp.jstl.sql.Result的信息,因为Result不是forEach知道的其他信息(集合,数组,迭代器等)。
因此,所有这些都意味着SQL查询会将整个结果集都吸收到RAM中。
如果我们由于不想将整个结果集加载到集合中而将查询移至JSP,那么看起来SQL标记似乎无法为我们解决该问题。
实际上,我们应该查找"值列表模式"。
但是,针对问题的"简单"解决方案将是创建一个"知道"ResultSet的自定义迭代器。这个包装一个结果集,并在遇到异常或者结果运行过程中关闭所有内容(就像在forEach中一样)。一种特殊目的的东西。
`
公共类ResultSetIterator实现了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行为与Iterator几乎完全不同,但并不完全相同。)
基于org.apache.taglibs.standard.tag.common.sql.QueryTagSupport的来源的观察
taglib遍历ResultSet并将所有数据放入数组,Map和List中。因此,在开始循环之前,所有内容都已加载到内存中。
遇到查询开始标记时将打开连接(doStartTag方法)。遇到查询结束标记时将检索结果(doEndTag方法)。该连接在doFinally方法中关闭。
简而言之,这绝对可怕。