001: /*
002: * Copyright (c) 1998 - 2005 Versant Corporation
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * Versant Corporation - initial API and implementation
010: */
011: package com.versant.core.jdbc.query;
012:
013: import com.versant.core.jdo.QueryDetails;
014: import com.versant.core.metadata.*;
015: import com.versant.core.jdo.query.GroupingNode;
016: import com.versant.core.server.CompiledQuery;
017: import com.versant.core.jdbc.FgDs;
018: import com.versant.core.jdbc.JdbcQueryResult;
019: import com.versant.core.jdbc.ProjectionQueryDecoder;
020: import com.versant.core.jdbc.metadata.JdbcColumn;
021: import com.versant.core.jdbc.metadata.JdbcField;
022: import com.versant.core.jdbc.metadata.JdbcRefField;
023: import com.versant.core.jdbc.metadata.JdbcClass;
024: import com.versant.core.jdbc.sql.SqlDriver;
025: import com.versant.core.util.CharBuf;
026: import com.versant.core.common.Debug;
027:
028: import java.sql.*;
029: import java.util.HashMap;
030: import java.util.Map;
031: import java.util.Set;
032: import java.util.List;
033:
034: import com.versant.core.common.BindingSupportImpl;
035:
036: /**
037: * This is a QueryImp compiled by a JdbcDataStore and ready to run.
038: * It contains the SQL and enough information to convert the ResultSet
039: * into State's. There is not much logic to avoid referencing server side
040: * classes as a client application may serialize a query containing one of
041: * these and re-associate it with a PM later.
042: */
043: public class JdbcCompiledQuery implements CompiledQuery {
044:
045: public static final int PARAM_IN = 0;//default
046: public static final int PARAM_OUT = 1;
047: public static final int PARAM_OUT_CURSOR = 2;
048:
049: private int id;
050: private final QueryDetails qp;
051:
052: private Map parCollSqlStrucMap = new HashMap();
053:
054: /**
055: * The class index of the candidate class of the query.
056: */
057: private int classIndex;
058: /**
059: * The cls index's of all the filter classes.
060: */
061: private ClassMetaData[] filterCmds;
062: /**
063: * Bitmapped array of the class indexes that will cause the results
064: * of this query to be evicted when their instances are modified. Each
065: * class index has one bit in this array.
066: */
067: private int[] evictionClassBits;
068: private int[] evictionClassIndexes;
069: /**
070: * If the results of the query is cacheble.
071: */
072: private boolean cacheble;
073: /**
074: * Must subclasses be included in the result?
075: */
076: private boolean includeSubclasses;
077: /**
078: * The root fetch group for the query. This will be from cmd. This
079: * will be the default fetch group unless the user specified a
080: * different group.
081: */
082: private int fetchGroupIndex;
083: /**
084: * Must this query return results suitable for random access?
085: */
086: private boolean randomAccess;
087: /**
088: * The max amount of rows to return for this query.
089: */
090: private int maxRows;
091: /**
092: * This is the amount of data that will be prefetched per each round trip
093: * to the server.
094: */
095: private int queryResultBatchSize;
096:
097: private SqlStruct sqlStruct;
098:
099: private boolean parColFetchEnabled;
100: /**
101: * This is the group by exp for the aggregate expression.
102: * This may only be non-null if result was specified and it contains aggregates.
103: */
104: private GroupingNode groupByNode;
105: protected int unique;
106: private int selectColumnCount;
107: private boolean copyResultsForCache;
108: public FgDs fgDs;
109: private boolean sqlQuery;
110: private boolean storeProc;
111: private boolean directSql;
112: private MappingInfo mappingInfo;
113: private ClassMetaData cmd;
114: private int[] sqlTypes;
115: /**
116: * If this is a 'in', 'out' or a 'inout' type
117: */
118: private int[] paramDirection;
119: private int outParamCount;
120:
121: private boolean crossJoinAllowed;
122: private ProjectionQueryDecoder projectionDecoder;
123:
124: public JdbcCompiledQuery(ClassMetaData cmd, QueryDetails queryParams) {
125: this .cmd = cmd;
126: if (cmd != null) {
127: this .classIndex = cmd.index;
128: this .includeSubclasses = queryParams.includeSubClasses()
129: && cmd.isInHeirachy();
130: } else {
131: this .classIndex = -1;
132: }
133: if (queryParams.getLanguage() == QueryDetails.LANGUAGE_SQL) {
134: sqlQuery = true;
135: if (queryParams.getFilter() == null
136: || queryParams.getFilter().trim().length() == 0) {
137: throw BindingSupportImpl
138: .getInstance()
139: .invalidOperation(
140: "Must supply a valid filter for a 'SqlQeury'");
141: }
142: if (queryParams.getFilter().toUpperCase().startsWith(
143: "SELECT")) {
144: storeProc = false;
145: directSql = true;
146: } else {
147: storeProc = true;
148: directSql = false;
149: }
150: }
151:
152: this .fetchGroupIndex = queryParams.getFetchGroupIndex();
153: this .randomAccess = queryParams.isRandomAccess();
154: this .maxRows = queryParams.getMaxResultCount();
155: this .queryResultBatchSize = queryParams.getResultBatchSize();
156: if (Debug.DEBUG) {
157: if (queryResultBatchSize <= 0) {
158: throw BindingSupportImpl.getInstance().internal(
159: "The queryDetails.resultBatchSize is not set");
160: }
161: }
162: this .qp = queryParams;
163:
164: if (!sqlQuery) {
165: this .sqlStruct = new SqlStruct();
166: this .sqlStruct.jdoqlFilter = queryParams.getFilter();
167: if (sqlStruct.jdoqlFilter == null
168: || sqlStruct.jdoqlFilter.length() == 0) {
169: sqlStruct.jdoqlFilter = "NO FILTER";
170: }
171:
172: parColFetchEnabled = QueryDetails
173: .enableParallelCollectionFetch(qp,
174: cmd.fetchGroups[qp.getFetchGroupIndex()]);
175: } else {
176: unique = QueryDetails.FALSE;
177: //process the param types.
178: String[] types = queryParams.getParamTypes();
179: int count = queryParams.getParamCount();
180: sqlTypes = new int[count];
181: paramDirection = new int[count];
182: for (int i = 0; i < count; i++) {
183: String type = types[i].toUpperCase();
184: if (type.startsWith("OUT.")) {
185: outParamCount++;
186: if (type.equals("OUT.CURSOR")) {
187: //the rs
188: paramDirection[i] = PARAM_OUT_CURSOR;
189: } else {
190: //change unique to true as we will create an Object[]
191: unique = QueryDetails.TRUE;
192: //single value
193: paramDirection[i] = PARAM_OUT;
194: sqlTypes[i] = getTypeInt(type.substring(type
195: .indexOf(".") + 1));
196: }
197: } else {
198: sqlTypes[i] = getTypeInt(types[i]);
199: }
200: }
201: }
202: }
203:
204: public ClassMetaData getCmd() {
205: return cmd;
206: }
207:
208: public void setCmd(ClassMetaData cmd) {
209: this .cmd = cmd;
210: }
211:
212: public int getOutParamCount() {
213: return outParamCount;
214: }
215:
216: public int[] getSqlTypes() {
217: return sqlTypes;
218: }
219:
220: public int[] getParamDirection() {
221: return paramDirection;
222: }
223:
224: private int getTypeInt(String val) {
225: try {
226: return Types.class.getDeclaredField(val.toUpperCase())
227: .getInt(null);
228: } catch (Exception e) {
229: throw BindingSupportImpl.getInstance().internal(
230: "Param type '" + val + "' is not a valid "
231: + Types.class.getName() + " type.");
232: }
233: }
234:
235: public boolean isSqlQuery() {
236: return sqlQuery;
237: }
238:
239: public int getSelectColumnCount() {
240: return selectColumnCount;
241: }
242:
243: public void setSelectColumnCount(int selectColumnCount) {
244: this .selectColumnCount = selectColumnCount;
245: }
246:
247: /**
248: * This may only be called once the resultnode and the groupBy node has been processed and set.
249: */
250: public void process() {
251: if (qp.getUnique() == QueryDetails.TRUE) {
252: unique = QueryDetails.TRUE;
253: } else if (qp.getUnique() == QueryDetails.FALSE) {
254: unique = QueryDetails.FALSE;
255: } else {
256: if (projectionDecoder != null) {
257: if (projectionDecoder.containsAggregate()) {
258: if (groupByNode == null) {
259: if (!projectionDecoder.aggregateOnly()) {
260: unique = QueryDetails.FALSE;
261: } else {
262: unique = QueryDetails.TRUE;
263: }
264: } else {
265: if (projectionDecoder.aggregateOnly()) {
266: unique = QueryDetails.TRUE;
267: } else {
268: unique = QueryDetails.FALSE;
269: }
270: }
271: } else {
272: unique = QueryDetails.FALSE;
273: if (groupByNode != null) {
274: throw BindingSupportImpl
275: .getInstance()
276: .invalidOperation(
277: "The query contains a 'Group By' "
278: + "expression but no aggregate's.");
279: }
280: }
281: } else {
282: unique = QueryDetails.FALSE;
283: }
284: }
285:
286: copyResultsForCache = (projectionDecoder != null
287: && !projectionDecoder.isContainsThisOnly() && projectionDecoder
288: .getRefIndexArray().length > 0);
289:
290: sqlStruct.setAggregate(projectionDecoder != null
291: && projectionDecoder.containsAggregate());
292:
293: crossJoinAllowed = (projectionDecoder == null)
294: && parColFetchEnabled && !sqlQuery
295: && !qp.isRandomAccess()
296: && (qp.getMaxResultCount() <= 0);
297: }
298:
299: /**
300: * If this is a query with a single/unique result.
301: */
302: public boolean isUnique() {
303: if (unique == QueryDetails.NOT_SET) {
304: throw BindingSupportImpl.getInstance().internal(
305: "The 'unique' value has not been processed.");
306: }
307: if (unique == QueryDetails.TRUE)
308: return true;
309: return false;
310: }
311:
312: public boolean isProjectionQuery() {
313: return (projectionDecoder != null);
314: }
315:
316: public int getFirstThisIndex() {
317: if (projectionDecoder == null)
318: return -1;
319: return projectionDecoder.getFirstThisIndex();
320: }
321:
322: /**
323: * If this is a result/projection that only contains 'this' and no other
324: * fields in the projection.
325: */
326: public boolean isContainsThisOnly() {
327: return projectionDecoder.isContainsThisOnly();
328: }
329:
330: /**
331: * If this is a result/projection that only contains 'this' and no other
332: * fields in the projection.
333: */
334: public boolean isContainsThis() {
335: if (projectionDecoder == null)
336: return false;
337: return projectionDecoder.containsThis();
338: }
339:
340: /**
341: * If the results of the query should be copied for caching.
342: * This should only happen for non-default type projection queries that
343: * contains references.
344: * <p/>
345: * If this is a projection that only specifies 'this' then this should
346: * also return false.
347: */
348: public boolean isCopyResultsForCache() {
349: return copyResultsForCache;
350: }
351:
352: /**
353: * If this query returns default results.
354: */
355: public boolean isDefaultResult() {
356: if (sqlQuery) {
357: return cmd != null;
358: }
359: if (projectionDecoder == null)
360: return true;
361: return projectionDecoder.isContainsThisOnly();
362: }
363:
364: /**
365: * Array containing the index pos of ref fields of the projection.
366: * Return null if no ref fields in projection.
367: */
368: public int[] getRefIndexArray() {
369: if (projectionDecoder == null) {
370: throw BindingSupportImpl.getInstance().internal(
371: "This may only be called on 'projection queries'");
372: }
373: return projectionDecoder.getRefIndexArray();
374: }
375:
376: public int[] getResultTypeCodes() {
377: return projectionDecoder == null ? null : projectionDecoder
378: .getTypeCodes();
379: }
380:
381: public void setGroupingNode(GroupingNode groupingNode) {
382: groupByNode = groupingNode;
383: }
384:
385: public boolean isParColFetchEnabled() {
386: return parColFetchEnabled;
387: }
388:
389: public synchronized SqlStruct get(JdbcQueryResult.ColFHKey key) {
390: return (SqlStruct) parCollSqlStrucMap.get(key);
391: }
392:
393: public synchronized void add(JdbcQueryResult.ColFHKey key,
394: SqlStruct val) {
395: parCollSqlStrucMap.put(key, val);
396: }
397:
398: public SqlStruct getSqlStruct() {
399: return sqlStruct;
400: }
401:
402: public int getMaxRows() {
403: return maxRows;
404: }
405:
406: public int getQueryResultBatchSize() {
407: return queryResultBatchSize;
408: }
409:
410: public ClassMetaData[] getQueryClasses() {
411: return filterCmds;
412: }
413:
414: public void setFilterClsIndexs(ClassMetaData[] filterClsIndexs) {
415: this .filterCmds = filterClsIndexs;
416: }
417:
418: public int[] getEvictionClassBits() {
419: return evictionClassBits;
420: }
421:
422: public int[] getClassIndexes() {
423: return evictionClassIndexes;
424: }
425:
426: public QueryDetails getQueryDetails() {
427: return qp;
428: }
429:
430: public void setEvictionClassBits(int[] evictionClassBits) {
431: this .evictionClassBits = evictionClassBits;
432: }
433:
434: public void setEvictionClassIndexes(int[] evictionClassIndexes) {
435: this .evictionClassIndexes = evictionClassIndexes;
436: }
437:
438: public boolean isCacheble() {
439: return cacheble;
440: }
441:
442: public void setCacheable(boolean cacheble) {
443: this .cacheble = cacheble;
444: }
445:
446: public String getFirstTableOrAlias() {
447: return sqlStruct.getFirstTableOrAlias();
448: }
449:
450: public void setFirstTableOrAlias(String firstTableOrAlias) {
451: sqlStruct.setFirstTableOrAlias(firstTableOrAlias);
452: }
453:
454: public boolean isDistinct() {
455: return sqlStruct.isDistinct();
456: }
457:
458: /**
459: * Get the SQL query text.
460: */
461: public String getSql() {
462: return sqlStruct.getSql();
463: }
464:
465: public int getClassIndex() {
466: return classIndex;
467: }
468:
469: public boolean isIncludeSubclasses() {
470: return includeSubclasses;
471: }
472:
473: public int getFetchGroupIndex() {
474: return fetchGroupIndex;
475: }
476:
477: public FetchGroup getFetchGroup() {
478: return cmd.fetchGroups[fetchGroupIndex];
479: }
480:
481: public boolean isRandomAccess() {
482: return randomAccess;
483: }
484:
485: public CharBuf getSqlbuf() {
486: return sqlStruct.getSqlbuf();
487: }
488:
489: public SqlStruct.Param getParamList() {
490: return sqlStruct.getParamList();
491: }
492:
493: /**
494: * Update all our Param's for the null/not null state of their parameters
495: * and for 'select for update' or not. This may change the SQL query
496: * string.
497: */
498: public void updateSql(SqlDriver driver, Object[] params,
499: boolean forUpdate, boolean forCount) {
500: sqlStruct.updateSql(driver, params, forUpdate, forCount);
501: }
502:
503: /**
504: * Set all the parameters for this query on ps. This is a NOP if params
505: * is null.
506: */
507: public void setParamsOnPS(ModelMetaData jmd, SqlDriver driver,
508: PreparedStatement ps, Object[] params, String sql)
509: throws SQLException {
510: sqlStruct.setParamsOnPS(jmd, driver, ps, params, sql);
511: }
512:
513: public MappingInfo getMappingInfo(ResultSet rs) throws SQLException {
514: if (mappingInfo == null) {
515: mappingInfo = createMappingInfo(rs.getMetaData(), cmd);
516: }
517: return mappingInfo;
518: }
519:
520: public boolean equals(Object obj) {
521: if (obj == this )
522: return true;
523: if (obj instanceof JdbcCompiledQuery) {
524: return qp.equals(((JdbcCompiledQuery) obj).qp);
525: }
526: return false;
527: }
528:
529: public int hashCode() {
530: return qp.hashCode();
531: }
532:
533: public static MappingInfo createMappingInfo(ResultSetMetaData rsmd,
534: ClassMetaData cmd) throws SQLException {
535: final int count = rsmd.getColumnCount();
536: MappingInfo mi = new MappingInfo();
537: mi.colCount = count;
538: if (cmd == null)
539: return mi;
540: mi.cmd = cmd;
541:
542: JdbcColumn[] pkCols = ((JdbcClass) cmd.storeClass).table.pk;
543: JdbcColumn discr = ((JdbcClass) cmd.storeClass).classIdCol;
544:
545: /**
546: * Look for discriminator
547: */
548: if (discr != null) {
549: //get the discriminator col
550: for (int i = 1; i <= count;) {
551: if (discr.name.toUpperCase().equals(
552: rsmd.getColumnName(i).toUpperCase())) {
553: mi.discrIndex = i++;
554: break;
555: }
556: i++;
557: }
558: }
559:
560: final boolean appId = cmd.pkFields != null;
561: Set fieldList = new java.util.HashSet();
562: JdbcField[] jdbcFields = mi.fields = new JdbcField[count];
563: int[] pkindexes = null;
564: if (appId) {
565: pkindexes = new int[pkCols.length];
566: }
567:
568: for (int i = 1; i <= count;) {
569: String colName = rsmd.getColumnName(i).toUpperCase();
570:
571: //skip the already found discriminator col
572: if (i == mi.discrIndex) {
573: i++;
574: continue;
575: }
576:
577: // if (mi.pkIndex == -1 && pkCols[0].name.toUpperCase().equals(colName)) {
578: // //assume the same ordering
579: // boolean ok = true;
580: // for (int j = 1; j < pkCols.length; j++) {
581: // if (!pkCols[j].name.toUpperCase().equals(rsmd.getColumnName(i + j).toUpperCase())) {
582: // ok = false;
583: // break;
584: // }
585: // }
586: //
587: // if (ok) {
588: // mi.pkIndex = i;
589: // i += pkCols.length;
590: // continue;
591: // }
592: // }
593:
594: if (mi.dsPkIndex == -1 && !appId
595: && pkCols[0].name.toUpperCase().equals(colName)) {
596: //assume the same ordering
597: mi.dsPkIndex = i;
598: i++;
599: continue;
600: }
601:
602: JdbcField field = getSubClassField(colName, cmd,
603: mi.discrIndex != 0);
604: // if (field == null) {
605: // refFieldToJdbcField.clear();
606: // field = getRefField(colName, cmd, true, refFieldToJdbcField);
607: // System.out.println("refField = " + refFieldToJdbcField.fromField);
608: // if (field != null) {
609: // System.out.println("found ref field: " + field);
610: // }
611: // }
612:
613: if (field != null) {
614: if (fieldList.contains(field)) {
615: //ignore
616: System.out
617: .println("Ignoring column '"
618: + colName
619: + "' at index '"
620: + i
621: + "' because this column has already been mapped to '"
622: + field.fmd.classMetaData.qname
623: + "." + field.fmd.name + "'");
624: i++;
625: continue;
626: } else {
627: if (field.mainTableCols.length == 1) {
628: fieldList.add(field);
629: jdbcFields[i - 1] = field;
630: i++;
631: continue;
632: } else {
633: /**
634: * This field is mapped to more that one column in the db
635: * so we must check to see that the next n cols will also resolve to this
636: * field. If not the skip the current col
637: */
638: int endIndex = i + field.mainTableCols.length;
639: boolean ok = false;
640: for (int j = i + 1; j < endIndex; j++) {
641: if (((JdbcClass) cmd.storeClass)
642: .getColNamesToJdbcField().get(
643: colName) != field) {
644: //not the current field so skip this col and continue
645: break;
646: }
647: }
648:
649: if (ok) {
650: /**
651: * all the cols matched for the current field so accept it. Update the index
652: * to the endindex.
653: */
654: fieldList.add(field);
655: jdbcFields[i - 1] = field;
656: i = endIndex;
657: } else {
658: /**
659: * Skip the current col
660: */
661: i++;
662: }
663: continue;
664: }
665: }
666: } else {
667: i++;
668: //not in this class
669: }
670: }
671:
672: if (cmd.pkFields != null) {
673: //flag to check that all pk fields are found in the jdbcfields array
674: boolean keyFound = false;
675: for (int i = 0; i < cmd.pkFields.length; i++) {
676: FieldMetaData pkField = cmd.pkFields[i];
677: keyFound = false;
678: for (int j = 0; j < jdbcFields.length; j++) {
679: JdbcField jdbcField = jdbcFields[j];
680: if (pkField.storeField == jdbcField) {
681: keyFound = true;
682: pkindexes[i] = j;
683: break;
684: }
685: }
686: //if any of the keys is not found then break.
687: if (!keyFound) {
688: break;
689: }
690: }
691:
692: if (Debug.DEBUG) {
693: if (keyFound) {
694: for (int i = 0; i < cmd.pkFields.length; i++) {
695: FieldMetaData pkField = cmd.pkFields[i];
696: if (pkField.storeField != jdbcFields[pkindexes[i]]) {
697: throw BindingSupportImpl.getInstance()
698: .internal("pk field mismatch");
699: }
700: }
701: }
702: }
703: if (keyFound) {
704: mi.pkIndexInFieldsArray = pkindexes;
705: }
706: }
707: // if (mi.pkIndex == -1) {
708: // throw BindingSupportImpl.getInstance().invalidOperation("No columns in the result set " +
709: // "could be mapped to the pk fields");
710: // }
711: return mi;
712: }
713:
714: /**
715: * Find a JdbcField by column name from reference fields.
716: */
717: private static JdbcField getRefField(String colName,
718: ClassMetaData cmd, boolean includeSubs,
719: RefFieldMapping refFieldMapping) {
720: if (!includeSubs) {
721: return getRefFieldImp(cmd, colName, true);
722: } else {
723: List cmds = cmd.getHeirarchyList();
724: for (int i = 0; i < cmds.size(); i++) {
725: JdbcField jdbcField = getRefFieldImp(
726: (ClassMetaData) cmds.get(i), colName, true);
727: if (jdbcField != null)
728: return jdbcField;
729: }
730: }
731: return null;
732: }
733:
734: /**
735: * Find a JdbcField by column name from reference fields.
736: */
737: private static JdbcField getRefFieldImp(ClassMetaData cmd,
738: String colName, boolean includeSubs) {
739: FieldMetaData[] fields = cmd.stateFields;
740: for (int i = 0; i < fields.length; i++) {
741: FieldMetaData fmd = fields[i];
742: if (fmd.category == MDStatics.CATEGORY_REF) {
743: JdbcField jdbcField = getSubClassField(colName,
744: fmd.typeMetaData, includeSubs);
745: if (jdbcField != null)
746: return jdbcField;
747: }
748: }
749: return null;
750: }
751:
752: /**
753: * Do a breadth first recursive traversal of subclasses to find a JdbcField with a column name.
754: */
755: private static JdbcField getSubClassField(String colName,
756: ClassMetaData cmd, boolean checkSubClasses) {
757: if (!checkSubClasses) {
758: return (JdbcField) ((JdbcClass) cmd.storeClass)
759: .getColNamesToJdbcField().get(colName);
760: } else {
761: List subsList = cmd.getHeirarchyList();
762: for (int i = 0; i < subsList.size(); i++) {
763: ClassMetaData icmd = (ClassMetaData) subsList.get(i);
764: JdbcField field = (JdbcField) ((JdbcClass) icmd.storeClass)
765: .getColNamesToJdbcField().get(colName);
766: if (field != null)
767: return field;
768: }
769: }
770: return null;
771: }
772:
773: public boolean isStoredProc() {
774: return storeProc;
775: }
776:
777: public boolean isDirectSql() {
778: return directSql;
779: }
780:
781: public boolean isCrossJoinAllowed() {
782: return crossJoinAllowed;
783: }
784:
785: public ProjectionQueryDecoder getProjectionDecoder() {
786: return projectionDecoder;
787: }
788:
789: public void setProjectionDecoder(ProjectionQueryDecoder decoder) {
790: this .projectionDecoder = decoder;
791: }
792:
793: /**
794: * Todo get rid of this horrible hack when we refactor all the query stuff
795: */
796: public boolean isEJBQLHack() {
797: return false;
798: }
799:
800: /**
801: * Utils struct that is used when building mapping info for
802: * sql queries.
803: */
804: public static class RefFieldMapping {
805: public JdbcRefField fromField;
806: public JdbcField toField;
807:
808: public void clear() {
809: fromField = null;
810: toField = null;
811: }
812: }
813:
814: /**
815: * Utils struct to hold the info wrt to mapping jdbc fields to a
816: * 'sql' query.
817: */
818: public static class MappingInfo {
819: public ClassMetaData cmd;
820: /**
821: * The index pos of where the pkCols start
822: */
823: public int dsPkIndex = -1;
824: /**
825: * The index pos of where the discr Cols is
826: */
827: public int discrIndex = 0;
828: /**
829: * These are only fields for this hierarchy. The position in the array determine
830: * the index in rs shifted by 1.
831: */
832: public JdbcField[] fields;
833: public int colCount;
834: public boolean pkFieldsFound;
835: public boolean discriminatorColFound;
836: public int[] pkIndexInFieldsArray;
837:
838: public void dump() {
839: System.out.println("MappingInfo.dump@"
840: + System.identityHashCode(this ));
841: for (int i = 0; i < fields.length; i++) {
842: JdbcField jdbcField = fields[i];
843: if (jdbcField != null) {
844: System.out.println("jdbcField = " + jdbcField);
845: }
846: }
847: }
848:
849: /**
850: * If there is enough info to map(id , discriminator)
851: */
852: public boolean isPkValid() {
853: return dsPkIndex != -1 || pkIndexInFieldsArray != null;
854: }
855: }
856:
857: public String toString() {
858: return sqlStruct == null ? "sqlStruct is null" : sqlStruct
859: .getSql();
860: }
861:
862: public int getId() {
863: return id;
864: }
865:
866: public void setId(int id) {
867: this.id = id;
868: }
869:
870: }
|