KickJava   Java API By Example, From Geeks To Geeks.

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


1 /*****************************************************************
2  * Licensed to the Apache Software Foundation (ASF) under one
3  * or more contributor license agreements. See the NOTICE file
4  * distributed with this work for additional information
5  * regarding copyright ownership. The ASF licenses this file
6  * to you under the Apache License, Version 2.0 (the
7  * "License"); you may not use this file except in compliance
8  * with the License. You may obtain a copy of the License at
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing,
13  * software distributed under the License is distributed on an
14  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15  * KIND, either express or implied. See the License for the
16  * specific language governing permissions and limitations
17  * under the License.
18  ****************************************************************/

19
20 package org.apache.cayenne.access.jdbc;
21
22 import java.io.IOException JavaDoc;
23 import java.io.PrintWriter JavaDoc;
24 import java.io.StringWriter JavaDoc;
25 import java.sql.Connection JavaDoc;
26 import java.sql.ResultSet JavaDoc;
27 import java.sql.SQLException JavaDoc;
28 import java.sql.Statement JavaDoc;
29 import java.util.ArrayList JavaDoc;
30 import java.util.List JavaDoc;
31 import java.util.Map JavaDoc;
32
33 import org.apache.cayenne.CayenneException;
34 import org.apache.cayenne.CayenneRuntimeException;
35 import org.apache.cayenne.DataRow;
36 import org.apache.cayenne.access.ResultIterator;
37 import org.apache.cayenne.access.types.ExtendedType;
38 import org.apache.cayenne.map.DbAttribute;
39 import org.apache.cayenne.map.DbEntity;
40 import org.apache.cayenne.util.Util;
41
42 /**
43  * A ResultIterator over the underlying JDBC ResultSet.
44  *
45  * @since 1.2
46  * @author Andrus Adamchik
47  */

48 // Replaces DefaultResultIterator
49
public class JDBCResultIterator implements ResultIterator {
50
51     // Connection information
52
protected Connection JavaDoc connection;
53     protected Statement JavaDoc statement;
54     protected ResultSet JavaDoc resultSet;
55
56     protected RowDescriptor rowDescriptor;
57
58     DataRowPostProcessor postProcessor;
59
60     // last indexed PK
61
protected DbEntity rootEntity;
62     protected int[] pkIndices;
63
64     protected int mapCapacity;
65
66     protected boolean closingConnection;
67     protected boolean closed;
68
69     protected boolean nextRow;
70     protected int fetchedSoFar;
71     protected int fetchLimit;
72     
73     private String JavaDoc[] labels;
74     private int[] types;
75
76     /**
77      * Creates new JDBCResultIterator that reads from provided ResultSet.
78      */

79     public JDBCResultIterator(Connection JavaDoc connection, Statement JavaDoc statement,
80             ResultSet JavaDoc resultSet, RowDescriptor descriptor, int fetchLimit)
81             throws CayenneException {
82
83         this.connection = connection;
84         this.statement = statement;
85         this.resultSet = resultSet;
86         this.rowDescriptor = descriptor;
87         this.fetchLimit = fetchLimit;
88
89         this.mapCapacity = (int) Math.ceil((descriptor.getWidth()) / 0.75);
90
91         checkNextRow();
92         
93         if(nextRow) {
94             // extract column parameters to speed up processing...
95
ColumnDescriptor[] columns = descriptor.getColumns();
96             int width = columns.length;
97             labels = new String JavaDoc[width];
98             types = new int[width];
99             
100             for(int i = 0; i < width; i++) {
101                 labels[i] = columns[i].getLabel();
102                 types[i] = columns[i].getJdbcType();
103             }
104         }
105     }
106
107     /**
108      * Returns all unread data rows from ResultSet, closing this iterator if needed.
109      */

110     public List JavaDoc dataRows(boolean close) throws CayenneException {
111         List JavaDoc list = new ArrayList JavaDoc();
112
113         try {
114             while (this.hasNextRow()) {
115                 list.add(this.nextDataRow());
116             }
117         }
118         finally {
119             if (close) {
120                 this.close();
121             }
122         }
123
124         return list;
125     }
126
127     /**
128      * Returns true if there is at least one more record that can be read from the
129      * iterator.
130      */

131     public boolean hasNextRow() {
132         return nextRow;
133     }
134
135     /**
136      * Returns the next result row as a Map.
137      */

138     public Map JavaDoc nextDataRow() throws CayenneException {
139         if (!hasNextRow()) {
140             throw new CayenneException(
141                     "An attempt to read uninitialized row or past the end of the iterator.");
142         }
143
144         // read
145
Map JavaDoc row = readDataRow();
146
147         // rewind
148
checkNextRow();
149
150         return row;
151     }
152
153     /**
154      * Returns a map of ObjectId values from the next result row. Primary key columns are
155      * determined from the provided DbEntity.
156      */

157     public Map JavaDoc nextObjectId(DbEntity entity) throws CayenneException {
158         if (!hasNextRow()) {
159             throw new CayenneException(
160                     "An attempt to read uninitialized row or past the end of the iterator.");
161         }
162
163         // index id
164
if (rootEntity != entity || pkIndices == null) {
165             this.rootEntity = entity;
166             indexPK();
167         }
168
169         // read ...
170
// TODO: note a mismatch with 1.1 API - ID positions are preset and are
171
// not affected by the entity specified (think of deprecating/replacing this)
172
Map JavaDoc row = readIdRow();
173
174         // rewind
175
checkNextRow();
176
177         return row;
178     }
179
180     public void skipDataRow() 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         checkNextRow();
187     }
188
189     /**
190      * Closes ResultIterator and associated ResultSet. This method must be called
191      * explicitly when the user is finished processing the records. Otherwise unused
192      * database resources will not be released properly.
193      */

