KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > objectstyle > cayenne > access > util > PrimaryKeyHelper


1 /* ====================================================================
2  *
3  * The ObjectStyle Group Software License, version 1.1
4  * ObjectStyle Group - http://objectstyle.org/
5  *
6  * Copyright (c) 2002-2005, Andrei (Andrus) Adamchik and individual authors
7  * of the software. All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  *
13  * 1. Redistributions of source code must retain the above copyright
14  * notice, this list of conditions and the following disclaimer.
15  *
16  * 2. Redistributions in binary form must reproduce the above copyright
17  * notice, this list of conditions and the following disclaimer in
18  * the documentation and/or other materials provided with the
19  * distribution.
20  *
21  * 3. The end-user documentation included with the redistribution, if any,
22  * must include the following acknowlegement:
23  * "This product includes software developed by independent contributors
24  * and hosted on ObjectStyle Group web site (http://objectstyle.org/)."
25  * Alternately, this acknowlegement may appear in the software itself,
26  * if and wherever such third-party acknowlegements normally appear.
27  *
28  * 4. The names "ObjectStyle Group" and "Cayenne" must not be used to endorse
29  * or promote products derived from this software without prior written
30  * permission. For written permission, email
31  * "andrus at objectstyle dot org".
32  *
33  * 5. Products derived from this software may not be called "ObjectStyle"
34  * or "Cayenne", nor may "ObjectStyle" or "Cayenne" appear in their
35  * names without prior written permission.
36  *
37  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
38  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
39  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
40  * DISCLAIMED. IN NO EVENT SHALL THE OBJECTSTYLE GROUP OR
41  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
42  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
43  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
44  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
45  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
46  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
47  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
48  * SUCH DAMAGE.
49  * ====================================================================
50  *
51  * This software consists of voluntary contributions made by many
52  * individuals and hosted on ObjectStyle Group web site. For more
53  * information on the ObjectStyle Group, please see
54  * <http://objectstyle.org/>.
55  */

56
57 package org.objectstyle.cayenne.access.util;
58
59 import java.util.ArrayList JavaDoc;
60 import java.util.Collection JavaDoc;
61 import java.util.Comparator JavaDoc;
62 import java.util.HashMap JavaDoc;
63 import java.util.Iterator JavaDoc;
64 import java.util.List JavaDoc;
65 import java.util.Map JavaDoc;
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.access.DataNode;
79 import org.objectstyle.cayenne.access.QueryEngine;
80 import org.objectstyle.cayenne.dba.PkGenerator;
81 import org.objectstyle.cayenne.map.DataMap;
82 import org.objectstyle.cayenne.map.DbAttribute;
83 import org.objectstyle.cayenne.map.DbEntity;
84 import org.objectstyle.cayenne.map.DbJoin;
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 related to the
92  * supported query engine via topological sorting. It is directly based on ASHWOOD. In
93  * addition it provides means for primary key generation relying on DbAdapter in this.
94  *
95  * @author Andriy Shapochka
96  */

