001: /*
002: * Created on Feb 22, 2004
003: */
004: package net.sourceforge.orbroker;
005:
006: import java.sql.Connection;
007: import java.sql.SQLException;
008: import java.util.ArrayList;
009: import java.util.Collection;
010: import java.util.Iterator;
011: import java.util.List;
012:
013: /**
014: * An abstract queryable connection.
015: *
016: * @author Nils Kilden-Pedersen
017: * @see net.sourceforge.orbroker.Query
018: * @see net.sourceforge.orbroker.Transaction
019: */
020:
021: abstract class QueryableConnection extends BrokerConnection {
022:
023: /**
024: * Check that query only returned one row as expected.
025: * @param stmId Statement id
026: * @param qr Query result
027: * @throws MoreThanOneRowException
028: */
029: private static final void checkForOne(String stmId, QueryResult qr)
030: throws MoreThanOneRowException {
031: if (qr.nextRow()) {
032: String msg = "Statement '" + stmId
033: + "' returned more than 1 row.";
034: throw new MoreThanOneRowException(msg);
035: }
036: }
037:
038: private static final void checkRowRange(int startRow, int rowCount) {
039: if (startRow < 1) {
040: throw new IllegalArgumentException(
041: "Starting row cannot be less than 1. Passed as "
042: + startRow);
043: }
044: if (rowCount < 1) {
045: throw new IllegalArgumentException(
046: "Row count must be greater than 0. Passed as "
047: + rowCount);
048: }
049: }
050:
051: protected QueryableConnection(Broker broker, Integer isolationLevel) {
052: super (broker, isolationLevel);
053: }
054:
055: protected QueryableConnection(Broker broker, Connection connection) {
056: super (broker, connection);
057: }
058:
059: /**
060: * @param stm
061: * @param useScrollable
062: * @return query result
063: * @throws BrokerException
064: */
065: protected final QueryResult executeQuery(Statement stm,
066: boolean useScrollable) throws BrokerException {
067: try {
068: return stm.executeQuery(getActiveConnection(),
069: useScrollable, getConnectionContext());
070: } catch (SQLException e) {
071: if (retryFailedStatement(e)) {
072: return executeQuery(stm, useScrollable);
073: }
074: throw new QueryException(e);
075: }
076: }
077:
078: /**
079: * Return a list of 0..n objects. An empty {@link List} will be returned if
080: * result set is empty.
081: *
082: * @param statementID Statement id
083: * @return List of result objects
084: * @throws BrokerException
085: */
086: public final List selectMany(String statementID)
087: throws BrokerException {
088:
089: List resultList = new ArrayList();
090: selectMany(statementID, resultList);
091: return resultList;
092: }
093:
094: /**
095: * Fill Collection with 0..n objects. The supplied Collection does not have
096: * to be empty. Objects are added to Collection.
097: *
098: * @param statementID Statement id
099: * @param resultCollection
100: * The collection where the result will be added.
101: * @return Number of objects added to collection.
102: * @throws BrokerException
103: */
104: public final int selectMany(String statementID,
105: Collection resultCollection) throws BrokerException {
106:
107: int initCollectionSize = resultCollection.size();
108: Statement stm = getStatement(statementID);
109: QueryResult qr = executeQuery(stm, false);
110: ResultRow row = qr.getResultRow();
111: try {
112: while (qr.nextRow()) {
113: Object obj = stm.buildResultObject(row);
114: resultCollection.add(obj);
115: }
116: } finally {
117: qr.close();
118: }
119: return resultCollection.size() - initCollectionSize;
120: }
121:
122: /**
123: * Return a live iterator. Very useful for large rows
124: * that, for memory concerns, need to be built a few at a time,
125: * controlled by the fetch size. The Iterator must be iterated
126: * with the source object active, i.e. a
127: * {@link Query} or {@link Transaction} must be open.
128: * @param statementID Statement id
129: * @param fetchSize Row buffer size
130: * @return Streamed {@link Iterator} of result objects.
131: */
132: public Iterator iterate(String statementID, int fetchSize) {
133: Statement stm = getStatement(statementID);
134: QueryResult qr = executeQuery(stm, false);
135: qr.setFetchSize(fetchSize);
136: return new ResultSetIterator(qr, stm);
137: }
138:
139: /**
140: * Close iterator. Use this to close a live iterator returned by
141: * {@link #iterate(String, int)}. Only necessary if iterator has
142: * not been exhausted by {@link Iterator#hasNext() hasNext()}
143: * returning <code>false</code>
144: * @param iterator The not fully iterated iterator.
145: * @see #iterate(String, int)
146: */
147: public final void closeIterator(Iterator iterator) {
148: if (iterator instanceof ResultSetIterator) {
149: ((ResultSetIterator) iterator).closeIterator();
150: }
151: }
152:
153: /**
154: * Fill Collection with 0..n objects. The supplied Collection does not have
155: * to be empty. Objects are added to Collection.
156: *
157: * @param statementID Statement id
158: * @param resultCollection
159: * The collection where the result will be added.
160: * @param startRow The start row to return. First row is 1.
161: * @param rowCount The number of rows to return
162: * @return Number of objects added to collection.
163: * @throws BrokerException
164: */
165: public final int selectMany(String statementID,
166: Collection resultCollection, int startRow, int rowCount)
167: throws BrokerException {
168:
169: checkRowRange(startRow, rowCount);
170: int initCollectionSize = resultCollection.size();
171: Statement stm = getStatement(statementID);
172: int rowCounter = 0;
173: QueryResult qr = executeQuery(stm, supportsScrollable());
174: qr.setFetchSize(startRow + rowCount - 1);
175: ResultRow row = qr.getResultRow(startRow, supportsScrollable());
176: qr.setFetchSize(rowCount);
177: try {
178: while (rowCounter < rowCount && qr.nextRow()) {
179: Object obj = stm.buildResultObject(row);
180: resultCollection.add(obj);
181: rowCounter++;
182: }
183: } finally {
184: qr.close();
185: }
186: return resultCollection.size() - initCollectionSize;
187: }
188:
189: /**
190: * Return a list of 0..n objects. An empty {@link List} will be returned if
191: * result set is empty or if <code>startRow</code> is greater than
192: * the result set size.
193: *
194: * @param statementID Statement id
195: * @param startRow The start row to return. First row is 1.
196: * @param rowCount The number of rows to return
197: * @return List of result objects
198: * @throws BrokerException
199: */
200: public final List selectMany(String statementID, int startRow,
201: int rowCount) throws BrokerException {
202:
203: List resultList = new ArrayList(rowCount);
204: selectMany(statementID, resultList, startRow, rowCount);
205: return resultList;
206: }
207:
208: /**
209: * Return a single result object.
210: *
211: * @param statementID Statement id
212: * @return the result object, or <code>null</code> if query returned 0
213: * records.
214: * @throws BrokerException
215: * @throws MoreThanOneRowException
216: */
217: public final Object selectOne(String statementID)
218: throws BrokerException, MoreThanOneRowException {
219: Statement stm = getStatement(statementID);
220: QueryResult qr = executeQuery(stm, false);
221: qr.setFetchSize(1);
222: Object obj;
223: ResultRow row = qr.getResultRow();
224: try {
225: if (qr.nextRow()) {
226: obj = stm.buildResultObject(row);
227: checkForOne(statementID, qr);
228: } else {
229: obj = null;
230: }
231: } finally {
232: qr.close();
233: }
234: return obj;
235: }
236:
237: /**
238: * Use supplied result object to apply values to. Any constructor or
239: * factory method defined for mapping will be ignored.
240: * @param statementID Statement id
241: * @param resultObject result object to map to
242: * @return <code>false</code> if query didn't return a record.
243: * @throws BrokerException
244: * @throws MoreThanOneRowException
245: */
246: public final boolean selectOne(String statementID,
247: Object resultObject) throws BrokerException,
248: MoreThanOneRowException {
249:
250: Statement stm = getStatement(statementID);
251: QueryResult qr = executeQuery(stm, false);
252: qr.setFetchSize(1);
253: ResultRow row = qr.getResultRow();
254: try {
255: if (!qr.nextRow()) {
256: return false;
257: }
258: stm.buildResultObject(row, resultObject);
259: checkForOne(statementID, qr);
260: } finally {
261: qr.close();
262: }
263: return true;
264: }
265:
266: /**
267: * Return one result object from a query containing many.
268: * Returns <code>null</code> if query is empty or returnRow is out of range.
269: * @param statementID Statement id
270: * @param fromRow Row to return. First row is 1.
271: * @return result object from row
272: * @throws BrokerException
273: */
274: public Object selectOneFromMany(String statementID, int fromRow)
275: throws BrokerException {
276: int rowCount = 1;
277: checkRowRange(fromRow, rowCount);
278: Statement stm = getStatement(statementID);
279: QueryResult qr = executeQuery(stm, supportsScrollable());
280: qr.setFetchSize(fromRow);
281: ResultRow row = qr.getResultRow(fromRow, supportsScrollable());
282: qr.setFetchSize(rowCount);
283: Object obj;
284: try {
285: if (qr.nextRow()) {
286: obj = stm.buildResultObject(row);
287: } else {
288: obj = null;
289: }
290: } finally {
291: qr.close();
292: }
293: return obj;
294: }
295:
296: }
|