001: /**
002: * Objective Database Abstraction Layer (ODAL)
003: * Copyright (c) 2004, The ODAL Development Group
004: * All rights reserved.
005: * For definition of the ODAL Development Group please refer to LICENCE.txt file
006: *
007: * Distributable under LGPL license.
008: * See terms of license at gnu.org.
009: */package com.completex.objective.components.persistency.core.impl;
010:
011: import com.completex.objective.components.log.Log;
012: import com.completex.objective.components.persistency.AbstractPersistentObject;
013: import com.completex.objective.components.persistency.ColumnType;
014: import com.completex.objective.components.persistency.Compound;
015: import com.completex.objective.components.persistency.CompoundColumnFilter;
016: import com.completex.objective.components.persistency.CompoundPersistentObject;
017: import com.completex.objective.components.persistency.DelegatingPersistentObjectFactory;
018: import com.completex.objective.components.persistency.JavaToMetaType;
019: import com.completex.objective.components.persistency.LifeCycleController;
020: import com.completex.objective.components.persistency.MetaColumn;
021: import com.completex.objective.components.persistency.MetaTable;
022: import com.completex.objective.components.persistency.MultiRsHandler;
023: import com.completex.objective.components.persistency.OdalPersistencyException;
024: import com.completex.objective.components.persistency.PersistentObject;
025: import com.completex.objective.components.persistency.PersistentObjectFactory;
026: import com.completex.objective.components.persistency.Record;
027: import com.completex.objective.components.persistency.ResultableQuery;
028: import com.completex.objective.components.persistency.ResultableQueryManager;
029: import com.completex.objective.components.persistency.State;
030: import com.completex.objective.components.persistency.core.DatabasePolicy;
031: import com.completex.objective.components.persistency.core.ResultSetCtl;
032: import com.completex.objective.components.persistency.core.impl.query.QueryContext;
033: import com.completex.objective.components.persistency.transact.Transaction;
034: import com.completex.objective.components.persistency.type.CollectionFactory;
035: import com.completex.objective.components.persistency.type.MultipartCollection;
036: import com.completex.objective.components.persistency.type.TracingCollection;
037: import com.completex.objective.components.persistency.type.TypeHandler;
038:
039: import java.sql.ResultSet;
040: import java.sql.ResultSetMetaData;
041: import java.sql.SQLException;
042: import java.sql.Types;
043: import java.util.Collection;
044:
045: /**
046: * @author Gennady Krizhevsky
047: */
048: public class ResultSetCtlImpl implements ResultSetCtl {
049:
050: private BasicDatabasePersistencyImpl persistency;
051: private Log logger = Log.NULL_LOGGER;
052:
053: ResultSetCtlImpl() {
054: }
055:
056: public ResultSetCtlImpl(BasicDatabasePersistencyImpl persistency,
057: Log logger) {
058: this .persistency = persistency;
059: this .logger = logger;
060: }
061:
062: public void retrieve(Transaction transaction,
063: ResultableQueryManager query, ResultSet resultSet,
064: int resultSetIndex, MultipartCollection results,
065: LifeCycleController controller, QueryContext queryContext)
066: throws SQLException {
067: long pageSize = query.getPageSize();
068: Collection resultCollection = newMultipleResult(query,
069: resultSetIndex);
070: handleTracingSelectIn(resultCollection);
071: DelegatingPersistentObjectFactory persistentFactory = getDelegatingPoFactory(
072: query, resultSetIndex);
073: // dbrandt 2004-11-23. Optimization for databases that don't support native Limit
074: optimizeFetchSize(resultSet, query);
075: persistentFactory = lazyPersistentFactory(persistentFactory,
076: resultSet);
077: skip(resultSet, query);
078: int count = 0;
079: while (resultSet.next()) {
080: retrievePersistentObject(transaction, query,
081: persistentFactory, resultCollection, resultSet,
082: controller, queryContext);
083: count++;
084: if (count >= pageSize && pageSize > 0) {
085: break;
086: }
087: }
088: query.setRetrievedCount(count);
089: resultCollection = handleTracingSelectOut(resultCollection);
090: results.add(resultCollection);
091: }
092:
093: private void retrievePersistentObject(Transaction transaction,
094: ResultableQuery query,
095: DelegatingPersistentObjectFactory poFactory,
096: Collection result, ResultSet resultSet,
097: LifeCycleController controller, QueryContext queryContext)
098: throws OdalPersistencyException {
099: CompoundColumnFilter columnFilter = query
100: .getCompoundColumnFilter();
101: boolean useColumnNames = query.isUseColumnNames();
102: AbstractPersistentObject resultObject = retrievePersistentObject0(
103: poFactory, resultSet, columnFilter, useColumnNames);
104: if (resultObject != null && poFactory.delegateFactory() != null) {
105: resultObject = poFactory.delegateFactory()
106: .newPersistentInstance(transaction, persistency,
107: persistency.getQueryFactory(),
108: ((PersistentObject) resultObject));
109: }
110: Object valueObject = resultObject;
111: valueObject = resolveThroughQueryContext(query, valueObject,
112: resultObject, queryContext);
113: result.add(valueObject);
114: BasicDatabasePersistencyImpl.resolveNull(controller)
115: .afterSelect(resultObject);
116: }
117:
118: protected static Object resolveThroughQueryContext(
119: ResultableQuery query,
120: AbstractPersistentObject resultObject,
121: QueryContext queryContext) {
122: return resolveThroughQueryContext(query, resultObject,
123: resultObject, queryContext);
124: }
125:
126: protected static Object resolveThroughQueryContext(
127: ResultableQuery query, Object valueObject,
128: AbstractPersistentObject resultObject,
129: QueryContext queryContext) {
130: if (valueObject == resultObject && !query.isUnion()) {
131: valueObject = query.resolveThroughQueryContext(
132: resultObject, queryContext);
133: }
134: return valueObject;
135: }
136:
137: private AbstractPersistentObject retrievePersistentObject0(
138: PersistentObjectFactory persistentObjectFactory,
139: ResultSet resultSet, CompoundColumnFilter columnFilter,
140: boolean useColumnNames) throws OdalPersistencyException {
141: AbstractPersistentObject abstractPersistentObject;
142: LastIndex lastIndexHolder = new LastIndex();
143: if (persistentObjectFactory.compound()) {
144: abstractPersistentObject = retrieveCompound0(
145: persistentObjectFactory, resultSet, columnFilter,
146: useColumnNames, 0, lastIndexHolder);
147: } else {
148: BasicDatabasePersistencyImpl.RetrieveResult retrieveResult = retrievePersistentObject0(
149: persistentObjectFactory, resultSet, columnFilter,
150: useColumnNames, 0, lastIndexHolder);
151:
152: abstractPersistentObject = retrieveResult
153: .getPersistentObject();
154: }
155: return abstractPersistentObject;
156: }
157:
158: private AbstractPersistentObject retrieveCompound0(
159: PersistentObjectFactory persistentObjectFactory,
160: ResultSet resultSet, CompoundColumnFilter columnFilter,
161: boolean useColumnNames, int level, LastIndex lastIndexHolder)
162: throws OdalPersistencyException {
163: level++;
164: AbstractPersistentObject abstractPersistentObject;
165: Compound compound = persistentObjectFactory
166: .newPersistentInstance();
167: boolean isCompoundPo = compound instanceof CompoundPersistentObject;
168: PersistentObject[] resultObjects = compound.compoundEntries();
169: for (int k = 0; k < resultObjects.length; k++) {
170: PersistentObject entry = resultObjects[k];
171: if (isCompoundPo && entry.compound() && level < 2) {
172: // System.out.println("isCompoundPo && entry.compound() && level < 2");
173: retrieveCompound0(entry, resultSet, columnFilter,
174: useColumnNames, level, lastIndexHolder);
175: } else {
176: // System.out.println("NOT (isCompoundPo && entry.compound() && level < 2)");
177: BasicDatabasePersistencyImpl.RetrieveResult retrieveResult = retrievePersistentObject0(
178: entry, resultSet, columnFilter, useColumnNames,
179: k, lastIndexHolder);
180: resultObjects[k] = retrieveResult.getPersistentObject();
181: if (compound.selfReferencing() && k == 0) {
182: compound = resultObjects[k];
183: } else {
184: compound.compoundEntry(k, resultObjects[k]);
185: }
186: lastIndexHolder.setLastIndex(retrieveResult.getIndex());
187: }
188: }
189: abstractPersistentObject = (AbstractPersistentObject) compound;
190: return abstractPersistentObject;
191: }
192:
193: private BasicDatabasePersistencyImpl.RetrieveResult retrievePersistentObject0(
194: PersistentObjectFactory persistentObjectFactory,
195: ResultSet resultSet, CompoundColumnFilter columnFilter,
196: boolean useColumnNames, int entryIndex,
197: LastIndex lastIndexHolder) throws OdalPersistencyException {
198: PersistentObject persistentObject = (PersistentObject) persistentObjectFactory
199: .newPersistentInstance();
200: Record record = persistentObject.record();
201: record.setState(State.NEW_INITIALIZING);
202: int i = 0;
203: int jdbcColIndex = lastIndexHolder.getLastIndex();
204: for (; i < persistentObject.record().getColumnCount(); i++) {
205: String columnAlias = record.getColumnAlias(i);
206: String columnName = record.getColumnName(i);
207: if (columnFilter.accept(entryIndex, i, columnName,
208: columnAlias)) {
209: jdbcColIndex += 1;
210: TypeHandler typeHandler = persistency
211: .getTypeHandler(record.getColumn(i).getType());
212: Object value = null;
213: try {
214: if (useColumnNames) {
215: value = typeHandler.handleRead(resultSet,
216: columnName, databasePolicy());
217: } else {
218: value = typeHandler.handleRead(resultSet,
219: jdbcColIndex, databasePolicy());
220: }
221: record.setObject(i, value);
222: } catch (ClassCastException e) {
223: throw new OdalPersistencyException(
224: "(ClassCastException) Cannot read "
225: + e.getMessage()
226: + " type value as "
227: + record.getColumn(i).getType()
228: + " for column "
229: + record.getColumn(i)
230: .getColumnName()
231: + " for value " + value
232: + "; record " + record);
233: } catch (RuntimeException e) {
234: if (record != null) {
235: getLogger().error(
236: "Error while retrieving from "
237: + "table "
238: + record.getTableName()
239: + " column [" + columnName
240: + "]" + " jdbcColIndex = "
241: + jdbcColIndex
242: + " useColumnNames = "
243: + useColumnNames);
244: }
245: throw e;
246: } catch (OdalPersistencyException e) {
247: if (record != null) {
248: getLogger().error(
249: "Error while retrieving from "
250: + "table "
251: + record.getTableName()
252: + " column [" + columnName
253: + "]");
254: }
255: throw e;
256: } catch (SQLException e) {
257: if (record != null) {
258: getLogger().error(
259: "Error while retrieving from "
260: + "table "
261: + record.getTableName()
262: + " column [" + columnName
263: + "]");
264: }
265: throw new OdalPersistencyException(e);
266: }
267: record.setFieldDirty(i, false);
268: }
269: }
270: persistentObject.toBeanFields();
271: record.setState(State.NEW_INITIALIZED);
272: return new BasicDatabasePersistencyImpl.RetrieveResult(
273: jdbcColIndex, persistentObject);
274: }
275:
276: private DatabasePolicy databasePolicy() {
277: return persistency.getDatabasePolicy();
278: }
279:
280: static void handleTracingSelectIn(Collection resultCollection) {
281: if (resultCollection instanceof TracingCollection) {
282: ((TracingCollection) resultCollection).unsetTrace();
283: }
284: }
285:
286: static Collection handleTracingSelectOut(Collection collection) {
287: if (collection != null
288: && collection instanceof TracingCollection) {
289: ((TracingCollection) collection).setTrace();
290: }
291: return collection;
292: }
293:
294: /**
295: * Advance the cursor to the first required row of the <tt>ResultSet</tt>
296: */
297: private void skip(final ResultSet rs, final ResultableQuery query)
298: throws SQLException {
299: if (query.isDisconnectedPageQuery()) {
300: boolean hasMore = true;
301: for (int i = 0; i < query.getOffset(); i++) {
302: if (hasMore) {
303: hasMore = rs.next();
304: } else {
305: break;
306: }
307: }
308: }
309: }
310:
311: private DelegatingPersistentObjectFactory lazyPersistentFactory(
312: DelegatingPersistentObjectFactory persistentObjectFactory,
313: ResultSet resultSet) throws SQLException {
314: if (persistentObjectFactory == null) {
315: ResultSetMetaData metaData = resultSet.getMetaData();
316: int size = metaData.getColumnCount();
317: MetaTable table = new MetaTable("query", "query", size, 0);
318:
319: JavaToMetaType javaToMetaType = MetaTable
320: .getDefaultJavaToMetaType();
321: for (int i = 0; i < size; i++) {
322: int jdbcIndex;
323: int precision = 0;
324: int scale = 0;
325: int jdbcType = 0;
326: String columnName = null;
327: MetaColumn column;
328: ColumnType columnType = null;
329: jdbcIndex = i + 1;
330: try {
331: columnName = metaData.getColumnName(jdbcIndex);
332: jdbcType = metaData.getColumnType(jdbcIndex);
333: try {
334: if (!(jdbcType == Types.CLOB
335: || jdbcType == Types.BLOB || jdbcType == Types.OTHER)) {
336: precision = metaData
337: .getPrecision(jdbcIndex);
338: scale = metaData.getScale(jdbcIndex);
339: }
340: } catch (NumberFormatException e) {
341: // Suppress number format exception - compensate deficiency of some drivers
342: }
343: try {
344: boolean required = metaData
345: .isNullable(jdbcIndex) == ResultSetMetaData.columnNoNulls;
346: columnType = javaToMetaType.dataType(jdbcType,
347: scale, precision, required);
348: } catch (Exception e) {
349: getLogger().warn(
350: "Cannot get columnType by jdbcType "
351: + jdbcType);
352: }
353: column = new MetaColumn(metaData
354: .getColumnName(jdbcIndex), table);
355: column.setColumnIndex(i);
356: column.setJdbcType(jdbcType);
357: column.setType(columnType);
358: } catch (RuntimeException e) {
359: getLogger().error(
360: "Error while getting meta data for column "
361: + columnName
362: + "; jdbcType = "
363: + javaToMetaType
364: .jdbcTypeToString(jdbcType)
365: + "; jdbcIndex = " + jdbcIndex);
366: throw e;
367: } catch (SQLException e) {
368: getLogger().error(
369: "Error while getting meta data for column "
370: + columnName
371: + "; jdbcType = "
372: + javaToMetaType
373: .jdbcTypeToString(jdbcType)
374: + "; jdbcIndex = " + jdbcIndex);
375: throw e;
376: }
377:
378: if (!table.containsColumn(columnName)) {
379: table.addColumn(column);
380: } else {
381: throw new SQLException(
382: "Duplicate column name ["
383: + columnName
384: + "] as result of query execution. Solution - use aliases");
385: }
386: }
387: persistentObjectFactory = new PersistentObject(new Record(
388: table));
389: }
390: return persistentObjectFactory;
391: }
392:
393: private void optimizeFetchSize(ResultSet resultSet,
394: ResultableQuery query) {
395: try {
396: int fetchSize = resultSet.getFetchSize();
397: int limitMax = query.getLimitMax();
398: if (fetchSize > limitMax && limitMax > 0) {
399: resultSet.setFetchSize(limitMax);
400: } else if (query.isDisconnectedPageQuery()
401: && resultSet.getFetchSize() < query.getPageSize()) {
402: resultSet.setFetchSize((int) query.getPageSize());
403: }
404: } catch (SQLException e) {
405: // Ignore if error happened on optimization
406: BasicDatabasePersistencyImpl.warn(logger,
407: "setFetchSize throws SQLException: "
408: + e.getMessage());
409: }
410: }
411:
412: private Collection newMultipleResult(ResultableQuery query,
413: int index) {
414: try {
415: CollectionFactory multipleResultFactory = query
416: .getMultipleResultFactory();
417: setIndex(index, multipleResultFactory);
418: return multipleResultFactory.newCollection();
419: } catch (Exception e) {
420: new RuntimeException(
421: "Cannot get new instance of multiple result", e);
422: }
423: return null;
424: }
425:
426: private DelegatingPersistentObjectFactory getDelegatingPoFactory(
427: ResultableQuery query, int index) {
428: DelegatingPersistentObjectFactory persistentFactory = (DelegatingPersistentObjectFactory) query
429: .getSingularResultFactory();
430: setIndex(index, persistentFactory);
431: return (DelegatingPersistentObjectFactory) query
432: .getSingularResultFactory();
433: }
434:
435: private void setIndex(int index, Object value) {
436: if (index > 0 && value instanceof MultiRsHandler) {
437: ((MultiRsHandler) value).setIndex(index);
438: }
439: }
440:
441: public BasicDatabasePersistencyImpl getPersistency() {
442: return persistency;
443: }
444:
445: void setPersistency(BasicDatabasePersistencyImpl persistency) {
446: this .persistency = persistency;
447: }
448:
449: public Log getLogger() {
450: return logger;
451: }
452:
453: public void setLogger(Log logger) {
454: if (logger != null) {
455: this.logger = logger;
456: }
457: }
458:
459: }
|