97 public class PrimaryKeyHelper {
98
99     private Map JavaDoc indexedDbEntities;
100     private QueryEngine queryEngine;
101     private DbEntityComparator dbEntityComparator;
102     private ObjEntityComparator objEntityComparator;
103
104     public PrimaryKeyHelper(QueryEngine queryEngine) {
105         this.queryEngine = queryEngine;
106         init();
107         dbEntityComparator = new DbEntityComparator();
108         objEntityComparator = new ObjEntityComparator();
109     }
110
111     public void reset() {
112         init();
113     }
114
115     public Comparator JavaDoc getDbEntityComparator() {
116         return dbEntityComparator;
117     }
118
119     public Comparator JavaDoc getObjEntityComparator() {
120         return objEntityComparator;
121     }
122
123     public void createPermIdsForObjEntity(ObjEntity objEntity, List JavaDoc dataObjects)
124             throws CayenneException {
125
126         if (dataObjects.isEmpty()) {
127             return;
128         }
129
130         DbEntity dbEntity = objEntity.getDbEntity();
131         DataNode owner = queryEngine.lookupDataNode(objEntity.getDataMap());
132         if (owner == null) {
133             throw new CayenneRuntimeException(
134                     "No suitable DataNode to handle primary key generation.");
135         }
136
137         PkGenerator pkGenerator = owner.getAdapter().getPkGenerator();
138         boolean supportsGeneratedKeys = owner.getAdapter().supportsGeneratedKeys();
139         List JavaDoc pkAttributes = dbEntity.getPrimaryKey();
140
141         boolean pkFromMaster = true;
142         Iterator JavaDoc i = dataObjects.iterator();
143         while (i.hasNext()) {
144
145             DataObject object = (DataObject) i.next();
146             ObjectId id = object.getObjectId();
147             if (id == null || !id.isTemporary()) {
148                 continue;
149             }
150
151             // modify replacement id directly...
152
Map JavaDoc idMap = id.getReplacementIdMap();
153
154             // first get values delivered via relationships
155
if (pkFromMaster) {
156                 pkFromMaster = appendPkFromMasterRelationships(
157                         idMap,
158                         object,
159                         objEntity,
160                         dbEntity,
161                         supportsGeneratedKeys);
162             }
163
164             boolean autoPkDone = false;
165             Iterator JavaDoc it = pkAttributes.iterator();
166             while (it.hasNext()) {
167                 DbAttribute dbAttr = (DbAttribute) it.next();
168                 String JavaDoc dbAttrName = dbAttr.getName();
169
170                 // skip generated keys...
171
if (supportsGeneratedKeys && dbAttr.isGenerated()) {
172                     continue;
173                 }
174
175                 if (idMap.containsKey(dbAttrName)) {
176                     continue;
177                 }
178
179                 ObjAttribute objAttr = objEntity.getAttributeForDbAttribute(dbAttr);
180                 if (objAttr != null) {
181                     idMap.put(dbAttrName, object.readPropertyDirectly(objAttr.getName()));
182                     continue;
183                 }
184
185                 // only a single key can be generated from DB... if this is done already
186
// in this loop, we must bail out.
187
if (autoPkDone) {
188                     throw new CayenneException(
189                             "Primary Key autogeneration only works for a single attribute.");
190                 }
191
192                 // finally, use database generation mechanism
193
try {
194                     Object JavaDoc pkValue = pkGenerator.generatePkForDbEntity(owner, dbEntity);
195                     idMap.put(dbAttrName, pkValue);
196                     autoPkDone = true;
197                 }
198                 catch (Exception JavaDoc ex) {
199                     throw new CayenneException(
200                             "Error generating PK: " + ex.getMessage(),
201                             ex);
202                 }
203             }
204         }
205     }
206
207     private boolean appendPkFromMasterRelationships(
208             Map JavaDoc idMap,
209             DataObject dataObject,
210             ObjEntity objEntity,
211             DbEntity dbEntity,
212             boolean supportsGeneratedKeys) throws CayenneException {
213
214         boolean useful = false;
215         Iterator JavaDoc it = dbEntity.getRelationships().iterator();
216         while (it.hasNext()) {
217             DbRelationship dbRel = (DbRelationship) it.next();
218             if (!dbRel.isToMasterPK()) {
219                 continue;
220             }
221
222             ObjRelationship rel = objEntity.getRelationshipForDbRelationship(dbRel);
223             if (rel == null) {
224                 continue;
225             }
226
227             DataObject targetDo = (DataObject) dataObject.readPropertyDirectly(rel
228                     .getName());
229
230             if (targetDo == null) {
231                 throw new CayenneException(
232                         "Null master object, can't create primary key for: "
233                                 + dataObject.getClass()
234                                 + "."
235                                 + dbRel.getName());
236             }
237
238             ObjectId targetKey = targetDo.getObjectId();
239             Map JavaDoc targetKeyMap = targetKey.getIdSnapshot();
240             if (targetKeyMap == null) {
241                 throw new CayenneException(noMasterPkMsg(objEntity.getName(), targetKey
242                         .getObjectClass()
243                         .toString(), dbRel.getName()));
244             }
245
246             // DbRelationship logic currently throws an exception when some key is
247
// missing... so have to implement a similar code here that is more
248
// tolerant to the deferred keys.
249
// TODO: maybe merge this back to DbRel?
250

251             Iterator JavaDoc joins = dbRel.getJoins().iterator();
252             while (joins.hasNext()) {
253                 DbJoin join = (DbJoin) joins.next();
254                 Object JavaDoc value = targetKeyMap.get(join.getTargetName());
255                 if (value == null) {
256                     if (supportsGeneratedKeys && join.getTarget().isGenerated()) {
257                         // ignore
258
continue;
259                     }
260
261                     throw new CayenneRuntimeException(
262                             "Some parts of FK are missing in snapshot, join: " + join);
263                 }
264
265                 idMap.put(join.getSourceName(), value);
266             }
267
268             useful = true;
269         }
270
271         return useful;
272     }
273
274     private String JavaDoc noMasterPkMsg(String JavaDoc src, String JavaDoc dst, String JavaDoc rel) {
275         StringBuffer JavaDoc msg = new StringBuffer JavaDoc(
276                 "Can't create primary key, master object has no PK snapshot.");
277         msg.append("\nrelationship name: ").append(rel).append(", src object: ").append(
278                 src).append(", target obj: ").append(dst);
279         return msg.toString();
280     }
281
282     private List JavaDoc collectAllDbEntities() {
283         List JavaDoc entities = new ArrayList JavaDoc(32);
284         for (Iterator JavaDoc i = queryEngine.getDataMaps().iterator(); i.hasNext();) {
285             entities.addAll(((DataMap) i.next()).getDbEntities());
286         }
287         return entities;
288     }
289
290     private void init() {
291         List JavaDoc dbEntitiesToResolve = collectAllDbEntities();
292         Digraph pkDependencyGraph = new MapDigraph(MapDigraph.HASHMAP_FACTORY);
293         indexedDbEntities = new HashMap JavaDoc(dbEntitiesToResolve.size());
294         for (Iterator JavaDoc i = dbEntitiesToResolve.iterator(); i.hasNext();) {
295             DbEntity origin = (DbEntity) i.next();
296             for (Iterator JavaDoc j = origin.getRelationships().iterator(); j.hasNext();) {
297                 DbRelationship relation = (DbRelationship) j.next();
298                 if (relation.isToDependentPK()) {
299                     DbEntity dst = (DbEntity) relation.getTargetEntity();
300                     if (origin.equals(dst)) {
301                         continue;
302                     }
303                     pkDependencyGraph.putArc(origin, dst, Boolean.TRUE);
304                 }
305             }
306         }
307         int index = 0;
308         for (Iterator JavaDoc i = dbEntitiesToResolve.iterator(); i.hasNext();) {
309             DbEntity entity = (DbEntity) i.next();
310             if (!pkDependencyGraph.containsVertex(entity)) {
311                 indexedDbEntities.put(entity, new Integer JavaDoc(index++));
312             }
313         }
314         boolean acyclic = GraphUtils.isAcyclic(pkDependencyGraph);
315         if (acyclic) {
316             IndegreeTopologicalSort sorter = new IndegreeTopologicalSort(
317                     pkDependencyGraph);
318             while (sorter.hasNext())
319                 indexedDbEntities.put(sorter.next(), new Integer JavaDoc(index++));
320         }
321         else {
322             StrongConnection contractor = new StrongConnection(
323                     pkDependencyGraph,
324                     CollectionFactory.ARRAYLIST_FACTORY);
325             Digraph contractedDigraph = new MapDigraph(MapDigraph.HASHMAP_FACTORY);
326             contractor.contract(contractedDigraph, CollectionFactory.ARRAYLIST_FACTORY);
327             IndegreeTopologicalSort sorter = new IndegreeTopologicalSort(
328                     contractedDigraph);
329             while (sorter.hasNext()) {
330                 Collection JavaDoc component = (Collection JavaDoc) sorter.next();
331                 for (Iterator JavaDoc i = component.iterator(); i.hasNext();)
332                     indexedDbEntities.put(i.next(), new Integer JavaDoc(index++));
333             }
334         }
335     }
336
337     private class DbEntityComparator implements Comparator JavaDoc {
338
339         public int compare(Object JavaDoc o1, Object JavaDoc o2) {
340             if (o1.equals(o2)) {
341                 return 0;
342             }
343             Integer JavaDoc index1 = (Integer JavaDoc) indexedDbEntities.get(o1);
344             Integer JavaDoc index2 = (Integer JavaDoc) indexedDbEntities.get(o2);
345             return ComparatorUtils.NATURAL_COMPARATOR.compare(index1, index2);
346         }
347     }
348
349     private class ObjEntityComparator implements Comparator JavaDoc {
350
351         public int compare(Object JavaDoc o1, Object JavaDoc o2) {
352             if (o1.equals(o2)) {
353                 return 0;
354             }
355             DbEntity e1 = ((ObjEntity) o1).getDbEntity();
356             DbEntity e2 = ((ObjEntity) o2).getDbEntity();
357             return dbEntityComparator.compare(e1, e2);
358         }
359     }
360 }
Popular Tags