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 package org.apache.openjpa.jdbc.sql;
20
21 import java.io.InputStream;
22 import java.io.Reader;
23 import java.math.BigDecimal;
24 import java.math.BigInteger;
25 import java.sql.Array;
26 import java.sql.Blob;
27 import java.sql.Clob;
28 import java.sql.Connection;
29 import java.sql.Ref;
30 import java.sql.ResultSet;
31 import java.sql.SQLException;
32 import java.sql.Statement;
33 import java.sql.Time;
34 import java.sql.Timestamp;
35 import java.sql.Types;
36 import java.util.Calendar;
37 import java.util.Date;
38 import java.util.Locale;
39 import java.util.Map;
40
41 import org.apache.openjpa.jdbc.kernel.JDBCStore;
42 import org.apache.openjpa.jdbc.meta.JavaSQLTypes;
43 import org.apache.openjpa.jdbc.schema.Column;
44 import org.apache.openjpa.meta.JavaTypes;
45
46 import serp.util.Numbers;
47
48 /**
49 * Base {@link Result} implementation wrapped around a result set.
50 * Column objects, column names, or column indexes (as <code>Number</code>
51 * instances) can be used to retrieve result set data.
52 *
53 * @author Abe White
54 */
55 public class ResultSetResult
56 extends AbstractResult {
57
58 private final Connection _conn;
59 private final Statement _stmnt;
60 private final ResultSet _rs;
61 private final DBDictionary _dict;
62 private boolean _closeConn = true;
63 private int _row = -1;
64 private int _size = -1;
65
66 // optional; used to deserialize blobs containing refs to persistent objs
67 private JDBCStore _store = null;
68
69 /**
70 * Constructor.
71 */
72 public ResultSetResult(Connection conn, Statement stmnt,
73 ResultSet rs, DBDictionary dict) {
74 if (stmnt == null)
75 try {
76 stmnt = rs.getStatement();
77 } catch (Throwable t) {
78 }
79
80 _conn = conn;
81 _stmnt = stmnt;
82 _rs = rs;
83 _dict = dict;
84 }
85
86 /**
87 * Constructor.
88 */
89 public ResultSetResult(Connection conn, Statement stmnt,
90 ResultSet rs, JDBCStore store) {
91 this(conn, stmnt, rs, store.getDBDictionary());
92 setStore(store);
93 }
94
95 /**
96 * Constructor.
97 */
98 public ResultSetResult(Connection conn,
99 ResultSet rs, DBDictionary dict) {
100 _conn = conn;
101 _stmnt = null;
102 _rs = rs;
103 _dict = dict;
104 }
105
106 /**
107 * JDBC 2 constructor. Relies on being able to retrieve the statement
108 * from the result set, and the connection from the statement.
109 */
110 public ResultSetResult(ResultSet rs, DBDictionary dict)
111 throws SQLException {
112 _stmnt = rs.getStatement();
113 _conn = _stmnt.getConnection();
114 _rs = rs;
115 _dict = dict;
116 }
117
118 /**
119 * JDBC 2 constructor. Relies on being able to retrieve the statement
120 * from the result set, and the connection from the statement.
121 */
122 public ResultSetResult(ResultSet rs, JDBCStore store)
123 throws SQLException {
124 this(rs, store.getDBDictionary());
125 setStore(store);
126 }
127
128 /**
129 * Return the statement that produced this result.
130 */
131 public Statement getStatement() {
132 return _stmnt;
133 }
134
135 /**
136 * Return the backing result set.
137 */
138 public ResultSet getResultSet() {
139 return _rs;
140 }
141
142 /**
143 * Return the dictionary in use.
144 */
145 public DBDictionary getDBDictionary() {
146 return _dict;
147 }
148
149 /**
150 * Optional store manager used to deserialize blobs containing
151 * references to persistent objects.
152 */
153 public JDBCStore getStore() {
154 return _store;
155 }
156
157 /**
158 * Optional store manager used to deserialize blobs containing
159 * references to persistent objects.
160 */
161 public void setStore(JDBCStore store) {
162 _store = store;
163 }
164
165 /**
166 * Whether to close the backing connection when this result is closed.
167 * Defaults to true.
168 */
169 public boolean getCloseConnection() {
170 return _closeConn;
171 }
172
173 /**
174 * Whether to close the backing connection when this result is closed.
175 * Defaults to true.
176 */
177 public void setCloseConnection(boolean closeConn) {
178 _closeConn = closeConn;
179 }
180
181 public void close() {
182 super.close();
183 try {
184 _rs.close();
185 } catch (SQLException se) {
186 }
187 if (_stmnt != null)
188 try {
189 _stmnt.close();
190 } catch (SQLException se) {
191 }
192 if (_closeConn)
193 try {
194 _conn.close();
195 } catch (SQLException se) {
196 }
197 }
198
199 public boolean supportsRandomAccess()
200 throws SQLException {
201 return _rs.getType() != ResultSet.TYPE_FORWARD_ONLY;
202 }
203
204 protected boolean absoluteInternal(int row)
205 throws SQLException {
206 if (row == ++_row)
207 return _rs.next();
208
209 // random access
210 _rs.absolute(row + 1);
211 if (_rs.getRow() == 0) {
212 _row = -1;
213 return false;
214 }
215 _row = row;
216 return true;
217 }
218
219 protected boolean nextInternal()
220 throws SQLException {
221 _row++;
222 return _rs.next();
223 }
224
225 public int size()
226 throws SQLException {
227 if (_size == -1) {
228 _rs.last();
229 _size = _rs.getRow();
230 if (_row == -1)
231 _rs.beforeFirst();
232 else
233 _rs.absolute(_row + 1);
234 }
235 return _size;
236 }
237
238 protected boolean containsInternal(Object obj, Joins joins)
239 throws SQLException {
240 return ((Number) translate(obj, joins)).intValue() > 0;
241 }
242
243 protected Array getArrayInternal(Object obj, Joins joins)
244 throws SQLException {
245 return _dict.getArray(_rs, ((Number) obj).intValue());
246 }
247
248 protected InputStream getAsciiStreamInternal(Object obj, Joins joins)
249 throws SQLException {
250 return _dict.getAsciiStream(_rs, ((Number) obj).intValue());
251 }
252
253 protected BigDecimal getBigDecimalInternal(Object obj, Joins joins)
254 throws SQLException {
255 return _dict.getBigDecimal(_rs, ((Number) obj).intValue());
256 }
257
258 protected Number getNumberInternal(Object obj, Joins joins)
259 throws SQLException {
260 return _dict.getNumber(_rs, ((Number) obj).intValue());
261 }
262
263 protected BigInteger getBigIntegerInternal(Object obj, Joins joins)
264 throws SQLException {
265 return _dict.getBigInteger(_rs, ((Number) obj).intValue());
266 }
267
268 protected InputStream getBinaryStreamInternal(Object obj, Joins joins)
269 throws SQLException {
270 return _dict.getBinaryStream(_rs, ((Number) obj).intValue());
271 }
272
273 protected Blob getBlobInternal(Object obj, Joins joins)
274 throws SQLException {
275 return _dict.getBlob(_rs, ((Number) obj).intValue());
276 }
277
278 protected boolean getBooleanInternal(Object obj, Joins joins)
279 throws SQLException {
280 return _dict.getBoolean(_rs, ((Number) obj).intValue());
281 }
282
283 protected byte getByteInternal(Object obj, Joins joins)
284 throws SQLException {
285 return _dict.getByte(_rs, ((Number) obj).intValue());
286 }
287
288 protected byte[] getBytesInternal(Object obj, Joins joins)
289 throws SQLException {
290 return _dict.getBytes(_rs, ((Number) obj).intValue());
291 }
292
293 protected Calendar getCalendarInternal(Object obj, Joins joins)
294 throws SQLException {
295 return _dict.getCalendar(_rs, ((Number) obj).intValue());
296 }
297
298 protected char getCharInternal(Object obj, Joins joins)
299 throws SQLException {
300 return _dict.getChar(_rs, ((Number) obj).intValue());
301 }
302
303 protected Reader getCharacterStreamInternal(Object obj, Joins joins)
304 throws SQLException {
305 return _dict.getCharacterStream(_rs, ((Number) obj).intValue());
306 }
307
308 protected Clob getClobInternal(Object obj, Joins joins)
309 throws SQLException {
310 return _dict.getClob(_rs, ((Number) obj).intValue());
311 }
312
313 protected Date getDateInternal(Object obj, Joins joins)
314 throws SQLException {
315 return _dict.getDate(_rs, ((Number) obj).intValue());
316 }
317
318 protected java.sql.Date getDateInternal(Object obj, Calendar cal,
319 Joins joins)
320 throws SQLException {
321 return _dict.getDate(_rs, ((Number) obj).intValue(), cal);
322 }
323
324 protected double getDoubleInternal(Object obj, Joins joins)
325 throws SQLException {
326 return _dict.getDouble(_rs, ((Number) obj).intValue());
327 }
328
329 protected float getFloatInternal(Object obj, Joins joins)
330 throws SQLException {
331 return _dict.getFloat(_rs, ((Number) obj).intValue());
332 }
333
334 protected int getIntInternal(Object obj, Joins joins)
335 throws SQLException {
336 return _dict.getInt(_rs, ((Number) obj).intValue());
337 }
338
339 protected Locale getLocaleInternal(Object obj, Joins joins)
340 throws SQLException {
341 return _dict.getLocale(_rs, ((Number) obj).intValue());
342 }
343
344 protected long getLongInternal(Object obj, Joins joins)
345 throws SQLException {
346 return _dict.getLong(_rs, ((Number) obj).intValue());
347 }
348
349 protected Object getStreamInternal(JDBCStore store, Object obj,
350 int metaTypeCode, Object arg, Joins joins) throws SQLException {
351 return getLOBStreamInternal(store, obj, joins);
352 }
353
354 protected Object getObjectInternal(Object obj, int metaTypeCode,
355 Object arg, Joins joins)
356 throws SQLException {
357 if (metaTypeCode == -1 && obj instanceof Column)
358 metaTypeCode = ((Column) obj).getJavaType();
359
360 Object val = null;
361 switch (metaTypeCode) {
362 case JavaTypes.BOOLEAN:
363 case JavaTypes.BOOLEAN_OBJ:
364 val = (getBooleanInternal(obj, joins)) ? Boolean.TRUE
365 : Boolean.FALSE;
366 break;
367 case JavaTypes.BYTE:
368 case JavaTypes.BYTE_OBJ:
369 val = new Byte(getByteInternal(obj, joins));
370 break;
371 case JavaTypes.CHAR:
372 case JavaTypes.CHAR_OBJ:
373 val = new Character(getCharInternal(obj, joins));
374 break;
375 case JavaTypes.DOUBLE:
376 case JavaTypes.DOUBLE_OBJ:
377 val = new Double(getDoubleInternal(obj, joins));
378 break;
379 case JavaTypes.FLOAT:
380 case JavaTypes.FLOAT_OBJ:
381 val = new Float(getFloatInternal(obj, joins));
382 break;
383 case JavaTypes.INT:
384 case JavaTypes.INT_OBJ:
385 val = Numbers.valueOf(getIntInternal(obj, joins));
386 break;
387 case JavaTypes.LONG:
388 case JavaTypes.LONG_OBJ:
389 val = Numbers.valueOf(getLongInternal(obj, joins));
390 break;
391 case JavaTypes.SHORT:
392 case JavaTypes.SHORT_OBJ:
393 val = new Short(getShortInternal(obj, joins));
394 break;
395 case JavaTypes.STRING:
396 return getStringInternal(obj, joins);
397 case JavaTypes.OBJECT:
398 return _dict
399 .getBlobObject(_rs, ((Number) obj).intValue(), _store);
400 case JavaTypes.DATE:
401 return getDateInternal(obj, joins);
402 case JavaTypes.CALENDAR:
403 return getCalendarInternal(obj, joins);
404 case JavaTypes.BIGDECIMAL:
405 return getBigDecimalInternal(obj, joins);
406 case JavaTypes.NUMBER:
407 return getNumberInternal(obj, joins);
408 case JavaTypes.BIGINTEGER:
409 return getBigIntegerInternal(obj, joins);
410 case JavaTypes.LOCALE:
411 return getLocaleInternal(obj, joins);
412 case JavaSQLTypes.SQL_ARRAY:
413 return getArrayInternal(obj, joins);
414 case JavaSQLTypes.ASCII_STREAM:
415 return getAsciiStreamInternal(obj, joins);
416 case JavaSQLTypes.BINARY_STREAM:
417 return getBinaryStreamInternal(obj, joins);
418 case JavaSQLTypes.BLOB:
419 return getBlobInternal(obj, joins);
420 case JavaSQLTypes.BYTES:
421 return getBytesInternal(obj, joins);
422 case JavaSQLTypes.CHAR_STREAM:
423 return getCharacterStreamInternal(obj, joins);
424 case JavaSQLTypes.CLOB:
425 return getClobInternal(obj, joins);
426 case JavaSQLTypes.SQL_DATE:
427 return getDateInternal(obj, (Calendar) arg, joins);
428 case JavaSQLTypes.SQL_OBJECT:
429 return getSQLObjectInternal(obj, (Map) arg, joins);
430 case JavaSQLTypes.REF:
431 return getRefInternal(obj, (Map) arg, joins);
432 case JavaSQLTypes.TIME:
433 return getTimeInternal(obj, (Calendar) arg, joins);
434 case JavaSQLTypes.TIMESTAMP:
435 return getTimestampInternal(obj, (Calendar) arg, joins);
436 default:
437 if (obj instanceof Column) {
438 Column col = (Column) obj;
439 if (col.getType() == Types.BLOB
440 || col.getType() == Types.VARBINARY) {
441 return _dict
442 .getBlobObject(_rs, col.getIndex(), _store);
443 }
444 }
445 return _dict.getObject(_rs, ((Number) obj).intValue(), null);
446 }
447 return (_rs.wasNull()) ? null : val;
448 }
449
450 protected Object getSQLObjectInternal(Object obj, Map map, Joins joins)
451 throws SQLException {
452 return _dict.getObject(_rs, ((Number) obj).intValue(), map);
453 }
454
455 protected Ref getRefInternal(Object obj, Map map, Joins joins)
456 throws SQLException {
457 return _dict.getRef(_rs, ((Number) obj).intValue(), map);
458 }
459
460 protected short getShortInternal(Object obj, Joins joins)
461 throws SQLException {
462 return _dict.getShort(_rs, ((Number) obj).intValue());
463 }
464
465 protected String getStringInternal(Object obj, Joins joins)
466 throws SQLException {
467 if (obj instanceof Column && ((Column) obj).getType() == Types.CLOB)
468 return _dict.getClobString(_rs, ((Column) obj).getIndex());
469 return _dict.getString(_rs, ((Number) obj).intValue());
470 }
471
472 protected Time getTimeInternal(Object obj, Calendar cal, Joins joins)
473 throws SQLException {
474 return _dict.getTime(_rs, ((Number) obj).intValue(), cal);
475 }
476
477 protected Timestamp getTimestampInternal(Object obj, Calendar cal,
478 Joins joins)
479 throws SQLException {
480 return _dict.getTimestamp(_rs, ((Number) obj).intValue(), cal);
481 }
482
483 public boolean wasNull()
484 throws SQLException {
485 return _rs.wasNull();
486 }
487
488 protected Object translate(Object obj, Joins joins)
489 throws SQLException {
490 if (obj instanceof Number)
491 return obj;
492 return Numbers.valueOf(findObject(obj, joins));
493 }
494
495 /**
496 * Return the 1-based result set index for the given column or id, or a
497 * non-positive number if the column is not contained in this result.
498 */
499 protected int findObject(Object obj, Joins joins)
500 throws SQLException {
501 try {
502 return getResultSet().findColumn(obj.toString());
503 } catch (SQLException se) {
504 return 0;
505 }
506 }
507
508 protected InputStream getLOBStreamInternal(JDBCStore store, Object obj,
509 Joins joins) throws SQLException {
510 return _dict.getLOBStream(store, _rs, ((Number) obj).intValue());
511 }
512 }