194     public void close() throws CayenneException {
195         if (!closed) {
196
197             nextRow = false;
198
199             StringWriter JavaDoc errors = new StringWriter JavaDoc();
200             PrintWriter JavaDoc out = new PrintWriter JavaDoc(errors);
201
202             try {
203                 resultSet.close();
204             }
205             catch (SQLException JavaDoc e1) {
206                 out.println("Error closing ResultSet");
207                 e1.printStackTrace(out);
208             }
209
210             if (statement != null) {
211                 try {
212                     statement.close();
213                 }
214                 catch (SQLException JavaDoc e2) {
215                     out.println("Error closing PreparedStatement");
216                     e2.printStackTrace(out);
217                 }
218             }
219
220             // TODO: andrus, 5/8/2006 - closing connection within JDBCResultIterator is
221
// obsolete as this is bound to transaction closing in DataContext. Deprecate
222
// this after 1.2
223

224             // close connection, if this object was explicitly configured to be
225
// responsible for doing it
226
if (connection != null && isClosingConnection()) {
227                 try {
228                     connection.close();
229                 }
230                 catch (SQLException JavaDoc e3) {
231                     out.println("Error closing Connection");
232                     e3.printStackTrace(out);
233                 }
234             }
235
236             try {
237                 out.close();
238                 errors.close();
239             }
240             catch (IOException JavaDoc ioex) {
241                 // ignore - this is never going to happen, after all we are writing to
242
// StringBuffer in memory
243
}
244
245             StringBuffer JavaDoc buf = errors.getBuffer();
246             if (buf.length() > 0) {
247                 throw new CayenneException("Error closing ResultIterator: " + buf);
248             }
249
250             closed = true;
251         }
252     }
253
254     /**
255      * Returns the number of columns in the result row.
256      */

257     public int getDataRowWidth() {
258         return rowDescriptor.getWidth();
259     }
260
261     /**
262      * Moves internal ResultSet cursor position down one row. Checks if the next row is
263      * available.
264      */

265     protected void checkNextRow() throws CayenneException {
266         nextRow = false;
267         try {
268             if ((fetchLimit <= 0 || fetchedSoFar < fetchLimit) && resultSet.next()) {
269                 nextRow = true;
270                 fetchedSoFar++;
271             }
272         }
273         catch (SQLException JavaDoc e) {
274             throw new CayenneException("Error rewinding ResultSet", e);
275         }
276     }
277
278     /**
279      * Reads a row from the internal ResultSet at the current cursor position.
280      */

