KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > objectstyle > cayenne > access > jdbc > JDBCResultIterator


1 /* ====================================================================
2  *
3  * The ObjectStyle Group Software License, version 1.1
4  * ObjectStyle Group - http://objectstyle.org/
5  *
6  * Copyright (c) 2002-2005, Andrei (Andrus) Adamchik and individual authors
7  * of the software. All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  *
13  * 1. Redistributions of source code must retain the above copyright
14  * notice, this list of conditions and the following disclaimer.
15  *
16  * 2. Redistributions in binary form must reproduce the above copyright
17  * notice, this list of conditions and the following disclaimer in
18  * the documentation and/or other materials provided with the
19  * distribution.
20  *
21  * 3. The end-user documentation included with the redistribution, if any,
22  * must include the following acknowlegement:
23  * "This product includes software developed by independent contributors
24  * and hosted on ObjectStyle Group web site (http://objectstyle.org/)."
25  * Alternately, this acknowlegement may appear in the software itself,
26  * if and wherever such third-party acknowlegements normally appear.
27  *
28  * 4. The names "ObjectStyle Group" and "Cayenne" must not be used to endorse
29  * or promote products derived from this software without prior written
30  * permission. For written permission, email
31  * "andrus at objectstyle dot org".
32  *
33  * 5. Products derived from this software may not be called "ObjectStyle"
34  * or "Cayenne", nor may "ObjectStyle" or "Cayenne" appear in their
35  * names without prior written permission.
36  *
37  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
38  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
39  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
40  * DISCLAIMED. IN NO EVENT SHALL THE OBJECTSTYLE GROUP OR
41  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
42  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
43  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
44  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
45  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
46  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
47  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
48  * SUCH DAMAGE.
49  * ====================================================================
50  *
51  * This software consists of voluntary contributions made by many
52  * individuals and hosted on ObjectStyle Group web site. For more
53  * information on the ObjectStyle Group, please see
54  * <http://objectstyle.org/>.
55  */

56 package org.objectstyle.cayenne.access.jdbc;
57
58 import java.io.IOException JavaDoc;
59 import java.io.PrintWriter JavaDoc;
60 import java.io.StringWriter JavaDoc;
61 import java.sql.Connection JavaDoc;
62 import java.sql.ResultSet JavaDoc;
63 import java.sql.SQLException JavaDoc;
64 import java.sql.Statement JavaDoc;
65 import java.util.ArrayList JavaDoc;
66 import java.util.List JavaDoc;
67 import java.util.Map JavaDoc;
68
69 import org.objectstyle.cayenne.CayenneException;
70 import org.objectstyle.cayenne.CayenneRuntimeException;
71 import org.objectstyle.cayenne.DataRow;
72 import org.objectstyle.cayenne.access.ResultIterator;
73 import org.objectstyle.cayenne.access.types.ExtendedType;
74 import org.objectstyle.cayenne.map.DbAttribute;
75 import org.objectstyle.cayenne.map.DbEntity;
76 import org.objectstyle.cayenne.util.Util;
77
78 /**
79  * A ResultIterator over the underlying JDBC ResultSet.
80  *
81  * @since 1.2
82  * @author Andrei Adamchik
83  */

