我正在使用以下代码从我的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之间的内存中。使用此标记库看起来是最简单的解决方案。
答案 0 :(得分:8)
基于org.apache.taglibs.standard.tag.common.sql.QueryTagSupport的来源的观察
taglib遍历ResultSet并将所有数据放入数组,映射和列表中。因此,在开始循环之前,所有内容都会加载到内存中。
遇到查询开始标记时打开连接(doStartTag方法)。遇到查询结束标记时检索结果(doEndTag方法)。连接在doFinally方法中关闭。
简而言之,这绝对是糟糕的。
答案 1 :(得分:1)
这里的关键是: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
}
}
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可以与Iterator一起使用,因此它是您可以真正传递给它的最简单的对象。这将阻止您将整个结果集加载到内存中。 (作为一个有趣的方面,值得注意的是,几乎,但并不完全不同于Iterator的ResultSet行为。)