KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > ojb > broker > accesslayer > MtoNCollectionPrefetcher


1 package org.apache.ojb.broker.accesslayer;
2
3 /* Copyright 2003-2005 The Apache Software Foundation
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */

17
18 import java.lang.reflect.Array JavaDoc;
19 import java.util.ArrayList JavaDoc;
20 import java.util.Collection JavaDoc;
21 import java.util.HashMap JavaDoc;
22 import java.util.HashSet JavaDoc;
23 import java.util.Iterator JavaDoc;
24 import java.util.List JavaDoc;
25
26 import org.apache.ojb.broker.Identity;
27 import org.apache.ojb.broker.ManageableCollection;
28 import org.apache.ojb.broker.PersistenceBroker;
29 import org.apache.ojb.broker.accesslayer.conversions.FieldConversion;
30 import org.apache.ojb.broker.core.PersistenceBrokerImpl;
31 import org.apache.ojb.broker.core.proxy.CollectionProxyDefaultImpl;
32 import org.apache.ojb.broker.metadata.ClassDescriptor;
33 import org.apache.ojb.broker.metadata.CollectionDescriptor;
34 import org.apache.ojb.broker.metadata.FieldDescriptor;
35 import org.apache.ojb.broker.metadata.FieldHelper;
36 import org.apache.ojb.broker.metadata.ObjectReferenceDescriptor;
37 import org.apache.ojb.broker.metadata.fieldaccess.PersistentField;
38 import org.apache.ojb.broker.query.Criteria;
39 import org.apache.ojb.broker.query.Query;
40 import org.apache.ojb.broker.query.QueryByMtoNCriteria;
41 import org.apache.ojb.broker.query.ReportQueryByMtoNCriteria;
42
43 /**
44  * Relationship Prefetcher for MtoN-Collections.
45  *
46  * @author <a HREF="mailto:jbraeuchi@gmx.ch">Jakob Braeuchi</a>
47  * @version $Id: MtoNCollectionPrefetcher.java,v 1.12.2.7 2005/12/21 22:22:58 tomdz Exp $
48  */

