001 package org.apache.ojb.broker.accesslayer;
002
003 /* Copyright 2002-2005 The Apache Software Foundation
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017
018 import java.lang.reflect.Array;
019 import java.util.ArrayList;
020 import java.util.Collection;
021 import java.util.HashMap;
022 import java.util.HashSet;
023 import java.util.Iterator;
024 import java.util.List;
025
026 import org.apache.ojb.broker.Identity;
027 import org.apache.ojb.broker.ManageableCollection;
028 import org.apache.ojb.broker.OJBRuntimeException;
029 import org.apache.ojb.broker.PersistenceBroker;
030 import org.apache.ojb.broker.IdentityFactory;
031 import org.apache.ojb.broker.core.PersistenceBrokerImpl;
032 import org.apache.ojb.broker.core.proxy.CollectionProxyDefaultImpl;
033 import org.apache.ojb.broker.core.proxy.ProxyHelper;
034 import org.apache.ojb.broker.metadata.ClassDescriptor;
035 import org.apache.ojb.broker.metadata.CollectionDescriptor;
036 import org.apache.ojb.broker.metadata.FieldHelper;
037 import org.apache.ojb.broker.metadata.MetadataException;
038 import org.apache.ojb.broker.metadata.ObjectReferenceDescriptor;
039 import org.apache.ojb.broker.metadata.fieldaccess.PersistentField;
040 import org.apache.ojb.broker.query.Query;
041 import org.apache.ojb.broker.query.QueryByCriteria;
042 import org.apache.ojb.broker.util.BrokerHelper;
043 import org.apache.ojb.broker.util.collections.RemovalAwareCollection;
044 import org.apache.ojb.broker.util.collections.RemovalAwareList;
045 import org.apache.ojb.broker.util.collections.RemovalAwareSet;
046
047 /**
048 * Relationship Prefetcher for Collections.
049 *
050 * @author <a href="mailto:jbraeuchi@gmx.ch">Jakob Braeuchi</a>
051 * @version $Id: CollectionPrefetcher.java,v 1.1 2007-08-24 22:17:30 ewestfal Exp $
052 */
053 public class CollectionPrefetcher extends RelationshipPrefetcherImpl
054 {
055
056 /**
057 * Constructor for CollectionPrefetcher.
058 *
059 * @param aBroker
060 * @param anOrd
061 */
062 public CollectionPrefetcher(PersistenceBrokerImpl aBroker, ObjectReferenceDescriptor anOrd)
063 {
064 super(aBroker, anOrd);
065 }
066
067 /**
068 * Build the multiple queries for one relationship because of limitation of IN(...)
069 *
070 * @param owners Collection containing all objects of the ONE side
071 */
072 protected Query[] buildPrefetchQueries(Collection owners, Collection children)
073 {
074 ClassDescriptor cld = getOwnerClassDescriptor();
075 Class topLevelClass = getBroker().getTopLevelClass(cld.getClassOfObject());
076 BrokerHelper helper = getBroker().serviceBrokerHelper();
077 Collection queries = new ArrayList(owners.size());
078 Collection idsSubset = new HashSet(owners.size());
079 Object[] fkValues;
080 Object owner;
081 Identity id;
082
083 Iterator iter = owners.iterator();
084 while (iter.hasNext())
085 {
086 owner = iter.next();
087 fkValues = helper.extractValueArray(helper.getKeyValues(cld, owner));
088 id = getBroker().serviceIdentity().buildIdentity(null, topLevelClass, fkValues);
089 idsSubset.add(id);
090 if (idsSubset.size() == pkLimit)
091 {
092 queries.add(buildPrefetchQuery(idsSubset));
093 idsSubset.clear();
094 }
095 }
096
097 if (idsSubset.size() > 0)
098 {
099 queries.add(buildPrefetchQuery(idsSubset));
100 }
101
102 return (Query[]) queries.toArray(new Query[queries.size()]);
103 }
104
105 /**
106 * Build the query to perform a batched read get orderBy settings from CollectionDescriptor
107 *
108 * @param ids Collection containing all identities of objects of the ONE side
109 */
110 protected Query buildPrefetchQuery(Collection ids)
111 {
112 CollectionDescriptor cds = getCollectionDescriptor();
113 QueryByCriteria query = buildPrefetchQuery(ids, cds.getForeignKeyFieldDescriptors(getItemClassDescriptor()));
114
115 // check if collection must be ordered
116 if (!cds.getOrderBy().isEmpty())
117 {
118 Iterator iter = cds.getOrderBy().iterator();
119 while (iter.hasNext())
120 {
121 query.addOrderBy((FieldHelper) iter.next());
122 }
123 }
124
125 return query;
126 }
127
128 /**
129 * associate the batched Children with their owner object loop over children
130 */
131 protected void associateBatched(Collection owners, Collection children)
132 {
133 CollectionDescriptor cds = getCollectionDescriptor();
134 PersistentField field = cds.getPersistentField();
135 PersistenceBroker pb = getBroker();
136 Class ownerTopLevelClass = pb.getTopLevelClass(getOwnerClassDescriptor().getClassOfObject());
137 Class collectionClass = cds.getCollectionClass(); // this collection type will be used:
138 HashMap ownerIdsToLists = new HashMap(owners.size());
139
140 IdentityFactory identityFactory = pb.serviceIdentity();
141 // initialize the owner list map
142 for (Iterator it = owners.iterator(); it.hasNext();)
143 {
144 Object owner = it.next();
145 ownerIdsToLists.put(identityFactory.buildIdentity(getOwnerClassDescriptor(), owner), new ArrayList());
146 }
147
148 // build the children lists for the owners
149 for (Iterator it = children.iterator(); it.hasNext();)
150 {
151 Object child = it.next();
152 // BRJ: use cld for real class, relatedObject could be Proxy
153 ClassDescriptor cld = getDescriptorRepository().getDescriptorFor(ProxyHelper.getRealClass(child));
154
155 Object[] fkValues = cds.getForeignKeyValues(child, cld);
156 Identity ownerId = identityFactory.buildIdentity(null, ownerTopLevelClass, fkValues);
157 List list = (List) ownerIdsToLists.get(ownerId);
158 if (list != null)
159 {
160 list.add(child);
161 }
162 }
163
164 // connect children list to owners
165 for (Iterator it = owners.iterator(); it.hasNext();)
166 {
167 Object result;
168 Object owner = it.next();
169 Identity ownerId = identityFactory.buildIdentity(owner);
170 List list = (List) ownerIdsToLists.get(ownerId);
171
172 if ((collectionClass == null) && field.getType().isArray())
173 {
174 int length = list.size();
175 Class itemtype = field.getType().getComponentType();
176 result = Array.newInstance(itemtype, length);
177 for (int j = 0; j < length; j++)
178 {
179 Array.set(result, j, list.get(j));
180 }
181 }
182 else
183 {
184 ManageableCollection col = createCollection(cds, collectionClass);
185 for (Iterator it2 = list.iterator(); it2.hasNext();)
186 {
187 col.ojbAdd(it2.next());
188 }
189 result = col;
190 }
191
192 Object value = field.get(owner);
193 if ((value instanceof CollectionProxyDefaultImpl) && (result instanceof Collection))
194 {
195 ((CollectionProxyDefaultImpl) value).setData((Collection) result);
196 }
197 else
198 {
199 field.set(owner, result);
200 }
201 }
202 }
203
204 /**
205 * Create a collection object of the given collection type. If none has been given,
206 * OJB uses RemovalAwareList, RemovalAwareSet, or RemovalAwareCollection depending
207 * on the field type.
208 *
209 * @param desc The collection descriptor
210 * @param collectionClass The collection class specified in the collection-descriptor
211 * @return The collection object
212 */
213 protected ManageableCollection createCollection(CollectionDescriptor desc, Class collectionClass)
214 {
215 Class fieldType = desc.getPersistentField().getType();
216 ManageableCollection col;
217
218 if (collectionClass == null)
219 {
220 if (ManageableCollection.class.isAssignableFrom(fieldType))
221 {
222 try
223 {
224 col = (ManageableCollection)fieldType.newInstance();
225 }
226 catch (Exception e)
227 {
228 throw new OJBRuntimeException("Cannot instantiate the default collection type "+fieldType.getName()+" of collection "+desc.getAttributeName()+" in type "+desc.getClassDescriptor().getClassNameOfObject());
229 }
230 }
231 else if (fieldType.isAssignableFrom(RemovalAwareCollection.class))
232 {
233 col = new RemovalAwareCollection();
234 }
235 else if (fieldType.isAssignableFrom(RemovalAwareList.class))
236 {
237 col = new RemovalAwareList();
238 }
239 else if (fieldType.isAssignableFrom(RemovalAwareSet.class))
240 {
241 col = new RemovalAwareSet();
242 }
243 else
244 {
245 throw new MetadataException("Cannot determine a default collection type for collection "+desc.getAttributeName()+" in type "+desc.getClassDescriptor().getClassNameOfObject());
246 }
247 }
248 else
249 {
250 try
251 {
252 col = (ManageableCollection)collectionClass.newInstance();
253 }
254 catch (Exception e)
255 {
256 throw new OJBRuntimeException("Cannot instantiate the collection class "+collectionClass.getName()+" of collection "+desc.getAttributeName()+" in type "+desc.getClassDescriptor().getClassNameOfObject());
257 }
258 }
259 return col;
260 }
261
262 protected CollectionDescriptor getCollectionDescriptor()
263 {
264 return (CollectionDescriptor) getObjectReferenceDescriptor();
265 }
266 }