84 // Replaces DefaultResultIterator
85
public class JDBCResultIterator implements ResultIterator {
86
87     // Connection information
88
protected Connection JavaDoc connection;
89     protected Statement JavaDoc statement;
90     protected ResultSet JavaDoc resultSet;
91
92     protected RowDescriptor rowDescriptor;
93
94     // last indexed PK
95
protected DbEntity rootEntity;
96     protected int[] pkIndices;
97
98     protected int mapCapacity;
99
100     protected boolean closingConnection;
101     protected boolean closed;
102
103     protected boolean nextRow;
104     protected int fetchedSoFar;
105     protected int fetchLimit;
106
107     /**
108      * Creates new JDBCResultIterator that reads from provided ResultSet.
109      */

110     public JDBCResultIterator(Connection JavaDoc connection, Statement JavaDoc statement,
111             ResultSet JavaDoc resultSet, RowDescriptor descriptor, int fetchLimit)
112             throws SQLException JavaDoc, CayenneException {
113
114         this.connection = connection;
115         this.statement = statement;
116         this.resultSet = resultSet;
117         this.rowDescriptor = descriptor;
118         this.fetchLimit = fetchLimit;
119
120         this.mapCapacity = (int) Math.ceil((descriptor.getWidth()) / 0.75);
121
122         checkNextRow();
123     }
124
125     /**
126      * Returns all unread data rows from ResultSet, closing this iterator if needed.
127      */

128     public List JavaDoc dataRows(boolean close) throws CayenneException {
129         List JavaDoc list = new ArrayList JavaDoc();
130
131         try {
132             while (this.hasNextRow()) {
133                 list.add(this.nextDataRow());
134             }
135         }
136         finally {
137             if (close) {
138                 this.close();
139             }
140         }
141
142         return list;
143     }
144
145     /**
146      * Returns true if there is at least one more record that can be read from the
147      * iterator.
148      */

149     public boolean hasNextRow() {
150         return nextRow;
151     }
152
153     /**
154      * Returns the next result row as a Map.
155      */

156     public Map JavaDoc nextDataRow() throws CayenneException {
157         if (!hasNextRow()) {
158             throw new CayenneException(
159                     "An attempt to read uninitialized row or past the end of the iterator.");
160         }
161
162         try {
163             // read
164
Map JavaDoc row = readDataRow();
165
166             // rewind
167
checkNextRow();
168
169             return row;
170         }
171         catch (SQLException JavaDoc sqex) {
172             throw new CayenneException("Exception reading ResultSet.", sqex);
173         }
174     }
175
176     /**
177      * Returns a map of ObjectId values from the next result row. Primary key columns are
178      * determined from the provided DbEntity.
179      */

180     public Map JavaDoc nextObjectId(DbEntity entity) throws CayenneException {
181         if (!hasNextRow()) {
182             throw new CayenneException(
183                     "An attempt to read uninitialized row or past the end of the iterator.");
184         }
185
186         // index id
187
if (rootEntity != entity || pkIndices == null) {
188             this.rootEntity = entity;
189             indexPK();
190         }
191
192         try {
193             // read ...
194
// TODO: note a mismatch with 1.1 API - ID positions are preset and are
195
// not affected by the entity specified (think of deprecating/replacing this)
196
Map JavaDoc row = readIdRow();
197
198             // rewind
199
checkNextRow();
200
201             return row;
202         }
203         catch (SQLException JavaDoc sqex) {
204             throw new CayenneException("Exception reading ResultSet.", sqex);
205         }
206     }
207
208     public void skipDataRow() throws CayenneException {
209         if (!hasNextRow()) {
210             throw new CayenneException(
211                     "An attempt to read uninitialized row or past the end of the iterator.");
212         }
213
214         try {
215             checkNextRow();
216         }
217         catch (SQLException JavaDoc sqex) {
218             throw new CayenneException("Exception reading ResultSet.", sqex);
219         }
220     }
221
222     /**
223      * Closes ResultIterator and associated ResultSet. This method must be called
224      * explicitly when the user is finished processing the records. Otherwise unused
225      * database resources will not be released properly.
226      */

227     public void close() throws CayenneException {
228         if (!closed) {
229
230             nextRow = false;
231
232             StringWriter JavaDoc errors = new StringWriter JavaDoc();
233             PrintWriter JavaDoc out = new PrintWriter JavaDoc(errors);
234
235             try {
236                 resultSet.close();
237             }
238             catch (SQLException JavaDoc e1) {
239                 out.println("Error closing ResultSet");
240                 e1.printStackTrace(out);
241             }
242
243             if (statement != null) {
244                 try {
245                     statement.close();
246                 }
247                 catch (SQLException JavaDoc e2) {
248                     out.println("Error closing PreparedStatement");
249                     e2.printStackTrace(out);
250                 }
251             }
252
253             // close connection, if this object was explicitly configured to be
254
// responsible for doing it
255
if (connection != null && isClosingConnection()) {
256                 try {
257                     connection.close();
258                 }
259                 catch (SQLException JavaDoc e3) {
260                     out.println("Error closing Connection");
261                     e3.printStackTrace(out);
262                 }
263             }
264
265             try {
266                 out.close();
267                 errors.close();
268             }
269             catch (IOException JavaDoc ioex) {
270                 // ignore - this is never going to happen, after all we are writing to
271
// StringBuffer in memory
272
}
273
274             StringBuffer JavaDoc buf = errors.getBuffer();
275             if (buf.length() > 0) {
276                 throw new CayenneException("Error closing ResultIterator: " + buf);
277             }
278
279             closed = true;
280         }
281     }
282
283     /**
284      * Returns the number of columns in the result row.
285      */

286     public int getDataRowWidth() {
287         return rowDescriptor.getWidth();
288     }
289
290     /**
291      * Moves internal ResultSet cursor position down one row. Checks if the next row is
292      * available.
293      */

294     protected void checkNextRow() throws SQLException JavaDoc, CayenneException {
295         nextRow = false;
296         if ((fetchLimit <= 0 || fetchedSoFar < fetchLimit) && resultSet.next()) {
297             nextRow = true;
298             fetchedSoFar++;
299         }
300     }
301
302     /**
303      * Reads a row from the internal ResultSet at the current cursor position.
304      */

305     protected Map JavaDoc readDataRow() throws SQLException JavaDoc, CayenneException {
306         try {
307             Map JavaDoc dataRow = new DataRow(mapCapacity);
308             ExtendedType[] converters = rowDescriptor.getConverters();
309             ColumnDescriptor[] columns = rowDescriptor.getColumns();
310             int resultWidth = rowDescriptor.getWidth();
311
312             // process result row columns,
313
for (int i = 0; i < resultWidth; i++) {
314                 // note: jdbc column indexes start from 1, not 0 unlike everywhere else
315
Object JavaDoc val = converters[i].materializeObject(resultSet, i + 1, columns[i]
316                         .getJdbcType());
317                 dataRow.put(columns[i].getLabel(), val);
318             }
319
320             return dataRow;
321         }
322         catch (CayenneException cex) {
323             // rethrow unmodified
324
throw cex;
325         }
326         catch (Exception JavaDoc otherex) {
327             throw new CayenneException("Exception materializing column.", Util
328                     .unwindException(otherex));
329         }
330     }
331
332     /**
333      * Reads a row from the internal ResultSet at the current cursor position, processing
334      * only columns that are part of the ObjectId of a target class.
335      */

336     protected Map JavaDoc readIdRow() throws SQLException JavaDoc, CayenneException {
337         try {
338             Map JavaDoc idRow = new DataRow(2);
339             ExtendedType[] converters = rowDescriptor.getConverters();
340             ColumnDescriptor[] columns = rowDescriptor.getColumns();
341             int len = pkIndices.length;
342
343             for (int i = 0; i < len; i++) {
344
345                 // dereference column index
346
int index = pkIndices[i];
347
348                 // note: jdbc column indexes start from 1, not 0 as in arrays
349
Object JavaDoc val = converters[index].materializeObject(resultSet,
350                         index + 1,
351                         columns[index].getJdbcType());
352                 idRow.put(columns[index].getLabel(), val);
353             }
354
355             return idRow;
356         }
357         catch (CayenneException cex) {
358             // rethrow unmodified
359
throw cex;
360         }
361         catch (Exception JavaDoc otherex) {
362             throw new CayenneException("Exception materializing id column.", Util
363                     .unwindException(otherex));
364         }
365     }
366
367     /**
368      * Creates an index of PK columns in the RowDescriptor.
369      */

370     protected void indexPK() {
371         if (rootEntity == null) {
372             throw new CayenneRuntimeException("Null root DbEntity, can't index PK");
373         }
374
375         int len = rootEntity.getPrimaryKey().size();
376
377         // sanity check
378
if (len == 0) {
379             throw new CayenneRuntimeException("Root DbEntity has no PK defined: "
380                     + rootEntity);
381         }
382
383         int[] pk = new int[len];
384         ColumnDescriptor[] columns = rowDescriptor.getColumns();
385         for (int i = 0, j = 0; i < columns.length; i++) {
386             DbAttribute a = (DbAttribute) rootEntity.getAttribute(columns[i].getName());
387             if (a != null && a.isPrimaryKey()) {
388                 pk[j++] = i;
389             }
390         }
391
392         this.pkIndices = pk;
393     }
394
395     /**
396      * Returns <code>true</code> if this iterator is responsible for closing its
397      * connection, otherwise a user of the iterator must close the connection after
398      * closing the iterator.
399      */

400     public boolean isClosingConnection() {
401         return closingConnection;
402     }
403
404     /**
405      * Sets the <code>closingConnection</code> property.
406      */

407     public void setClosingConnection(boolean flag) {
408         this.closingConnection = flag;
409     }
410
411     public RowDescriptor getRowDescriptor() {
412         return rowDescriptor;
413     }
414 }
Popular Tags