Source code: org/objectstyle/cayenne/access/util/PrefetchHelper.java
1 /* ====================================================================
2 *
3 * The ObjectStyle Group Software License, Version 1.0
4 *
5 * Copyright (c) 2002-2003 The ObjectStyle Group
6 * and individual authors of the software. All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 *
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in
17 * the documentation and/or other materials provided with the
18 * distribution.
19 *
20 * 3. The end-user documentation included with the redistribution, if
21 * any, must include the following acknowlegement:
22 * "This product includes software developed by the
23 * ObjectStyle Group (http://objectstyle.org/)."
24 * Alternately, this acknowlegement may appear in the software itself,
25 * if and wherever such third-party acknowlegements normally appear.
26 *
27 * 4. The names "ObjectStyle Group" and "Cayenne"
28 * must not be used to endorse or promote products derived
29 * from this software without prior written permission. For written
30 * permission, please contact andrus@objectstyle.org.
31 *
32 * 5. Products derived from this software may not be called "ObjectStyle"
33 * nor may "ObjectStyle" appear in their names without prior written
34 * permission of the ObjectStyle Group.
35 *
36 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
37 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
38 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
39 * DISCLAIMED. IN NO EVENT SHALL THE OBJECTSTYLE GROUP OR
40 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
41 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
42 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
43 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
44 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
45 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
46 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
47 * SUCH DAMAGE.
48 * ====================================================================
49 *
50 * This software consists of voluntary contributions made by many
51 * individuals on behalf of the ObjectStyle Group. For more
52 * information on the ObjectStyle Group, please see
53 * <http://objectstyle.org/>.
54 */
55 package org.objectstyle.cayenne.access.util;
56
57 import java.util.ArrayList;
58 import java.util.HashMap;
59 import java.util.List;
60 import java.util.ListIterator;
61 import java.util.Map;
62
63 import org.objectstyle.cayenne.CayenneRuntimeException;
64 import org.objectstyle.cayenne.DataObject;
65 import org.objectstyle.cayenne.ObjectId;
66 import org.objectstyle.cayenne.access.DataContext;
67 import org.objectstyle.cayenne.access.ToManyList;
68 import org.objectstyle.cayenne.exp.Expression;
69 import org.objectstyle.cayenne.exp.ExpressionFactory;
70 import org.objectstyle.cayenne.map.DbRelationship;
71 import org.objectstyle.cayenne.map.ObjEntity;
72 import org.objectstyle.cayenne.map.ObjRelationship;
73 import org.objectstyle.cayenne.query.SelectQuery;
74
75 /**
76 * @author Arndt Brenschede
77 */
78 public class PrefetchHelper {
79
80 /**
81 * Resolves a toOne relationship for a list of objects.
82 * (performance tuning only)
83 */
84 public static void resolveToOneRelations(
85 DataContext context,
86 List objects,
87 String relName) {
88 int nobjects = objects.size();
89 if (nobjects == 0)
90 return;
91
92 List oids = new ArrayList(nobjects);
93
94 for (int i = 0; i < nobjects; i++) {
95 DataObject sourceObject = (DataObject) objects.get(i);
96 DataObject targetObject =
97 (DataObject) sourceObject.readPropertyDirectly(relName);
98
99 ObjectId oid = targetObject.getObjectId();
100 oids.add(oid);
101 }
102 // this maybe suboptimal, cause it uses an OR .. OR .. OR .. expression
103 // instead of IN (..) - to be compatble with compound keys -
104 // however, it seems to be quite fast as well
105 SelectQuery sel = QueryUtils.selectQueryForIds(oids);
106 context.performQuery(sel);
107 }
108
109 /**
110 * Resolves a toMany relation for a list of objects.
111 * (performance tuning only)
112 *
113 * <p>WARNING: this is a bit of a hack - it works for my
114 * toMany's, but it possibly doesn't work in all cases.</p>
115 *
116 *
117 * <p>*** It definitly does not work for compound keys ***</p>
118 */
119 public static void resolveToManyRelations(
120 DataContext context,
121 List objects,
122 String relName) {
123
124 int nobjects = objects.size();
125 if (nobjects == 0)
126 return;
127
128 String dbKey = null;
129 Map listMap = new HashMap(nobjects);
130
131 // put the object-ids in a map for later assignment of the
132 // query results
133
134 for (int i = 0; i < nobjects; i++) {
135 DataObject object = (DataObject) objects.get(i);
136 ObjectId oid = object.getObjectId();
137 if (dbKey == null) {
138 Map id = oid.getIdSnapshot();
139 if (id.size() != 1) {
140 throw new CayenneRuntimeException("resolveToManyRelations expects single keys for now...");
141 }
142 dbKey = (String) id.keySet().iterator().next();
143 }
144 listMap.put(oid.getValueForAttribute(dbKey), new ArrayList());
145 }
146
147 ObjEntity ent =
148 context.getEntityResolver().lookupObjEntity(
149 (DataObject) objects.get(0));
150 ObjRelationship rel = (ObjRelationship) ent.getRelationship(relName);
151 ObjEntity destEnt = (ObjEntity) rel.getTargetEntity();
152
153 List dbRels = rel.getDbRelationships();
154
155 // sanity check
156 if (dbRels == null || dbRels.size() == 0) {
157 throw new CayenneRuntimeException(
158 "ObjRelationship '" + rel.getName() + "' is unmapped.");
159 }
160
161 // build a reverse DB path
162 // ...while reverse ObjRelationship may be absent,
163 // reverse DB must always be there...
164 StringBuffer buf = new StringBuffer();
165 ListIterator it = dbRels.listIterator(dbRels.size());
166 while (it.hasPrevious()) {
167 if (buf.length() > 0) {
168 buf.append(".");
169 }
170 DbRelationship dbRel = (DbRelationship) it.previous();
171 DbRelationship reverse = dbRel.getReverseRelationship();
172
173 // another sanity check
174 if (reverse == null) {
175 throw new CayenneRuntimeException(
176 "DbRelatitionship '"
177 + dbRel.getName()
178 + "' has no reverse relationship");
179 }
180
181 buf.append(reverse.getName());
182 }
183
184 // do the query
185 SelectQuery sel =
186 new SelectQuery(
187 destEnt,
188 ExpressionFactory.binaryDbPathExp(
189 Expression.IN,
190 buf.toString(),
191 ExpressionFactory.unaryExp(Expression.LIST, objects)));
192 sel.setFetchingDataRows(true);
193 List results = context.performQuery(sel);
194
195 // sort the resulting objects into individual lists for each source object
196 int nrows = results.size();
197 for (int k = 0; k < nrows; k++) {
198 Map row = (Map) results.get(k);
199 ((List) listMap.get(row.get(dbKey))).add(
200 context.objectFromDataRow(destEnt, row, false));
201 }
202
203 // and finally set these lists in the relation targets
204 for (int i = 0; i < nobjects; i++) {
205 DataObject object = (DataObject) objects.get(i);
206 ObjectId oid = object.getObjectId();
207 List list = (List) listMap.get(oid.getValueForAttribute(dbKey));
208
209 ((ToManyList) object.readPropertyDirectly(relName)).setObjectList(
210 list);
211 }
212 }
213 }