1 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.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 97 public class PrimaryKeyHelper { 98 99 private Map 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 getDbEntityComparator() { 116 return dbEntityComparator; 117 } 118 119 public Comparator getObjEntityComparator() { 120 return objEntityComparator; 121 } 122 123 public void createPermIdsForObjEntity(ObjEntity objEntity, List 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 pkAttributes = dbEntity.getPrimaryKey(); 140 141 boolean pkFromMaster = true; 142 Iterator 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 Map idMap = id.getReplacementIdMap(); 153 154 if (pkFromMaster) { 156 pkFromMaster = appendPkFromMasterRelationships( 157 idMap, 158 object, 159 objEntity, 160 dbEntity, 161 supportsGeneratedKeys); 162 } 163 164 boolean autoPkDone = false; 165 Iterator it = pkAttributes.iterator(); 166 while (it.hasNext()) { 167 DbAttribute dbAttr = (DbAttribute) it.next(); 168 String dbAttrName = dbAttr.getName(); 169 170 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 if (autoPkDone) { 188 throw new CayenneException( 189 "Primary Key autogeneration only works for a single attribute."); 190 } 191 192 try { 194 Object pkValue = pkGenerator.generatePkForDbEntity(owner, dbEntity); 195 idMap.put(dbAttrName, pkValue); 196 autoPkDone = true; 197 } 198 catch (Exception ex) { 199 throw new CayenneException( 200 "Error generating PK: " + ex.getMessage(), 201 ex); 202 } 203 } 204 } 205 } 206 207 private boolean appendPkFromMasterRelationships( 208 Map idMap, 209 DataObject dataObject, 210 ObjEntity objEntity, 211 DbEntity dbEntity, 212 boolean supportsGeneratedKeys) throws CayenneException { 213 214 boolean useful = false; 215 Iterator 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 targetKeyMap = targetKey.getIdSnapshot(); 240 if (targetKeyMap == null) { 241 throw new CayenneException(noMasterPkMsg(objEntity.getName(), targetKey 242 .getObjectClass() 243 .toString(), dbRel.getName())); 244 } 245 246 251 Iterator joins = dbRel.getJoins().iterator(); 252 while (joins.hasNext()) { 253 DbJoin join = (DbJoin) joins.next(); 254 Object value = targetKeyMap.get(join.getTargetName()); 255 if (value == null) { 256 if (supportsGeneratedKeys && join.getTarget().isGenerated()) { 257 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 noMasterPkMsg(String src, String dst, String rel) { 275 StringBuffer msg = new StringBuffer ( 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 collectAllDbEntities() { 283 List entities = new ArrayList (32); 284 for (Iterator 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 dbEntitiesToResolve = collectAllDbEntities(); 292 Digraph pkDependencyGraph = new MapDigraph(MapDigraph.HASHMAP_FACTORY); 293 indexedDbEntities = new HashMap (dbEntitiesToResolve.size()); 294 for (Iterator i = dbEntitiesToResolve.iterator(); i.hasNext();) { 295 DbEntity origin = (DbEntity) i.next(); 296 for (Iterator 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 i = dbEntitiesToResolve.iterator(); i.hasNext();) { 309 DbEntity entity = (DbEntity) i.next(); 310 if (!pkDependencyGraph.containsVertex(entity)) { 311 indexedDbEntities.put(entity, new Integer (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 (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 component = (Collection ) sorter.next(); 331 for (Iterator i = component.iterator(); i.hasNext();) 332 indexedDbEntities.put(i.next(), new Integer (index++)); 333 } 334 } 335 } 336 337 private class DbEntityComparator implements Comparator { 338 339 public int compare(Object o1, Object o2) { 340 if (o1.equals(o2)) { 341 return 0; 342 } 343 Integer index1 = (Integer ) indexedDbEntities.get(o1); 344 Integer index2 = (Integer ) indexedDbEntities.get(o2); 345 return ComparatorUtils.NATURAL_COMPARATOR.compare(index1, index2); 346 } 347 } 348 349 private class ObjEntityComparator implements Comparator { 350 351 public int compare(Object o1, Object 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 |