001: /**********************************************************************
002: Copyright (c) 2003 Erik Bengtson and others. All rights reserved.
003: Licensed under the Apache License, Version 2.0 (the "License");
004: you may not use this file except in compliance with the License.
005: You may obtain a copy of the License at
006:
007: http://www.apache.org/licenses/LICENSE-2.0
008:
009: Unless required by applicable law or agreed to in writing, software
010: distributed under the License is distributed on an "AS IS" BASIS,
011: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012: See the License for the specific language governing permissions and
013: limitations under the License.
014:
015:
016: Contributors:
017: 2003 Andy Jefferson - coding standards
018: 2005 Andy Jefferson - added handling for left outer join of related fields when in fetch group
019: 2006 Andy Jefferson - added support for primary join table and select of owner object
020: ...
021: **********************************************************************/package org.jpox.store.mapping;
022:
023: import org.jpox.ClassLoaderResolver;
024: import org.jpox.metadata.AbstractMemberMetaData;
025: import org.jpox.metadata.Relation;
026: import org.jpox.store.DatastoreClass;
027: import org.jpox.store.DatastoreContainerObject;
028: import org.jpox.store.DatastoreElementContainer;
029: import org.jpox.store.DatastoreIdentifier;
030: import org.jpox.store.DatastoreObject;
031: import org.jpox.store.IdentifierFactory;
032: import org.jpox.store.MappedStoreManager;
033: import org.jpox.store.StatementExpressionIndex;
034: import org.jpox.store.StoreManager;
035: import org.jpox.store.expression.QueryExpression;
036: import org.jpox.store.expression.ScalarExpression;
037: import org.jpox.store.expression.LogicSetExpression;
038:
039: /**
040: * Helper class for doing many tasks with mapping.
041: *
042: * @version $Revision: 1.34 $
043: */
044: public class Mappings {
045: /**
046: * Add columns to a query. Columns added to the query are obtained from the
047: * mappings included in the statementExpressionIndex passed as parameter.
048: * After adding columns to the query, statementExpressionIndex is updated
049: * with the index/position of the columns in the query.
050: * @param qs the Query Statement
051: * @param statementExpressionIndex the list of mapping/column indexes to be
052: * included in the query
053: */
054: public static void selectMapping(QueryExpression qs,
055: StatementExpressionIndex[] statementExpressionIndex) {
056: selectMapping(qs, null, statementExpressionIndex);
057: }
058:
059: /**
060: * Add columns to a query. Columns added to the query are obtained from the
061: * mappings included in the statementExpressionIndex passed as parameter.
062: * After adding columns to the query, statementExpressionIndex is updated
063: * with the index/position of the columns in the query.
064: * @param qs the Query Statement
065: * @param tableIdentifier the Identifier for the table which the columns are from (may be null, if TODO)
066: * @param statementExpressionIndex the list of mapping/column indexes to be
067: * included in the query
068: */
069: public static void selectMapping(QueryExpression qs,
070: DatastoreIdentifier tableIdentifier,
071: StatementExpressionIndex[] statementExpressionIndex) {
072: if (statementExpressionIndex == null) {
073: return;
074: }
075: for (int i = 0; i < statementExpressionIndex.length; ++i) {
076: if (statementExpressionIndex[i] != null) {
077: JavaTypeMapping m = statementExpressionIndex[i]
078: .getMapping();
079: if (m != null) {
080: if (m.getNumberOfDatastoreFields() > 0) {
081: int[] columnNumbersByField = (tableIdentifier != null) ? qs
082: .select(tableIdentifier, m, true)
083: : qs.select(m, true);
084: statementExpressionIndex[i]
085: .setExpressionIndex(columnNumbersByField);
086: } else {
087: setStatementExpressionForFieldWithNoDatastoreColumns(
088: qs, tableIdentifier, m,
089: statementExpressionIndex[i]);
090: }
091: }
092: }
093: }
094: }
095:
096: /**
097: * Convenience method that sets the StatementExpressionIndex for a mapping that has no datastore mappings
098: * @param qs The QueryStatement
099: * @param tableIdentifier the Identifier for the table which the columns are from (may be null, if TODO)
100: * @param m The mapping
101: * @param statementExpressionIndex The StatementExpressionIndex to update
102: */
103: private static void setStatementExpressionForFieldWithNoDatastoreColumns(
104: QueryExpression qs, DatastoreIdentifier tableIdentifier,
105: JavaTypeMapping m,
106: StatementExpressionIndex statementExpressionIndex) {
107: AbstractMemberMetaData fmd = m.getFieldMetaData();
108: ClassLoaderResolver clr = qs.getClassLoaderResolver();
109: int relationType = fmd.getRelationType(clr);
110: if (relationType == Relation.ONE_TO_ONE_BI) {
111: if (fmd.getMappedBy() != null) {
112: // "mapped-by" at this side
113: // 1-1 bidirectional single FK relation, with FK in source table.
114: // This field is not stored, but instead a foreign is set in the source table which stores a reference to this (target table).
115: // A FK is set in the source table refering to the target table.
116: // Do a LEFT OUTER JOIN to the related (source) table(s) to pick up the identifier value of this.
117: /*
118: * For example :-
119: * TABLE A (TARGET) TABLE B (SOURCE)
120: * ID ID A_ID (FK)
121: * -- -- ----
122: * 18 21 18
123: *
124: * Class A Class B
125: * { {
126: * B b; A a;
127: * } }
128: */
129:
130: int[] colNums = null;
131: StoreManager storeMgr = qs.getStoreManager();
132: JavaTypeMapping[] sourceMappings = null;
133: DatastoreClass[] sourceTables = null;
134: if (m instanceof ReferenceMapping) {
135: // Reference field (with "n" implementations)
136: JavaTypeMapping[] refMappings = ((ReferenceMapping) m)
137: .getJavaTypeMapping();
138: sourceTables = new DatastoreClass[refMappings.length];
139: sourceMappings = new JavaTypeMapping[refMappings.length];
140: for (int i = 0; i < refMappings.length; i++) {
141: sourceTables[i] = storeMgr.getDatastoreClass(
142: refMappings[i].type, qs
143: .getClassLoaderResolver());
144: sourceMappings[i] = sourceTables[i]
145: .getFieldMapping(fmd.getMappedBy());
146: }
147: } else {
148: // PC field
149: sourceTables = new DatastoreClass[1];
150: sourceMappings = new JavaTypeMapping[1];
151: sourceTables[0] = storeMgr
152: .getDatastoreClass(fmd.getTypeName(), qs
153: .getClassLoaderResolver());
154: sourceMappings[0] = sourceTables[0]
155: .getFieldMapping(fmd.getMappedBy());
156: }
157:
158: for (int i = 0; i < sourceMappings.length; i++) {
159: // Do a LEFT OUTER JOIN for this sourceMapping back to the target id column(s)
160:
161: // TODO Move this hardcoded table name out into the calling class so it controls the names of all table aliases
162: DatastoreIdentifier sourceTableIdentifier = storeMgr
163: .getIdentifierFactory()
164: .newIdentifier(
165: IdentifierFactory.TABLE,
166: "SOURCECLASS"
167: + fmd
168: .getAbsoluteFieldNumber()
169: + "_" + i);
170: LogicSetExpression sourceTableExpr = qs
171: .newTableExpression(sourceTables[i],
172: sourceTableIdentifier, true)[0];
173:
174: // Left outer join from target ID to the mapped-by field on the source class
175: ScalarExpression sourceExpr = sourceMappings[i]
176: .newScalarExpression(qs, sourceTableExpr);
177: ScalarExpression targetExpr;
178: if (tableIdentifier != null) {
179: LogicSetExpression targetTableExpr;
180: if (qs.getTableExpression(tableIdentifier) == null) {
181: targetTableExpr = qs.newTableExpression(m
182: .getDatastoreContainer(),
183: tableIdentifier);
184: } else {
185: targetTableExpr = qs
186: .getTableExpression(tableIdentifier);
187: }
188: targetExpr = m.getDatastoreContainer()
189: .getIDMapping().newScalarExpression(qs,
190: targetTableExpr);
191: } else {
192: targetExpr = m.getDatastoreContainer()
193: .getIDMapping().newScalarExpression(qs,
194: qs.getMainTableExpression());
195: }
196: qs.leftOuterJoin(sourceExpr, targetExpr,
197: sourceTableExpr, true, true);
198:
199: // Select the Id column(s) of the source class (via the Left Outer Join)
200: int[] columnNumbersByField = qs.select(
201: sourceTableIdentifier, sourceTables[i]
202: .getIDMapping(), true);
203:
204: // Copy these column numbers into our overall column numbers set
205: if (sourceMappings.length == 1) {
206: // We only have one source so just reference these
207: colNums = columnNumbersByField;
208: } else if (colNums != null) {
209: // Append to the end of where we have got to
210: int[] tmpColNums = colNums;
211: colNums = new int[tmpColNums.length
212: + columnNumbersByField.length];
213: for (int j = 0; j < tmpColNums.length; j++) {
214: colNums[j] = tmpColNums[j];
215: }
216: for (int j = 0; j < columnNumbersByField.length; j++) {
217: colNums[tmpColNums.length + j] = columnNumbersByField[j];
218: }
219: tmpColNums = null;
220: } else {
221: // Add to the start of our column numbers
222: colNums = new int[columnNumbersByField.length];
223: for (int j = 0; j < columnNumbersByField.length; j++) {
224: colNums[j] = columnNumbersByField[j];
225: }
226: }
227: }
228:
229: statementExpressionIndex.setExpressionIndex(colNums);
230: }
231: } else if (relationType == Relation.MANY_TO_ONE_BI) {
232: AbstractMemberMetaData[] relatedMmds = fmd
233: .getRelatedMemberMetaData(clr);
234: // TODO Cater for more than 1 related field
235: if (fmd.getJoinMetaData() != null
236: || relatedMmds[0].getJoinMetaData() != null) {
237: // 1-N bidirectional join table relation.
238: // Do a LEFT OUTER JOIN to the join table to pick up the value.
239: MappedStoreManager storeMgr = (MappedStoreManager) qs
240: .getStoreManager();
241: DatastoreContainerObject joinTable = storeMgr
242: .getDatastoreContainerObject(relatedMmds[0]);
243: JavaTypeMapping referenceMapping = null;
244: JavaTypeMapping selectMapping = null;
245:
246: DatastoreElementContainer collTable = (DatastoreElementContainer) joinTable;
247: referenceMapping = collTable.getElementMapping();
248: selectMapping = collTable.getOwnerMapping();
249:
250: DatastoreObject mainTable = qs.getMainTableExpression()
251: .getMainTable();
252: if (!mainTable.equals(joinTable)) {
253: // Statement selects the element table and the owner column should be selected
254: // Join across to the join table, and select the owner mapping of the join table
255:
256: // TODO Move this hardcoded table name out into the calling class so it controls the names of all table aliases
257: DatastoreIdentifier joinTableIdentifier = storeMgr
258: .getIdentifierFactory()
259: .newIdentifier(
260: IdentifierFactory.TABLE,
261: "JOINTABLE"
262: + fmd
263: .getAbsoluteFieldNumber());
264: LogicSetExpression table_expr_sub = qs
265: .newTableExpression(joinTable,
266: joinTableIdentifier, true)[0];
267:
268: // Left outer join from our Id to the mapped-by field on the other class
269: ScalarExpression subExpr = referenceMapping
270: .newScalarExpression(qs, table_expr_sub);
271: ScalarExpression schExpr = null;
272: if (tableIdentifier != null) {
273: LogicSetExpression targetTableExpr;
274: if (qs.getTableExpression(tableIdentifier) == null) {
275: targetTableExpr = qs.newTableExpression(m
276: .getDatastoreContainer(),
277: tableIdentifier);
278: } else {
279: targetTableExpr = qs
280: .getTableExpression(tableIdentifier);
281: }
282: schExpr = m.getDatastoreContainer()
283: .getIDMapping().newScalarExpression(qs,
284: targetTableExpr);
285: } else {
286: schExpr = m.getDatastoreContainer()
287: .getIDMapping().newScalarExpression(qs,
288: qs.getMainTableExpression());
289: }
290: qs.leftOuterJoin(subExpr, schExpr, table_expr_sub,
291: true, true);
292:
293: // Select the Id column(s) of the other class (via the Left Outer Join)
294: int[] columnNumbersByField = qs.select(
295: joinTableIdentifier, selectMapping, true);
296: statementExpressionIndex
297: .setExpressionIndex(columnNumbersByField);
298: } else {
299: // The statement selects the join table, and joins to the element, and the owner column should be selected
300: // Select the owner mapping of the join table directly (no join needed)
301: int[] columnNumbersByField = qs.select(
302: selectMapping, true);
303: statementExpressionIndex
304: .setExpressionIndex(columnNumbersByField);
305: }
306: }
307: }
308: }
309:
310: /**
311: * Get the indexes/position for Mapping columns in a given the initial
312: * position. Used to set parameters in PreparedStatements
313: * @param initialPosition the initialPosition
314: * @param mapping the Mapping
315: * @return an array containing indexes for parameters
316: **/
317: public static int[] getParametersIndex(int initialPosition,
318: JavaTypeMapping mapping) {
319: if (mapping.getNumberOfDatastoreFields() < 1) {
320: return new int[] { initialPosition };
321: }
322:
323: int parameter[] = new int[mapping.getNumberOfDatastoreFields()];
324: for (int i = 0; i < parameter.length; i++) {
325: parameter[i] = initialPosition + i;
326: }
327: return parameter;
328: }
329: }
|