Source code: org/objectstyle/cayenne/access/util/BatchQueryUtils.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 */
56
57 package org.objectstyle.cayenne.access.util;
58
59 import java.sql.Types;
60 import java.util.Collection;
61 import java.util.Collections;
62 import java.util.HashMap;
63 import java.util.Iterator;
64 import java.util.List;
65 import java.util.Map;
66
67 import org.apache.commons.lang.builder.HashCodeBuilder;
68 import org.objectstyle.cayenne.DataObject;
69 import org.objectstyle.cayenne.map.DbAttribute;
70 import org.objectstyle.cayenne.map.DbAttributePair;
71 import org.objectstyle.cayenne.map.DbRelationship;
72 import org.objectstyle.cayenne.map.Entity;
73 import org.objectstyle.cayenne.map.ObjAttribute;
74 import org.objectstyle.cayenne.map.ObjEntity;
75 import org.objectstyle.cayenne.map.ObjRelationship;
76 import org.objectstyle.cayenne.query.BatchQuery;
77 import org.objectstyle.cayenne.query.InsertBatchQuery;
78 import org.objectstyle.cayenne.query.UpdateBatchQuery;
79
80 /**
81 * Collection of utility methods to work with BatchQueries.
82 *
83 * @author Andriy Shapochka
84 */
85
86 public class BatchQueryUtils {
87
88 private BatchQueryUtils() {
89 }
90
91 /**
92 * Utility method that returns <code>true</code> if
93 * the query will update at least one BLOB or CLOB DbAttribute.
94 */
95 public static boolean updatesLOBColumns(BatchQuery query) {
96 boolean isInsert = query instanceof InsertBatchQuery;
97 boolean isUpdate = query instanceof UpdateBatchQuery;
98
99 if (!isInsert && !isUpdate) {
100 return false;
101 }
102
103 List updatedAttributes =
104 (isInsert)
105 ? query.getDbAttributes()
106 : ((UpdateBatchQuery) query).getUpdatedDbAttributes();
107
108 Iterator it = updatedAttributes.iterator();
109 while (it.hasNext()) {
110 int type = ((DbAttribute) it.next()).getType();
111 if (type == Types.CLOB || type == Types.BLOB) {
112 return true;
113 }
114 }
115
116 return false;
117 }
118
119 public static Map buildSnapshotForUpdate(DataObject o) {
120 Map committedSnapshot = o.getCommittedSnapshot();
121 Map currentSnapshot = o.getCurrentSnapshot();
122 Map snapshot = null;
123
124 if (committedSnapshot == null || committedSnapshot.isEmpty()) {
125 snapshot = Collections.unmodifiableMap(currentSnapshot);
126 return snapshot;
127 } else
128 snapshot = new HashMap(currentSnapshot.size());
129
130 Iterator it = currentSnapshot.entrySet().iterator();
131 while (it.hasNext()) {
132 Map.Entry entry = (Map.Entry) it.next();
133 String attrName = (String) entry.getKey();
134 Object newValue = entry.getValue();
135 // if snapshot exists, compare old values and new values,
136 // only add attribute to the update clause if the value has changed
137 Object oldValue = committedSnapshot.get(attrName);
138 if (valueChanged(oldValue, newValue))
139 snapshot.put(attrName, newValue);
140 }
141
142 // original snapshot can have extra keys that are missing in current snapshot
143 // process those
144 Iterator origit = committedSnapshot.entrySet().iterator();
145 while (origit.hasNext()) {
146 Map.Entry entry = (Map.Entry) origit.next();
147 String attrName = (String) entry.getKey();
148 Object oldValue = entry.getValue();
149 if (oldValue == null || currentSnapshot.containsKey(attrName))
150 continue;
151 snapshot.put(attrName, null);
152 }
153
154 return Collections.unmodifiableMap(snapshot);
155 }
156
157 public static int hashCode(Collection c) {
158 HashCodeBuilder builder = new HashCodeBuilder();
159 for (Iterator i = c.iterator(); i.hasNext();)
160 builder.append(i.next());
161 return builder.toHashCode();
162 }
163
164 public static Map buildFlattenedSnapshot(
165 Map sourceId,
166 Map destinationId,
167 DbRelationship firstRelationship,
168 DbRelationship secondRelationship) {
169 Map snapshot = new HashMap(sourceId.size() + destinationId.size());
170 List joins = firstRelationship.getJoins();
171 for (int i = 0, numJoins = joins.size(); i < numJoins; i++) {
172 DbAttributePair thisJoin = (DbAttributePair) joins.get(i);
173 DbAttribute sourceAttribute = thisJoin.getSource();
174 DbAttribute targetAttribute = thisJoin.getTarget();
175 snapshot.put(
176 targetAttribute.getName(),
177 sourceId.get(sourceAttribute.getName()));
178 }
179
180 joins = secondRelationship.getJoins();
181 for (int i = 0, numJoins = joins.size(); i < numJoins; i++) {
182 DbAttributePair thisJoin = (DbAttributePair) joins.get(i);
183 DbAttribute sourceAttribute = thisJoin.getSource();
184 DbAttribute targetAttribute = thisJoin.getTarget();
185 snapshot.put(
186 sourceAttribute.getName(),
187 destinationId.get(targetAttribute.getName()));
188 }
189
190 return snapshot;
191 }
192
193 public static Map buildSnapshotForInsert(
194 ObjEntity ent,
195 DataObject o,
196 DbRelationship masterDependentRel) {
197 boolean isMasterDbEntity = (masterDependentRel == null);
198 Map map = new HashMap();
199
200 Map attrMap = ent.getAttributeMap();
201 Iterator it = attrMap.keySet().iterator();
202 while (it.hasNext()) {
203 String attrName = (String) it.next();
204 ObjAttribute objAttr = (ObjAttribute) attrMap.get(attrName);
205
206 if (isMasterDbEntity && !objAttr.isCompound()) {
207 map.put(
208 objAttr.getDbAttributePath(),
209 o.readPropertyDirectly(attrName));
210 } else if (!isMasterDbEntity && objAttr.isCompound()) {
211 DbAttribute dbAttr = objAttr.getDbAttribute();
212 if (dbAttr.getEntity() == masterDependentRel.getTargetEntity())
213 map.put(dbAttr.getName(), o.readPropertyDirectly(attrName));
214 }
215 }
216
217 if (isMasterDbEntity) {
218 Map relMap = ent.getRelationshipMap();
219 Iterator itr = relMap.keySet().iterator();
220 while (itr.hasNext()) {
221 String relName = (String) itr.next();
222 ObjRelationship rel = (ObjRelationship) relMap.get(relName);
223 // to-many will be handled on the other side
224 if (rel.isToMany())
225 continue;
226 if (rel.isToDependentEntity())
227 continue;
228 DataObject target =
229 (DataObject) o.readPropertyDirectly(relName);
230 if (target == null)
231 continue;
232 DbRelationship dbRel =
233 (DbRelationship) rel.getDbRelationships().get(0);
234 Map idParts = target.getObjectId().getIdSnapshot();
235 // this may happen in uncommitted objects
236 if (idParts == null)
237 continue;
238 Map fk = dbRel.srcFkSnapshotWithTargetSnapshot(idParts);
239 map.putAll(fk);
240 }
241 } else {
242 Map relMap = ent.getRelationshipMap();
243 Iterator itr = relMap.keySet().iterator();
244 while (itr.hasNext()) {
245 String relName = (String) itr.next();
246 ObjRelationship rel = (ObjRelationship) relMap.get(relName);
247 DbRelationship dbRel =
248 (DbRelationship) rel.getDbRelationships().get(1);
249 if (rel.isToMany())
250 continue;
251 if (dbRel.isToDependentPK())
252 continue;
253 DataObject target =
254 (DataObject) o.readPropertyDirectly(relName);
255 if (target == null)
256 continue;
257 if (dbRel.getSourceEntity()
258 != masterDependentRel.getTargetEntity())
259 continue;
260 Map idParts = target.getObjectId().getIdSnapshot();
261 // this may happen in uncommitted objects
262 if (idParts == null)
263 continue;
264 Map fk = dbRel.srcFkSnapshotWithTargetSnapshot(idParts);
265 map.putAll(fk);
266 }
267 }
268
269 // process object id map
270 // we should ignore any object id values if a corresponding attribute
271 // is a part of relationship "toMasterPK", since those values have been
272 // set above when db relationships where processed.
273 Map thisIdParts = o.getObjectId().getIdSnapshot();
274 if (thisIdParts != null) {
275 if (!isMasterDbEntity) {
276 thisIdParts =
277 masterDependentRel.targetPkSnapshotWithSrcSnapshot(
278 thisIdParts);
279 }
280 // put only thise that do not exist in the map
281 Iterator itm = thisIdParts.keySet().iterator();
282 while (itm.hasNext()) {
283 Object nextKey = itm.next();
284 if (!map.containsKey(nextKey))
285 map.put(nextKey, thisIdParts.get(nextKey));
286 }
287 }
288 return map;
289 }
290
291 // public static Map buildSnapshotForUpdate(ObjEntity ent,
292 // DataObject o,
293 // DbRelationship masterDependentRel) {
294 // boolean isMasterDbEntity = (masterDependentRel == null);
295 // Map committedSnapshot = o.getCommittedSnapshot();
296 // committedSnapshot = (committedSnapshot == null ||
297 // committedSnapshot.isEmpty() ?
298 // Collections.EMPTY_MAP :
299 // committedSnapshot);
300 // Map map = new HashMap();
301 //
302 // Map attrMap = ent.getAttributeMap();
303 // Iterator it = attrMap.keySet().iterator();
304 // while (it.hasNext()) {
305 // String attrName = (String) it.next();
306 // ObjAttribute objAttr = (ObjAttribute) attrMap.get(attrName);
307 //
308 // Object oldValue = committedSnapshot.get(objAttr.getDbAttributePath());
309 // Object newValue = o.readPropertyDirectly(attrName);
310 //
311 // // if snapshot exists, compare old values and new values,
312 // // only add attribute to the update clause if the value has changed
313 // if (!valueChanged(oldValue, newValue)) continue;
314 //
315 // if (isMasterDbEntity && !objAttr.isCompound()) {
316 // map.put(objAttr.getDbAttributePath(), newValue);
317 // } else if (!isMasterDbEntity && objAttr.isCompound()) {
318 // DbAttribute dbAttr = objAttr.getDbAttribute();
319 // if (dbAttr.getEntity() == masterDependentRel.getTargetEntity())
320 // map.put(dbAttr.getName(), newValue);
321 // }
322 // }
323 //
324 // if (isMasterDbEntity) {
325 // Map relMap = ent.getRelationshipMap();
326 // Iterator itr = relMap.keySet().iterator();
327 // while (itr.hasNext()) {
328 // String relName = (String) itr.next();
329 // ObjRelationship rel = (ObjRelationship) relMap.get(relName);
330 //
331 // // to-many will be handled on the other side
332 // if (rel.isToMany() || rel.isToDependentEntity()) continue;
333 //
334 // DbRelationship dbRel =
335 // (DbRelationship) rel.getDbRelationshipList().get(0);
336 //
337 // DataObject target = (DataObject) o.readPropertyDirectly(relName);
338 //
339 // if (target == null) {
340 // for (Iterator i = dbRel.getJoins().iterator(); i.hasNext(); ) {
341 // DbAttributePair join = (DbAttributePair)i.next();
342 // String dbAttrName = join.getSource().getName();
343 // if (committedSnapshot.get(dbAttrName) != null) {
344 // map.put(dbAttrName, null);
345 // }
346 // }
347 // continue;
348 // }
349 //
350 // Map idParts = target.getObjectId().getIdSnapshot();
351 //
352 // // this may happen in uncommitted objects
353 // if (idParts == null) {
354 // continue;
355 // }
356 //
357 // Map fk = dbRel.srcFkSnapshotWithTargetSnapshot(idParts);
358 // for (Iterator i = fk.entrySet().iterator(); i.hasNext(); ) {
359 // Map.Entry entry = (Map.Entry)i.next();
360 // Object key = entry.getKey();
361 // Object oldValue = committedSnapshot.get(key);
362 // Object newValue = entry.getValue();
363 //
364 // // if snapshot exists, compare old values and new values,
365 // // only add attribute to the update clause if the value has changed
366 // if (!valueChanged(oldValue, newValue)) continue;
367 //
368 // map.put(key, newValue);
369 // }
370 // }
371 // }
372 //
373 // // process object id map
374 // // we should ignore any object id values if a corresponding attribute
375 // // is a part of relationship "toMasterPK", since those values have been
376 // // set above when db relationships where processed.
377 // Map thisIdParts = o.getObjectId().getIdSnapshot();
378 // if (thisIdParts != null) {
379 // Map id = (isMasterDbEntity ?
380 // thisIdParts :
381 // masterDependentRel.
382 // targetPkSnapshotWithSrcSnapshot(thisIdParts));
383 // // put only thise that do not exist in the map
384 // Iterator itm = id.keySet().iterator();
385 // while (itm.hasNext()) {
386 // Object nextKey = itm.next();
387 // Object newValue = id.get(nextKey);
388 // if (!map.containsKey(nextKey)) {
389 // Object committedKey = (isMasterDbEntity ?
390 // nextKey :
391 // getSrcDbAttributeName((String)nextKey, masterDependentRel));
392 // Object oldValue = committedSnapshot.get(committedKey);
393 // if (!valueChanged(oldValue, newValue)) continue;
394 // map.put(nextKey, newValue);
395 // }
396 // }
397 // }
398 // return map;
399 // }
400
401 private static boolean valueChanged(Object oldValue, Object newValue) {
402 return (
403 (newValue == null && oldValue != null)
404 || (newValue != null && !newValue.equals(oldValue)));
405 }
406
407 /*
408 private static String getSrcDbAttributeName(
409 String targetDbAttributeName,
410 DbRelationship masterDependentRel) {
411 for (Iterator i = masterDependentRel.getJoins().iterator(); i.hasNext(); ) {
412 DbAttributePair join = (DbAttributePair)i.next();
413 if (targetDbAttributeName.equals(join.getTarget().getName()))
414 return join.getSource().getName();
415 }
416 return null;
417 }
418 */
419
420 private static String getTargetDbAttributeName(
421 String srcDbAttributeName,
422 DbRelationship masterDependentRel) {
423 for (Iterator i = masterDependentRel.getJoins().iterator();
424 i.hasNext();
425 ) {
426 DbAttributePair join = (DbAttributePair) i.next();
427 if (srcDbAttributeName.equals(join.getSource().getName()))
428 return join.getTarget().getName();
429 }
430 return null;
431 }
432
433 public static Map buildSnapshotForUpdate(
434 ObjEntity entity,
435 DataObject o,
436 DbRelationship masterDependentRel) {
437 boolean isMasterDbEntity = (masterDependentRel == null);
438 Map committedSnapshot = o.getCommittedSnapshot();
439 Map currentSnapshot = o.getCurrentSnapshot();
440 Map snapshot = new HashMap(currentSnapshot.size());
441
442 if (committedSnapshot == null || committedSnapshot.isEmpty()) {
443 for (Iterator i = currentSnapshot.entrySet().iterator();
444 i.hasNext();
445 ) {
446 Map.Entry entry = (Map.Entry) i.next();
447 String dbAttrPath = (String) entry.getKey();
448 boolean compoundDbAttr =
449 dbAttrPath.indexOf(Entity.PATH_SEPARATOR) > 0;
450 Object newValue = entry.getValue();
451 if (isMasterDbEntity && !compoundDbAttr) {
452 snapshot.put(dbAttrPath, newValue);
453 } else if (!isMasterDbEntity && compoundDbAttr) {
454 Iterator pathIterator =
455 entity.getDbEntity().resolvePathComponents(dbAttrPath);
456 if (pathIterator.hasNext()
457 && masterDependentRel.equals(pathIterator.next())) {
458 DbAttribute dbAttr = (DbAttribute) pathIterator.next();
459 snapshot.put(dbAttr.getName(), newValue);
460 }
461 } else if (!isMasterDbEntity && !compoundDbAttr) {
462 String pkAttrName =
463 getTargetDbAttributeName(
464 dbAttrPath,
465 masterDependentRel);
466 if (pkAttrName != null)
467 snapshot.put(pkAttrName, newValue);
468 }
469 }
470 return Collections.unmodifiableMap(snapshot);
471 }
472
473 Iterator it = currentSnapshot.entrySet().iterator();
474 while (it.hasNext()) {
475 Map.Entry entry = (Map.Entry) it.next();
476 String dbAttrPath = (String) entry.getKey();
477 boolean compoundDbAttr =
478 dbAttrPath.indexOf(Entity.PATH_SEPARATOR) > 0;
479 Object newValue = entry.getValue();
480 // if snapshot exists, compare old values and new values,
481 // only add attribute to the update clause if the value has changed
482 Object oldValue = committedSnapshot.get(dbAttrPath);
483 if (valueChanged(oldValue, newValue)) {
484 if (isMasterDbEntity && !compoundDbAttr) {
485 snapshot.put(dbAttrPath, newValue);
486 } else if (!isMasterDbEntity && compoundDbAttr) {
487 Iterator pathIterator =
488 entity.getDbEntity().resolvePathComponents(dbAttrPath);
489 if (pathIterator.hasNext()
490 && masterDependentRel.equals(pathIterator.next())) {
491 DbAttribute dbAttr = (DbAttribute) pathIterator.next();
492 snapshot.put(dbAttr.getName(), newValue);
493 }
494 } else if (!isMasterDbEntity && !compoundDbAttr) {
495 String pkAttrName =
496 getTargetDbAttributeName(
497 dbAttrPath,
498 masterDependentRel);
499 if (pkAttrName != null)
500 snapshot.put(pkAttrName, newValue);
501 }
502 }
503 }
504
505 // original snapshot can have extra keys that are missing in current snapshot
506 // process those
507 Iterator origit = committedSnapshot.entrySet().iterator();
508 while (origit.hasNext()) {
509 Map.Entry entry = (Map.Entry) origit.next();
510 String dbAttrPath = (String) entry.getKey();
511 boolean compoundDbAttr =
512 dbAttrPath.indexOf(Entity.PATH_SEPARATOR) > 0;
513 Object oldValue = entry.getValue();
514 if (oldValue == null || currentSnapshot.containsKey(dbAttrPath))
515 continue;
516 if (isMasterDbEntity && !compoundDbAttr) {
517 snapshot.put(dbAttrPath, null);
518 } else if (!isMasterDbEntity && compoundDbAttr) {
519 Iterator pathIterator =
520 entity.getDbEntity().resolvePathComponents(dbAttrPath);
521 if (pathIterator.hasNext()
522 && masterDependentRel.equals(pathIterator.next())) {
523 DbAttribute dbAttr = (DbAttribute) pathIterator.next();
524 snapshot.put(dbAttr.getName(), null);
525 }
526 } else if (!isMasterDbEntity && !compoundDbAttr) {
527 String pkAttrName =
528 getTargetDbAttributeName(dbAttrPath, masterDependentRel);
529 if (pkAttrName != null)
530 snapshot.put(pkAttrName, null);
531 }
532 }
533 return Collections.unmodifiableMap(snapshot);
534 }
535 }