281     protected Map JavaDoc readDataRow() throws CayenneException {
282         try {
283             DataRow dataRow = new DataRow(mapCapacity);
284             ExtendedType[] converters = rowDescriptor.getConverters();
285
286             int resultWidth = labels.length;
287
288             // process result row columns,
289
for (int i = 0; i < resultWidth; i++) {
290                 // note: jdbc column indexes start from 1, not 0 unlike everywhere else
291
Object JavaDoc val = converters[i].materializeObject(resultSet, i + 1, types[i]);
292                 dataRow.put(labels[i], val);
293             }
294
295             if (postProcessor != null) {
296                 postProcessor.postprocessRow(resultSet, dataRow);
297             }
298
299             return dataRow;
300         }
301         catch (CayenneException cex) {
302             // rethrow unmodified
303
throw cex;
304         }
305         catch (Exception JavaDoc otherex) {
306             throw new CayenneException("Exception materializing column.", Util
307                     .unwindException(otherex));
308         }
309     }
310
311     /**
312      * Reads a row from the internal ResultSet at the current cursor position, processing
313      * only columns that are part of the ObjectId of a target class.
314      */

315     protected Map JavaDoc readIdRow() throws CayenneException {
316         try {
317             DataRow idRow = new DataRow(2);
318             ExtendedType[] converters = rowDescriptor.getConverters();
319             int len = pkIndices.length;
320
321             for (int i = 0; i < len; i++) {
322
323                 // dereference column index
324
int index = pkIndices[i];
325
326                 // note: jdbc column indexes start from 1, not 0 as in arrays
327
Object JavaDoc val = converters[index].materializeObject(
328                         resultSet,
329                         index + 1,
330                         types[index]);
331                 idRow.put(labels[index], val);
332             }
333
334             if (postProcessor != null) {
335                 postProcessor.postprocessRow(resultSet, idRow);
336             }
337
338             return idRow;
339         }
340         catch (CayenneException cex) {
341             // rethrow unmodified
342
throw cex;
343         }
344         catch (Exception JavaDoc otherex) {
345             throw new CayenneException("Exception materializing id column.", Util
346                     .unwindException(otherex));
347         }
348     }
349
350     /**
351      * Creates an index of PK columns in the RowDescriptor.
352      */

353     protected void indexPK() {
354         if (rootEntity == null) {
355             throw new CayenneRuntimeException("Null root DbEntity, can't index PK");
356         }
357
358         int len = rootEntity.getPrimaryKey().size();
359
360         // sanity check
361
if (len == 0) {
362             throw new CayenneRuntimeException("Root DbEntity has no PK defined: "
363                     + rootEntity);
364         }
365
366         int[] pk = new int[len];
367         ColumnDescriptor[] columns = rowDescriptor.getColumns();
368         for (int i = 0, j = 0; i < columns.length; i++) {
369             DbAttribute a = (DbAttribute) rootEntity.getAttribute(columns[i].getName());
370             if (a != null && a.isPrimaryKey()) {
371                 pk[j++] = i;
372             }
373         }
374
375         this.pkIndices = pk;
376     }
377
378     /**
379      * Returns <code>true</code> if this iterator is responsible for closing its
380      * connection, otherwise a user of the iterator must close the connection after
381      * closing the iterator.
382      */

383     public boolean isClosingConnection() {
384         return closingConnection;
385     }
386
387     /**
388      * Sets the <code>closingConnection</code> property.
389      */

390     public void setClosingConnection(boolean flag) {
391         this.closingConnection = flag;
392     }
393
394     public RowDescriptor getRowDescriptor() {
395         return rowDescriptor;
396     }
397
398     void setPostProcessor(DataRowPostProcessor postProcessor) {
399         this.postProcessor = postProcessor;
400     }
401 }
402
Popular Tags