001: /*
002: * hgcommons 7
003: * Hammurapi Group Common Library
004: * Copyright (C) 2003 Hammurapi Group
005: *
006: * This program is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU Lesser General Public
008: * License as published by the Free Software Foundation; either
009: * version 2 of the License, or (at your option) any later version.
010: *
011: * This program is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * Lesser General Public License for more details.
015: *
016: * You should have received a copy of the GNU Lesser General Public
017: * License along with this library; if not, write to the Free Software
018: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
019: *
020: * URL: http://www.hammurapi.biz/hammurapi-biz/ef/xmenu/hammurapi-group/products/products/hgcommons/index.html
021: * e-Mail: support@hammurapi.biz
022: */
023: package biz.hammurapi.sql;
024:
025: import java.sql.Connection;
026: import java.sql.PreparedStatement;
027: import java.sql.ResultSet;
028: import java.sql.SQLException;
029: import java.sql.Statement;
030: import java.util.Collection;
031: import java.util.Iterator;
032: import java.util.LinkedList;
033: import java.util.NoSuchElementException;
034:
035: /**
036: * Collection backed by the database. It doesn't hold resultsets open
037: * but executes SQL statements for every method call. Iterators produced
038: * by this collection are backed by open ResultSet.
039: * @author Pavel Vlasov
040: * @version $Revision: 1.6 $
041: */
042: class ResultSetCollection implements Collection {
043: private SQLProcessor processor;
044: private String sql;
045: private Parameterizer parameterizer;
046: private Projector projector;
047: private Integer pageSize;
048: private int pageNum;
049:
050: /**
051: * @param processor
052: * @param sql
053: * @param parameterizer
054: * @param projector
055: */
056: public ResultSetCollection(SQLProcessor processor, String sql,
057: Parameterizer parameterizer, Projector projector) {
058: this .processor = processor;
059: this .sql = sql;
060: this .parameterizer = parameterizer;
061: this .projector = projector;
062: }
063:
064: /**
065: * @param processor2
066: * @param string
067: * @param parameterizer2
068: * @param projector2
069: * @param pageNum
070: * @param pageSize
071: */
072: public ResultSetCollection(SQLProcessor processor, String sql,
073: Parameterizer parameterizer, Projector projector,
074: int pageNum, int pageSize) {
075: this (processor, sql, parameterizer, projector);
076: this .pageSize = new Integer(pageSize);
077: this .pageNum = pageNum;
078: }
079:
080: public int size() {
081: try {
082: final int[] ret = { 0 };
083: final int[] counter = { 0 };
084: processor.processSelect(sql, parameterizer,
085: new RowProcessor() {
086: public boolean process(ResultSet rs) {
087: if (onPage(counter)) {
088: ++ret[0];
089: }
090: return true;
091: }
092: });
093: return ret[0];
094: } catch (SQLException e) {
095: throw new SQLRuntimeException(e);
096: }
097: }
098:
099: public void clear() {
100: throw new UnsupportedOperationException();
101: }
102:
103: public boolean isEmpty() {
104: try {
105: final boolean[] ret = { true };
106: final int[] counter = { 0 };
107: processor.processSelect(sql, parameterizer,
108: new RowProcessor() {
109: public boolean process(ResultSet rs) {
110: if (onPage(counter)) {
111: ret[0] = false;
112: return false;
113: }
114: return true;
115: }
116: });
117: return ret[0];
118: } catch (SQLException e) {
119: throw new SQLRuntimeException(e);
120: }
121: }
122:
123: public Object[] toArray() {
124: return new LinkedList(this ).toArray();
125: }
126:
127: public boolean add(Object o) {
128: throw new UnsupportedOperationException();
129: }
130:
131: public boolean contains(final Object o) {
132: try {
133: final boolean[] ret = { false };
134: final int[] counter = { 0 };
135: processor.processSelect(sql, parameterizer,
136: new RowProcessor() {
137: public boolean process(ResultSet rs)
138: throws SQLException {
139: if (onPage(counter)) {
140: if (o.equals(projector.project(rs))) {
141: ret[0] = true;
142: }
143: }
144: return !ret[0];
145: }
146: });
147: return ret[0];
148: } catch (SQLException e) {
149: throw new SQLRuntimeException(e);
150: }
151: }
152:
153: public boolean remove(final Object o) {
154: try {
155: final boolean[] ret = { false };
156: final int[] counter = { 0 };
157: processor.processSelect(sql, parameterizer,
158: new RowProcessor() {
159: public boolean process(ResultSet rs)
160: throws SQLException {
161: if (onPage(counter)) {
162: if (o.equals(projector.project(rs))) {
163: rs.deleteRow();
164: ret[0] = true;
165: }
166: }
167: return true;
168: }
169: });
170: return ret[0];
171: } catch (SQLException e) {
172: throw new SQLRuntimeException(e);
173: }
174: }
175:
176: public boolean addAll(Collection c) {
177: throw new UnsupportedOperationException();
178: }
179:
180: public boolean containsAll(Collection c) {
181: try {
182: final Collection param = new LinkedList(c);
183: final int[] counter = { 0 };
184: processor.processSelect(sql, parameterizer,
185: new RowProcessor() {
186: public boolean process(ResultSet rs)
187: throws SQLException {
188: if (onPage(counter)) {
189: Object o = projector.project(rs);
190: param.remove(o);
191: }
192: return !param.isEmpty();
193: }
194: });
195: return param.isEmpty();
196: } catch (SQLException e) {
197: throw new SQLRuntimeException(e);
198: }
199: }
200:
201: public boolean removeAll(final Collection c) {
202: try {
203: final boolean[] ret = { false };
204: final int[] counter = { 0 };
205: processor.processSelect(sql, parameterizer,
206: new RowProcessor() {
207: public boolean process(ResultSet rs)
208: throws SQLException {
209: if (onPage(counter)) {
210: if (c.contains(projector.project(rs))) {
211: rs.deleteRow();
212: ret[0] = true;
213: }
214: }
215: return true;
216: }
217: });
218: return ret[0];
219: } catch (SQLException e) {
220: throw new SQLRuntimeException(e);
221: }
222: }
223:
224: public boolean retainAll(final Collection c) {
225: try {
226: final boolean[] ret = { false };
227: final int[] counter = { 0 };
228: processor.processSelect(sql, parameterizer,
229: new RowProcessor() {
230: public boolean process(ResultSet rs)
231: throws SQLException {
232: if (onPage(counter)) {
233: if (!c.contains(projector.project(rs))) {
234: rs.deleteRow();
235: ret[0] = true;
236: }
237: }
238: return true;
239: }
240: });
241: return ret[0];
242: } catch (SQLException e) {
243: throw new SQLRuntimeException(e);
244: }
245: }
246:
247: public Iterator iterator() {
248: return new DataIterator() {
249: private Connection connection;
250: private Statement statement;
251: private ResultSet rs;
252: private int counter;
253:
254: public void close() throws SQLException {
255: //System.out.println("Iterator close: "+ --openIteratorCounter);
256:
257: try {
258: if (rs != null) {
259: rs.close();
260: }
261: } finally {
262: rs = null;
263: try {
264: if (statement != null) {
265: statement.close();
266: }
267: } finally {
268: statement = null;
269: try {
270: if (connection != null) {
271: processor.releaseConnection(connection);
272: }
273: } finally {
274: connection = null;
275: }
276: }
277: }
278: }
279:
280: private Object o;
281:
282: {
283: //System.out.println("Iterator open: "+ ++openIteratorCounter);
284: //new Exception("Iterator stack trace for debugging").printStackTrace(System.out);
285:
286: long start = processor.getTimeIntervalCategory() == null ? 0
287: : processor.getTimeIntervalCategory().getTime();
288: try {
289: connection = processor.getConnection();
290: if (parameterizer == null) {
291: statement = connection.createStatement();
292: rs = statement.executeQuery(sql);
293: } else {
294: PreparedStatement ps = connection
295: .prepareStatement(sql);
296: statement = ps;
297: parameterizer.parameterize(ps);
298: rs = ps.executeQuery();
299: // moving to the first record in page.
300: while (pageSize != null
301: && (++counter < pageSize.intValue()
302: * (pageNum - 1)) && rs.next())
303: ;
304: }
305: } catch (SQLException e) {
306: try {
307: close();
308: } catch (SQLException ee) {
309: // We already have an exception, so we ignore cleanup one.
310: }
311: throw new SQLRuntimeException(e);
312: } finally {
313: if (processor.getTimeIntervalCategory() != null) {
314: processor.getTimeIntervalCategory()
315: .addInterval(sql, start);
316: }
317: }
318: }
319:
320: public void remove() {
321: if (nextCalled) {
322: try {
323: rs.deleteRow();
324: } catch (SQLException e) {
325: try {
326: close();
327: } catch (SQLException ee) {
328: // Nothing
329: }
330:
331: throw new SQLRuntimeException(e);
332: }
333: } else {
334: throw new IllegalStateException("Call next() first");
335: }
336: }
337:
338: private boolean nextCalled = true;
339: private boolean lastResult = false;
340:
341: public boolean hasNext() {
342: if (nextCalled) {
343: nextCalled = false;
344: try {
345: if ((pageSize == null || counter < pageSize
346: .intValue()
347: * pageNum)
348: && rs.next()) {
349: o = projector.project(rs);
350: if (o instanceof DataAccessObject) {
351: ((DataAccessObject) o)
352: .setSQLProcessor(processor);
353: }
354:
355: lastResult = true;
356: } else {
357: lastResult = false;
358: close();
359: }
360: } catch (SQLException e) {
361: try {
362: close();
363: } catch (SQLException ee) {
364: // Nothing
365: }
366:
367: throw new SQLRuntimeException(e);
368: }
369: }
370:
371: return lastResult;
372: }
373:
374: public Object next() {
375: if (nextCalled) {
376: if (!hasNext()) {
377: throw new NoSuchElementException();
378: }
379: }
380:
381: nextCalled = true;
382: ++counter;
383: return o;
384: }
385:
386: protected void finalize() throws Throwable {
387: if (connection != null) {
388: System.err.println("WARN ["
389: + this .getClass().getName()
390: + "]: Connection leak for query '" + sql
391: + "'");
392: }
393:
394: try {
395: close();
396: } catch (SQLException e) {
397: e.printStackTrace();
398: }
399: super .finalize();
400: }
401:
402: };
403: }
404:
405: public Object[] toArray(Object[] a) {
406: return new LinkedList(this ).toArray(a);
407: }
408:
409: /**
410: * @param counter
411: * @return
412: */
413: private boolean onPage(final int[] counter) {
414: return pageSize == null
415: || (++counter[0] > (pageNum - 1) * pageSize.intValue() && counter[0] <= pageNum
416: * pageSize.intValue());
417: }
418:
419: }
|