Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: org/objectstyle/cayenne/access/util/PrimaryKeyHelper.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.util.ArrayList;
60  import java.util.Collection;
61  import java.util.Comparator;
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.collections.ComparatorUtils;
68  import org.objectstyle.ashwood.graph.CollectionFactory;
69  import org.objectstyle.ashwood.graph.Digraph;
70  import org.objectstyle.ashwood.graph.GraphUtils;
71  import org.objectstyle.ashwood.graph.IndegreeTopologicalSort;
72  import org.objectstyle.ashwood.graph.MapDigraph;
73  import org.objectstyle.ashwood.graph.StrongConnection;
74  import org.objectstyle.cayenne.CayenneException;
75  import org.objectstyle.cayenne.CayenneRuntimeException;
76  import org.objectstyle.cayenne.DataObject;
77  import org.objectstyle.cayenne.ObjectId;
78  import org.objectstyle.cayenne.TempObjectId;
79  import org.objectstyle.cayenne.access.DataNode;
80  import org.objectstyle.cayenne.access.QueryEngine;
81  import org.objectstyle.cayenne.dba.PkGenerator;
82  import org.objectstyle.cayenne.map.DataMap;
83  import org.objectstyle.cayenne.map.DbAttribute;
84  import org.objectstyle.cayenne.map.DbEntity;
85  import org.objectstyle.cayenne.map.DbRelationship;
86  import org.objectstyle.cayenne.map.ObjAttribute;
87  import org.objectstyle.cayenne.map.ObjEntity;
88  import org.objectstyle.cayenne.map.ObjRelationship;
89  
90  /**
91   * PrimaryKeyHelper resolves primary key dependencies for entities
92   * related to the supported query engine via topological sorting. It is
93   * directly based on ASHWOOD. In addition it provides means for primary key
94   * generation relying on DbAdapter in this.
95   *
96   * @author Andriy Shapochka
97   */
98  
99  public class PrimaryKeyHelper {
100   private Map indexedDbEntities;
101   private QueryEngine queryEngine;
102   private DbEntityComparator dbEntityComparator;
103   private ObjEntityComparator objEntityComparator;
104 
105   public PrimaryKeyHelper(QueryEngine queryEngine) {
106     this.queryEngine = queryEngine;
107     init();
108     dbEntityComparator = new DbEntityComparator();
109     objEntityComparator = new ObjEntityComparator();
110   }
111 
112   public void reset() {
113     init();
114   }
115 
116   public Comparator getDbEntityComparator() {
117     return dbEntityComparator;
118   }
119 
120   public Comparator getObjEntityComparator() {
121     return objEntityComparator;
122   }
123 
124   public void createPermIdsForObjEntity(
125     ObjEntity objEntity,
126     List dataObjects)
127     throws CayenneException {
128       
129     if (dataObjects.isEmpty()) {
130       return;
131     }
132 
133     TempObjectId tempId =
134       (TempObjectId) ((DataObject) dataObjects.get(0)).getObjectId();
135 
136     DbEntity dbEntity = objEntity.getDbEntity();
137     DataNode owner = queryEngine.dataNodeForObjEntity(objEntity);
138     if (owner == null) {
139       throw new CayenneRuntimeException("No suitable DataNode to handle primary key generation.");
140     }
141       
142     PkGenerator pkGenerator = owner.getAdapter().getPkGenerator();
143     List pkAttributes = dbEntity.getPrimaryKey();
144 
145     HashMap idMap = null;
146     boolean pkFromMaster = true;
147     for (Iterator i = dataObjects.iterator(); i.hasNext();) {
148       idMap = new HashMap(idMap != null ? idMap.size() : 1);
149       DataObject object = (DataObject) i.next();
150       ObjectId id = object.getObjectId();
151       if (!(id instanceof TempObjectId)) {
152         continue; //with next loop
153         //If the id is not a temp, then it must be permanent.  Do nothing else
154       }
155       
156       tempId = (TempObjectId) id;
157       if (tempId.getPermId() != null) {
158         continue;
159         //An id already exists... nothing further required (definitely do not create another)
160       }
161       
162       // first get values delivered via relationships
163       if (pkFromMaster)
164         pkFromMaster =
165           appendPkFromMasterRelationships(
166             idMap,
167             object,
168             objEntity,
169             dbEntity);
170             
171       boolean autoPkDone = false;
172       Iterator it = pkAttributes.iterator();
173       while (it.hasNext()) {
174         DbAttribute dbAttr = (DbAttribute) it.next();
175         String dbAttrName = dbAttr.getName();
176         if (idMap.containsKey(dbAttrName)) {
177           continue;
178         }
179         
180         ObjAttribute objAttr =
181           objEntity.getAttributeForDbAttribute(dbAttr);
182         if (objAttr != null) {
183           idMap.put(
184             dbAttrName,
185             object.readPropertyDirectly(objAttr.getName()));
186           continue;
187         }
188         
189         if (autoPkDone) {
190           throw new CayenneException("Primary Key autogeneration only works for a single attribute.");
191         }
192         
193         // finally, use database generation mechanism
194         try {
195           Object pkValue =
196             pkGenerator.generatePkForDbEntity(owner, dbEntity);
197           idMap.put(dbAttrName, pkValue);
198           autoPkDone = true;
199         } catch (Exception ex) {
200           throw new CayenneException(
201             "Error generating PK: " + ex.getMessage(),
202             ex);
203         }
204       }
205       
206       // create permanent ObjectId and attach it to the temporary id
207       tempId.setPermId(new ObjectId(tempId.getObjClass(), idMap));
208     }
209   }
210 
211   private boolean appendPkFromMasterRelationships(
212     Map map,
213     DataObject dataObject,
214     ObjEntity objEntity,
215     DbEntity dbEntity)
216     throws CayenneException {
217     boolean useful = false;
218     Iterator it = dbEntity.getRelationshipMap().values().iterator();
219     while (it.hasNext()) {
220       DbRelationship dbRel = (DbRelationship) it.next();
221       if (!dbRel.isToMasterPK())
222         continue;
223 
224       ObjRelationship rel =
225         objEntity.getRelationshipForDbRelationship(dbRel);
226       if (rel == null)
227         continue;
228 
229       DataObject targetDo =
230         (DataObject) dataObject.readPropertyDirectly(rel.getName());
231 
232       if (targetDo == null)
233         throw new CayenneException("Null master object, can't create primary key for: "
234                       + dataObject.getClass() + "." + dbRel.getName());
235 
236       ObjectId targetKey = targetDo.getObjectId();
237       Map idMap = targetKey.getIdSnapshot();
238       if (idMap == null)
239         throw new CayenneException(
240           noMasterPkMsg(
241             objEntity.getName(),
242             targetKey.getObjClass().toString(),
243             dbRel.getName()));
244       map.putAll(dbRel.srcFkSnapshotWithTargetSnapshot(idMap));
245       useful = true;
246     }
247     return useful;
248   }
249 
250   private String noMasterPkMsg(String src, String dst, String rel) {
251     StringBuffer msg =
252       new StringBuffer("Can't create primary key, master object has no PK snapshot.");
253     msg
254       .append("\nrelationship name: ")
255       .append(rel)
256       .append(", src object: ")
257       .append(src)
258       .append(", target obj: ")
259       .append(dst);
260     return msg.toString();
261   }
262 
263   private List collectAllDbEntities() {
264     List entities = new ArrayList(32);
265     for (Iterator i = queryEngine.getDataMaps().iterator(); i.hasNext();) {
266       entities.addAll(((DataMap)i.next()).getDbEntities());
267     }
268     return entities;
269   }
270 
271   private void init() {
272     List dbEntitiesToResolve = collectAllDbEntities();
273     Digraph pkDependencyGraph = new MapDigraph(MapDigraph.HASHMAP_FACTORY);
274     indexedDbEntities = new HashMap(dbEntitiesToResolve.size());
275     for (Iterator i = dbEntitiesToResolve.iterator(); i.hasNext();) {
276       DbEntity origin = (DbEntity) i.next();
277       for (Iterator j = origin.getRelationships().iterator(); j.hasNext();) {
278         DbRelationship relation = (DbRelationship) j.next();
279         if (relation.isToDependentPK()) {
280           DbEntity dst = (DbEntity) relation.getTargetEntity();
281           if (origin.equals(dst)) {
282             continue;
283           }
284           pkDependencyGraph.putArc(origin, dst, Boolean.TRUE);
285         }
286       }
287     }
288     int index = 0;
289     for (Iterator i = dbEntitiesToResolve.iterator(); i.hasNext();) {
290       DbEntity entity = (DbEntity) i.next();
291       if (!pkDependencyGraph.containsVertex(entity)) {
292         indexedDbEntities.put(entity, new Integer(index++));
293       }
294     }
295     boolean acyclic = GraphUtils.isAcyclic(pkDependencyGraph);
296     if (acyclic) {
297       IndegreeTopologicalSort sorter =
298         new IndegreeTopologicalSort(pkDependencyGraph);
299       while (sorter.hasNext())
300         indexedDbEntities.put(sorter.next(), new Integer(index++));
301     } else {
302       StrongConnection contractor =
303         new StrongConnection(
304           pkDependencyGraph,
305           CollectionFactory.ARRAYLIST_FACTORY);
306       Digraph contractedDigraph =
307         new MapDigraph(MapDigraph.HASHMAP_FACTORY);
308       contractor.contract(
309         contractedDigraph,
310         CollectionFactory.ARRAYLIST_FACTORY);
311       IndegreeTopologicalSort sorter =
312         new IndegreeTopologicalSort(contractedDigraph);
313       while (sorter.hasNext()) {
314         Collection component = (Collection) sorter.next();
315         for (Iterator i = component.iterator(); i.hasNext();)
316           indexedDbEntities.put(i.next(), new Integer(index++));
317       }
318     }
319   }
320 
321   private class DbEntityComparator implements Comparator {
322     public int compare(Object o1, Object o2) {
323       if (o1.equals(o2)) {
324         return 0;
325       }
326       Integer index1 = (Integer) indexedDbEntities.get(o1);
327       Integer index2 = (Integer) indexedDbEntities.get(o2);
328       return ComparatorUtils.NATURAL_COMPARATOR.compare(index1, index2);
329     }
330   }
331 
332   private class ObjEntityComparator implements Comparator {
333     public int compare(Object o1, Object o2) {
334       if (o1.equals(o2)) {
335         return 0;
336       }
337       DbEntity e1 = ((ObjEntity) o1).getDbEntity();
338       DbEntity e2 = ((ObjEntity) o2).getDbEntity();
339       return dbEntityComparator.compare(e1, e2);
340     }
341   }
342 }