49 public class MtoNCollectionPrefetcher extends CollectionPrefetcher
50 {
51
52     /**
53      * @param aBroker the PersistenceBroker
54      * @param anOrd the CollectionDescriptor
55      */

56     public MtoNCollectionPrefetcher(PersistenceBrokerImpl aBroker, ObjectReferenceDescriptor anOrd)
57     {
58         super(aBroker, anOrd);
59     }
60
61     /**
62      * @see org.apache.ojb.broker.accesslayer.RelationshipPrefetcher#prefetchRelationship(Collection)
63      */

64     public void prefetchRelationship(Collection JavaDoc owners)
65     {
66         Query[] queries;
67         Query[] mnQueries;
68         Collection JavaDoc children = new ArrayList JavaDoc();
69         Collection JavaDoc mnImplementors = new ArrayList JavaDoc();
70
71         queries = buildPrefetchQueries(owners, children);
72         mnQueries = buildMtoNImplementorQueries(owners, children);
73
74         for (int i = 0; i < queries.length; i++)
75         {
76             Iterator JavaDoc iter = getBroker().getIteratorByQuery(queries[i]);
77             while (iter.hasNext())
78             {
79                 Object JavaDoc aChild = iter.next();
80
81                 // BRJ: simulate the distinct removed from the query
82
if (!children.contains(aChild))
83                 {
84                     children.add(aChild);
85                 }
86             }
87
88             Iterator JavaDoc mnIter = getBroker().getReportQueryIteratorByQuery(mnQueries[i]);
89             while (mnIter.hasNext())
90             {
91                 mnImplementors.add(mnIter.next());
92             }
93         }
94
95         associateBatched(owners, children, mnImplementors);
96     }
97
98     /**
99      * Build the prefetch query for a M-N relationship, The query looks like the following sample :
100      * <br>
101      * <pre>
102      * crit = new Criteria();
103      * crit.addIn("PERSON_PROJECT.PROJECT_ID", ids);
104      * crit.addEqualToField("id","PERSON_PROJECT.PERSON_ID");
105      * qry = new QueryByMtoNCriteria(Person.class, "PERSON_PROJECT", crit, true);
106      * </pre>
107      *
108      * @param ids Collection containing all identities of objects of the M side
109      * @return the prefetch Query
110      */

111     protected Query buildPrefetchQuery(Collection JavaDoc ids)
112     {
113         CollectionDescriptor cds = getCollectionDescriptor();
114         String JavaDoc[] indFkCols = getFksToThisClass();
115         String JavaDoc[] indItemFkCols = getFksToItemClass();
116         FieldDescriptor[] itemPkFields = getItemClassDescriptor().getPkFields();
117
118         Criteria crit = buildPrefetchCriteria(ids, indFkCols, indItemFkCols, itemPkFields);
119
120         // BRJ: do not use distinct:
121
//
122
// ORA-22901 cannot compare nested table or VARRAY or LOB attributes of an object type
123
// Cause: Comparison of nested table or VARRAY or LOB attributes of an
124
// object type was attempted in the absence of a MAP or ORDER method.
125
// Action: Define a MAP or ORDER method for the object type.
126
//
127
// Without the distinct the resultset may contain duplicate rows
128

129         return new QueryByMtoNCriteria(cds.getItemClass(), cds.getIndirectionTable(), crit, false);
130     }
131
132     /**
133      * Build a query to read the mn-implementors
134      * @param ids
135      */

136     protected Query buildMtoNImplementorQuery(Collection JavaDoc ids)
137     {
138         String JavaDoc[] indFkCols = getFksToThisClass();
139         String JavaDoc[] indItemFkCols = getFksToItemClass();
140         FieldDescriptor[] pkFields = getOwnerClassDescriptor().getPkFields();
141         FieldDescriptor[] itemPkFields = getItemClassDescriptor().getPkFields();
142         String JavaDoc[] cols = new String JavaDoc[indFkCols.length + indItemFkCols.length];
143         int[] jdbcTypes = new int[indFkCols.length + indItemFkCols.length];
144
145         // concatenate the columns[]
146
System.arraycopy(indFkCols, 0, cols, 0, indFkCols.length);
147         System.arraycopy(indItemFkCols, 0, cols, indFkCols.length, indItemFkCols.length);
148
149         Criteria crit = buildPrefetchCriteria(ids, indFkCols, indItemFkCols, itemPkFields);
150
151         // determine the jdbcTypes of the pks
152
for (int i = 0; i < pkFields.length; i++)
153         {
154             jdbcTypes[i] = pkFields[i].getJdbcType().getType();
155         }
156         for (int i = 0; i < itemPkFields.length; i++)
157         {
158             jdbcTypes[pkFields.length + i] = itemPkFields[i].getJdbcType().getType();
159         }
160
161         ReportQueryByMtoNCriteria q = new ReportQueryByMtoNCriteria(getItemClassDescriptor().getClassOfObject(), cols,
162                 crit, false);
163         q.setIndirectionTable(getCollectionDescriptor().getIndirectionTable());
164         q.setJdbcTypes(jdbcTypes);
165
166         CollectionDescriptor cds = getCollectionDescriptor();
167         //check if collection must be ordered
168
if (!cds.getOrderBy().isEmpty())
169         {
170             Iterator JavaDoc iter = cds.getOrderBy().iterator();
171             while (iter.hasNext())
172             {
173                 q.addOrderBy((FieldHelper) iter.next());
174             }
175         }
176         
177         return q;
178     }
179
180     /**
181      * prefix the this class fk columns with the indirection table
182      */

183     private String JavaDoc[] getFksToThisClass()
184     {
185         String JavaDoc indTable = getCollectionDescriptor().getIndirectionTable();
186         String JavaDoc[] fks = getCollectionDescriptor().getFksToThisClass();
187         String JavaDoc[] result = new String JavaDoc[fks.length];
188
189         for (int i = 0; i < result.length; i++)
190         {
191             result[i] = indTable + "." + fks[i];
192         }
193
194         return result;
195     }
196
197     /**
198      * prefix the item class fk columns with the indirection table
199      */

200     private String JavaDoc[] getFksToItemClass()
201     {
202         String JavaDoc indTable = getCollectionDescriptor().getIndirectionTable();
203         String JavaDoc[] fks = getCollectionDescriptor().getFksToItemClass();
204         String JavaDoc[] result = new String JavaDoc[fks.length];
205
206         for (int i = 0; i < result.length; i++)
207         {
208             result[i] = indTable + "." + fks[i];
209         }
210
211         return result;
212     }
213
214     /**
215      * Build the multiple queries for one relationship because of limitation of IN(...)
216      *
217      * @param owners Collection containing all objects of the ONE side
218      */

219     protected Query[] buildMtoNImplementorQueries(Collection JavaDoc owners, Collection JavaDoc children)
220     {
221         ClassDescriptor cld = getOwnerClassDescriptor();
222         PersistenceBroker pb = getBroker();
223         //Class topLevelClass = pb.getTopLevelClass(cld.getClassOfObject());
224
//BrokerHelper helper = pb.serviceBrokerHelper();
225
Collection JavaDoc queries = new ArrayList JavaDoc(owners.size());
226         Collection JavaDoc idsSubset = new HashSet JavaDoc(owners.size());
227         //Object[] fkValues;
228
Object JavaDoc owner;
229         Identity id;
230
231         Iterator JavaDoc iter = owners.iterator();
232         while (iter.hasNext())
233         {
234             owner = iter.next();
235             id = pb.serviceIdentity().buildIdentity(cld, owner);
236             idsSubset.add(id);
237             if (idsSubset.size() == pkLimit)
238             {
239                 queries.add(buildMtoNImplementorQuery(idsSubset));
240                 idsSubset.clear();
241             }
242         }
243
244         if (idsSubset.size() > 0)
245         {
246             queries.add(buildMtoNImplementorQuery(idsSubset));
247         }
248
249         return (Query[]) queries.toArray(new Query[queries.size()]);
250     }
251
252     /**
253      * Build the prefetch criteria
254      *
255      * @param ids Collection of identities of M side
256      * @param fkCols indirection table fks to this class
257      * @param itemFkCols indirection table fks to item class
258      * @param itemPkFields
259      */

260     private Criteria buildPrefetchCriteria(Collection JavaDoc ids, String JavaDoc[] fkCols, String JavaDoc[] itemFkCols,
261             FieldDescriptor[] itemPkFields)
262     {
263         if (fkCols.length == 1 && itemFkCols.length == 1)
264         {
265             return buildPrefetchCriteriaSingleKey(ids, fkCols[0], itemFkCols[0], itemPkFields[0]);
266         }
267         else
268         {
269             return buildPrefetchCriteriaMultipleKeys(ids, fkCols, itemFkCols, itemPkFields);
270         }
271
272     }
273
274     /**
275      * Build the prefetch criteria
276      *
277      * @param ids Collection of identities of M side
278      * @param fkCol indirection table fks to this class
279      * @param itemFkCol indirection table fks to item class
280      * @param itemPkField
281      * @return the Criteria
282      */

283     private Criteria buildPrefetchCriteriaSingleKey(Collection JavaDoc ids, String JavaDoc fkCol, String JavaDoc itemFkCol,
284             FieldDescriptor itemPkField)
285     {
286         Criteria crit = new Criteria();
287         ArrayList JavaDoc values = new ArrayList JavaDoc(ids.size());
288         Iterator JavaDoc iter = ids.iterator();
289         Identity id;
290
291         while (iter.hasNext())
292         {
293             id = (Identity) iter.next();
294             values.add(id.getPrimaryKeyValues()[0]);
295         }
296
297         switch (values.size())
298         {
299             case 0 :
300                 break;
301             case 1 :
302                 crit.addEqualTo(fkCol, values.get(0));
303                 break;
304             default :
305                 // create IN (...) for the single key field
306
crit.addIn(fkCol, values);
307                 break;
308         }
309
310         crit.addEqualToField(itemPkField.getAttributeName(), itemFkCol);
311
312         return crit;
313     }
314
315     /**
316      * Build the prefetch criteria
317      *
318      * @param ids Collection of identities of M side
319      * @param fkCols indirection table fks to this class
320      * @param itemFkCols indirection table fks to item class
321      * @param itemPkFields
322      * @return the Criteria
323      */

324     private Criteria buildPrefetchCriteriaMultipleKeys(Collection JavaDoc ids, String JavaDoc[] fkCols, String JavaDoc[] itemFkCols,
325             FieldDescriptor[] itemPkFields)
326     {
327         Criteria crit = new Criteria();
328         Criteria critValue = new Criteria();
329         Iterator JavaDoc iter = ids.iterator();
330
331         for (int i = 0; i < itemPkFields.length; i++)
332         {
333             crit.addEqualToField(itemPkFields[i].getAttributeName(), itemFkCols[i]);
334         }
335         
336         while (iter.hasNext())
337         {
338             Criteria c = new Criteria();
339             Identity id = (Identity) iter.next();
340             Object JavaDoc[] val = id.getPrimaryKeyValues();
341
342             for (int i = 0; i < val.length; i++)
343             {
344
345                 if (val[i] == null)
346                 {
347                     c.addIsNull(fkCols[i]);
348                 }
349                 else
350                 {
351                     c.addEqualTo(fkCols[i], val[i]);
352                 }
353
354             }
355           
356             critValue.addOrCriteria(c);
357         }
358
359         crit.addAndCriteria(critValue);
360         return crit;
361     }
362
363     /**
364      * Answer the FieldConversions for the PkFields
365      * @param cld
366      * @return the pk FieldConversions
367      */

368     private FieldConversion[] getPkFieldConversion(ClassDescriptor cld)
369     {
370         FieldDescriptor[] pks = cld.getPkFields();
371         FieldConversion[] fc = new FieldConversion[pks.length];
372         
373         for (int i= 0; i < pks.length; i++)
374         {
375             fc[i] = pks[i].getFieldConversion();
376         }
377         
378         return fc;
379     }
380     
381     /**
382      * Convert the Values using the FieldConversion.sqlToJava
383      * @param fcs
384      * @param values
385      */

386     private Object JavaDoc[] convert(FieldConversion[] fcs, Object JavaDoc[] values)
387     {
388         Object JavaDoc[] convertedValues = new Object JavaDoc[values.length];
389         
390         for (int i= 0; i < values.length; i++)
391         {
392             convertedValues[i] = fcs[i].sqlToJava(values[i]);
393         }
394
395         return convertedValues;
396     }
397     
398     /**
399      * associate the batched Children with their owner object loop over children
400      * <br><br>
401      * BRJ: There is a potential problem with the type of the pks used to build the Identities.
402      * When creating an Identity for the owner, the type of pk is defined by the instvars
403      * representing the pk. When creating the Identity based on the mToNImplementor the
404      * type of the pk is defined by the jdbc-type of field-descriptor of the referenced class.
405      * This type mismatch results in Identities not being equal.
406      * Integer[] {10,20,30} is not equal Long[] {10,20,30}
407      * <br><br>
408      * This problem is documented in defect OJB296.
409      * The conversion of the keys of the mToNImplementor should solve this problem.
410      */

411     protected void associateBatched(Collection JavaDoc owners, Collection JavaDoc children, Collection JavaDoc mToNImplementors)
412     {
413         CollectionDescriptor cds = getCollectionDescriptor();
414         PersistentField field = cds.getPersistentField();
415         PersistenceBroker pb = getBroker();
416         Class JavaDoc ownerTopLevelClass = pb.getTopLevelClass(getOwnerClassDescriptor().getClassOfObject());
417         Class JavaDoc childTopLevelClass = pb.getTopLevelClass(getItemClassDescriptor().getClassOfObject());
418         Class JavaDoc collectionClass = cds.getCollectionClass(); // this collection type will be used:
419
HashMap JavaDoc childMap = new HashMap JavaDoc();
420         HashMap JavaDoc ownerIdsToLists = new HashMap JavaDoc();
421         FieldConversion[] ownerFc = getPkFieldConversion(getOwnerClassDescriptor());
422         FieldConversion[] childFc = getPkFieldConversion(getItemClassDescriptor());
423
424         // initialize the owner list map
425
for (Iterator JavaDoc it = owners.iterator(); it.hasNext();)
426         {
427             Object JavaDoc owner = it.next();
428             Identity oid = pb.serviceIdentity().buildIdentity(owner);
429             ownerIdsToLists.put(oid, new ArrayList JavaDoc());
430         }
431
432         // build the children map
433
for (Iterator JavaDoc it = children.iterator(); it.hasNext();)
434         {
435             Object JavaDoc child = it.next();
436             Identity oid = pb.serviceIdentity().buildIdentity(child);
437             childMap.put(oid, child);
438         }
439
440         int ownerPkLen = getOwnerClassDescriptor().getPkFields().length;
441         int childPkLen = getItemClassDescriptor().getPkFields().length;
442         Object JavaDoc[] ownerPk = new Object JavaDoc[ownerPkLen];
443         Object JavaDoc[] childPk = new Object JavaDoc[childPkLen];
444
445         // build list of children based on m:n implementors
446
for (Iterator JavaDoc it = mToNImplementors.iterator(); it.hasNext();)
447         {
448             Object JavaDoc[] mToN = (Object JavaDoc[]) it.next();
449             System.arraycopy(mToN, 0, ownerPk, 0, ownerPkLen);
450             System.arraycopy(mToN, ownerPkLen, childPk, 0, childPkLen);
451
452             // BRJ: apply the FieldConversions, OJB296
453
ownerPk = convert(ownerFc, ownerPk);
454             childPk = convert(childFc, childPk);
455
456             Identity ownerId = pb.serviceIdentity().buildIdentity(null, ownerTopLevelClass, ownerPk);
457             Identity childId = pb.serviceIdentity().buildIdentity(null, childTopLevelClass, childPk);
458
459             // Identities may not be equal due to type-mismatch
460
Collection JavaDoc list = (Collection JavaDoc) ownerIdsToLists.get(ownerId);
461             Object JavaDoc child = childMap.get(childId);
462             list.add(child);
463         }
464
465         // connect children list to owners
466
for (Iterator JavaDoc it = owners.iterator(); it.hasNext();)
467         {
468             Object JavaDoc result;
469             Object JavaDoc owner = it.next();
470             Identity ownerId = pb.serviceIdentity().buildIdentity(owner);
471
472             List JavaDoc list = (List JavaDoc) ownerIdsToLists.get(ownerId);
473
474             if ((collectionClass == null) && field.getType().isArray())
475             {
476                 int length = list.size();
477                 Class JavaDoc itemtype = field.getType().getComponentType();
478
479                 result = Array.newInstance(itemtype, length);
480
481                 for (int j = 0; j < length; j++)
482                 {
483                     Array.set(result, j, list.get(j));
484                 }
485             }
486             else
487             {
488                 ManageableCollection col = createCollection(cds, collectionClass);
489
490                 for (Iterator JavaDoc it2 = list.iterator(); it2.hasNext();)
491                 {
492                     col.ojbAdd(it2.next());
493                 }
494                 result = col;
495             }
496
497             Object JavaDoc value = field.get(owner);
498             if ((value instanceof CollectionProxyDefaultImpl) && (result instanceof Collection JavaDoc))
499             {
500                 ((CollectionProxyDefaultImpl) value).setData((Collection JavaDoc) result);
501             }
502             else
503             {
504                 field.set(owner, result);
505             }
506         }
507
508     }
509 }
510
Popular Tags