1 19 20 package org.apache.cayenne.access; 21 22 import java.util.ArrayList ; 23 import java.util.Collection ; 24 import java.util.Collections ; 25 import java.util.HashMap ; 26 import java.util.Iterator ; 27 import java.util.List ; 28 import java.util.Map ; 29 30 import org.apache.cayenne.CayenneException; 31 import org.apache.cayenne.CayenneRuntimeException; 32 import org.apache.cayenne.DataRow; 33 import org.apache.cayenne.ObjectContext; 34 import org.apache.cayenne.ObjectId; 35 import org.apache.cayenne.Persistent; 36 import org.apache.cayenne.QueryResponse; 37 import org.apache.cayenne.cache.QueryCache; 38 import org.apache.cayenne.cache.QueryCacheEntryFactory; 39 import org.apache.cayenne.map.DataMap; 40 import org.apache.cayenne.map.DbRelationship; 41 import org.apache.cayenne.map.ObjEntity; 42 import org.apache.cayenne.map.ObjRelationship; 43 import org.apache.cayenne.query.ObjectIdQuery; 44 import org.apache.cayenne.query.PrefetchSelectQuery; 45 import org.apache.cayenne.query.PrefetchTreeNode; 46 import org.apache.cayenne.query.Query; 47 import org.apache.cayenne.query.QueryMetadata; 48 import org.apache.cayenne.query.QueryRouter; 49 import org.apache.cayenne.query.RefreshQuery; 50 import org.apache.cayenne.query.RelationshipQuery; 51 import org.apache.cayenne.query.SQLResultSetMapping; 52 import org.apache.cayenne.reflect.ClassDescriptor; 53 import org.apache.cayenne.util.GenericResponse; 54 import org.apache.cayenne.util.ListResponse; 55 import org.apache.cayenne.util.Util; 56 import org.apache.commons.collections.Transformer; 57 58 65 class DataDomainQueryAction implements QueryRouter, OperationObserver { 66 67 static final boolean DONE = true; 68 69 DataContext context; 70 DataDomain domain; 71 DataRowStore cache; 72 Query query; 73 QueryMetadata metadata; 74 75 QueryResponse response; 76 GenericResponse fullResponse; 77 Map prefetchResultsByPath; 78 Map queriesByNode; 79 Map queriesByExecutedQueries; 80 boolean noObjectConversion; 81 82 86 DataDomainQueryAction(ObjectContext context, DataDomain domain, Query query) { 87 if (context != null && !(context instanceof DataContext)) { 88 throw new IllegalArgumentException ( 89 "DataDomain can only work with DataContext. " 90 + "Unsupported context type: " 91 + context); 92 } 93 94 this.domain = domain; 95 this.query = query; 96 this.metadata = query.getMetaData(domain.getEntityResolver()); 97 this.context = (DataContext) context; 98 99 if (context != null) { 101 this.cache = this.context.getObjectStore().getDataRowCache(); 102 } 103 104 if (this.cache == null) { 105 this.cache = domain.getSharedSnapshotCache(); 106 } 107 } 108 109 QueryResponse execute() { 110 111 if (interceptOIDQuery() != DONE) { 113 if (interceptRelationshipQuery() != DONE) { 114 if (interceptRefreshQuery() != DONE) { 115 if (interceptSharedCache() != DONE) { 116 if (interceptDataDomainQuery() != DONE) { 117 runQueryInTransaction(); 118 } 119 } 120 } 121 } 122 } 123 124 if (!noObjectConversion) { 125 126 if (interceptMappedConversion() != DONE) { 127 interceptObjectConversion(); 128 } 129 } 130 131 return response; 132 } 133 134 private boolean interceptDataDomainQuery() { 135 if (query instanceof DataDomainQuery) { 136 response = new ListResponse(domain); 137 return DONE; 138 } 139 140 return !DONE; 141 } 142 143 private boolean interceptOIDQuery() { 144 if (query instanceof ObjectIdQuery) { 145 146 ObjectIdQuery oidQuery = (ObjectIdQuery) query; 147 148 DataRow row = null; 149 150 if (!oidQuery.isFetchMandatory()) { 151 row = cache.getCachedSnapshot(oidQuery.getObjectId()); 152 } 153 154 if (row == null) { 156 157 if (oidQuery.isFetchAllowed()) { 158 159 runQueryInTransaction(); 160 } 161 else { 162 response = new ListResponse(); 163 } 164 } 165 else { 166 response = new ListResponse(row); 167 } 168 169 return DONE; 170 } 171 172 return !DONE; 173 } 174 175 private boolean interceptRelationshipQuery() { 176 177 if (query instanceof RelationshipQuery) { 178 179 RelationshipQuery relationshipQuery = (RelationshipQuery) query; 180 if (relationshipQuery.isRefreshing()) { 181 return !DONE; 182 } 183 184 ObjRelationship relationship = relationshipQuery.getRelationship(domain 185 .getEntityResolver()); 186 187 if (relationship.isSourceIndependentFromTargetChange()) { 190 return !DONE; 191 } 192 193 DataRow sourceRow = cache.getCachedSnapshot(relationshipQuery.getObjectId()); 194 195 if (sourceRow == null) { 196 return !DONE; 197 } 198 199 DbRelationship dbRelationship = (DbRelationship) relationship 203 .getDbRelationships() 204 .get(0); 205 206 ObjectId targetId = sourceRow.createTargetObjectId(relationship 207 .getTargetEntityName(), dbRelationship); 208 209 if (targetId == null) { 211 this.response = new GenericResponse(Collections.EMPTY_LIST); 212 return DONE; 213 } 214 215 DataRow targetRow = cache.getCachedSnapshot(targetId); 216 217 if (targetRow != null) { 218 this.response = new GenericResponse(Collections.singletonList(targetRow)); 219 return DONE; 220 } 221 else if (context != null 224 && domain.getEntityResolver().lookupInheritanceTree( 225 (ObjEntity) relationship.getTargetEntity()) == null) { 226 227 this.noObjectConversion = true; 228 Object object = context.localObject(targetId, null); 229 this.response = new GenericResponse(Collections.singletonList(object)); 230 return DONE; 231 } 232 } 233 234 return !DONE; 235 } 236 237 240 private boolean interceptRefreshQuery() { 241 242 if (query instanceof RefreshQuery) { 243 RefreshQuery refreshQuery = (RefreshQuery) query; 244 245 if (refreshQuery.isRefreshAll()) { 246 247 domain.getSharedSnapshotCache().clear(); 249 context.getQueryCache().clear(); 250 251 GenericResponse response = new GenericResponse(); 252 response.addUpdateCount(1); 253 this.response = response; 254 return DONE; 255 } 256 257 Collection objects = refreshQuery.getObjects(); 258 if (objects != null && !objects.isEmpty()) { 259 260 Collection ids = new ArrayList (objects.size()); 261 Iterator it = objects.iterator(); 262 while (it.hasNext()) { 263 Persistent object = (Persistent) it.next(); 264 ids.add(object.getObjectId()); 265 } 266 267 if (domain.getSharedSnapshotCache() != null) { 268 domain.getSharedSnapshotCache().processSnapshotChanges( 270 context.getObjectStore(), 271 Collections.EMPTY_MAP, 272 Collections.EMPTY_LIST, 273 ids, 274 Collections.EMPTY_LIST); 275 } 276 277 GenericResponse response = new GenericResponse(); 278 response.addUpdateCount(1); 279 this.response = response; 280 return DONE; 281 } 282 283 if (refreshQuery.getQuery() != null) { 286 Query cachedQuery = refreshQuery.getQuery(); 287 288 String cacheKey = cachedQuery 289 .getMetaData(context.getEntityResolver()) 290 .getCacheKey(); 291 context.getQueryCache().remove(cacheKey); 292 293 this.response = domain.onQuery(context, cachedQuery); 294 return DONE; 295 } 296 297 if (refreshQuery.getGroupKeys() != null 299 && refreshQuery.getGroupKeys().length > 0) { 300 301 String [] groups = refreshQuery.getGroupKeys(); 302 for (int i = 0; i < groups.length; i++) { 303 domain.getQueryCache().removeGroup(groups[i]); 304 } 305 306 GenericResponse response = new GenericResponse(); 307 response.addUpdateCount(1); 308 this.response = response; 309 return DONE; 310 } 311 } 312 313 return !DONE; 314 } 315 316 319 private final boolean interceptSharedCache() { 320 321 if (metadata.getCacheKey() == null) { 322 return !DONE; 323 } 324 325 boolean cache = QueryMetadata.SHARED_CACHE.equals(metadata.getCachePolicy()); 326 boolean cacheOrCacheRefresh = cache 327 || QueryMetadata.SHARED_CACHE_REFRESH.equals(metadata.getCachePolicy()); 328 329 if (!cacheOrCacheRefresh) { 330 return !DONE; 331 } 332 333 QueryCache queryCache = domain.getQueryCache(); 334 QueryCacheEntryFactory factory = getCacheObjectFactory(); 335 336 if (cache) { 337 List cachedResults = queryCache.get(metadata, factory); 338 339 if (response == null) { 342 response = new ListResponse(cachedResults); 343 } 344 345 if (cachedResults instanceof ListWithPrefetches) { 346 this.prefetchResultsByPath = ((ListWithPrefetches) cachedResults) 347 .getPrefetchResultsByPath(); 348 } 349 } 350 else { 351 queryCache.put(metadata, (List ) factory.createObject()); 353 } 354 355 return DONE; 356 } 357 358 private QueryCacheEntryFactory getCacheObjectFactory() { 359 return new QueryCacheEntryFactory() { 360 361 public Object createObject() { 362 runQueryInTransaction(); 363 364 List list = response.firstList(); 365 if (list != null) { 366 367 list = Collections.unmodifiableList(list); 369 370 if (prefetchResultsByPath != null) { 372 list = new ListWithPrefetches(list, prefetchResultsByPath); 373 } 374 } 375 376 return list; 377 } 378 }; 379 } 380 381 384 void runQueryInTransaction() { 385 386 domain.runInTransaction(new Transformer() { 387 388 public Object transform(Object input) { 389 runQuery(); 390 return null; 391 } 392 }); 393 } 394 395 private void runQuery() { 396 this.fullResponse = new GenericResponse(); 398 this.response = this.fullResponse; 399 this.queriesByNode = null; 400 this.queriesByExecutedQueries = null; 401 402 this.prefetchResultsByPath = metadata.getPrefetchTree() != null 405 && !metadata.isFetchingDataRows() ? new HashMap () : null; 406 407 query.route(this, domain.getEntityResolver(), null); 409 410 if (queriesByNode != null) { 412 Iterator nodeIt = queriesByNode.entrySet().iterator(); 413 while (nodeIt.hasNext()) { 414 Map.Entry entry = (Map.Entry ) nodeIt.next(); 415 QueryEngine nextNode = (QueryEngine) entry.getKey(); 416 Collection nodeQueries = (Collection ) entry.getValue(); 417 nextNode.performQueries(nodeQueries, this); 418 } 419 } 420 } 421 422 private void interceptObjectConversion() { 423 424 if (context != null && !metadata.isFetchingDataRows()) { 425 426 List mainRows = response.firstList(); 427 if (mainRows != null && !mainRows.isEmpty()) { 428 429 List objects; 430 ClassDescriptor descriptor = metadata.getClassDescriptor(); 431 PrefetchTreeNode prefetchTree = metadata.getPrefetchTree(); 432 433 if (prefetchTree == null) { 435 objects = new ObjectResolver(context, descriptor, metadata 436 .isRefreshingObjects(), metadata.isResolvingInherited()) 437 .synchronizedObjectsFromDataRows(mainRows); 438 } 439 else { 440 441 ObjectTreeResolver resolver = new ObjectTreeResolver( 442 context, 443 metadata); 444 objects = resolver.synchronizedObjectsFromDataRows( 445 prefetchTree, 446 mainRows, 447 prefetchResultsByPath); 448 } 449 450 if (response instanceof GenericResponse) { 451 ((GenericResponse) response).replaceResult(mainRows, objects); 452 } 453 else if (response instanceof ListResponse) { 454 this.response = new ListResponse(objects); 455 } 456 else { 457 throw new IllegalStateException ("Unknown response object: " 458 + this.response); 459 } 460 } 461 } 462 } 463 464 private boolean interceptMappedConversion() { 465 SQLResultSetMapping rsMapping = metadata.getResultSetMapping(); 466 467 if (rsMapping == null) { 468 return !DONE; 469 } 470 471 List mainRows = response.firstList(); 472 if (mainRows != null && !mainRows.isEmpty()) { 473 474 Collection columns = rsMapping.getColumnResults(); 475 if (columns.isEmpty()) { 476 throw new CayenneRuntimeException( 477 "Invalid result set mapping, no columns mapped."); 478 } 479 480 Object [] columnsArray = columns.toArray(); 481 int rowsLen = mainRows.size(); 482 int rowWidth = columnsArray.length; 483 484 List objects = new ArrayList (rowsLen); 485 486 if (rowWidth == 1) { 488 489 for (int i = 0; i < rowsLen; i++) { 490 Map row = (Map ) mainRows.get(i); 491 objects.add(row.get(columnsArray[0])); 492 } 493 } 494 else { 496 for (int i = 0; i < rowsLen; i++) { 497 Map row = (Map ) mainRows.get(i); 498 Object [] rowDecoded = new Object [rowWidth]; 499 500 for (int j = 0; j < rowWidth; j++) { 501 rowDecoded[j] = row.get(columnsArray[j]); 502 } 503 504 objects.add(rowDecoded); 505 } 506 } 507 508 if (response instanceof GenericResponse) { 509 ((GenericResponse) response).replaceResult(mainRows, objects); 510 } 511 else if (response instanceof ListResponse) { 512 this.response = new ListResponse(objects); 513 } 514 else { 515 throw new IllegalStateException ("Unknown response object: " 516 + this.response); 517 } 518 519 } 520 521 return DONE; 522 } 523 524 public void route(QueryEngine engine, Query query, Query substitutedQuery) { 525 526 List queries = null; 527 if (queriesByNode == null) { 528 queriesByNode = new HashMap (); 529 } 530 else { 531 queries = (List ) queriesByNode.get(engine); 532 } 533 534 if (queries == null) { 535 queries = new ArrayList (5); 536 queriesByNode.put(engine, queries); 537 } 538 539 queries.add(query); 540 541 if (substitutedQuery != null && substitutedQuery != query) { 544 545 if (queriesByExecutedQueries == null) { 546 queriesByExecutedQueries = new HashMap (); 547 } 548 549 queriesByExecutedQueries.put(query, substitutedQuery); 550 } 551 } 552 553 public QueryEngine engineForDataMap(DataMap map) { 554 if (map == null) { 555 throw new NullPointerException ("Null DataMap, can't determine DataNode."); 556 } 557 558 QueryEngine node = domain.lookupDataNode(map); 559 560 if (node == null) { 561 throw new CayenneRuntimeException("No DataNode exists for DataMap " + map); 562 } 563 564 return node; 565 } 566 567 public void nextCount(Query query, int resultCount) { 568 fullResponse.addUpdateCount(resultCount); 569 } 570 571 public void nextBatchCount(Query query, int[] resultCount) { 572 fullResponse.addBatchUpdateCount(resultCount); 573 } 574 575 public void nextDataRows(Query query, List dataRows) { 576 577 if (prefetchResultsByPath != null && query instanceof PrefetchSelectQuery) { 579 PrefetchSelectQuery prefetchQuery = (PrefetchSelectQuery) query; 580 prefetchResultsByPath.put(prefetchQuery.getPrefetchPath(), dataRows); 581 } 582 else { 583 fullResponse.addResultList(dataRows); 584 } 585 } 586 587 public void nextDataRows(Query q, ResultIterator it) { 588 throw new CayenneRuntimeException("Invalid attempt to fetch a cursor."); 589 } 590 591 public void nextGeneratedDataRows(Query query, ResultIterator keysIterator) { 592 if (keysIterator != null) { 593 try { 594 nextDataRows(query, keysIterator.dataRows(true)); 595 } 596 catch (CayenneException ex) { 597 nextQueryException(query, ex); 599 } 600 } 601 } 602 603 public void nextQueryException(Query query, Exception ex) { 604 throw new CayenneRuntimeException("Query exception.", Util.unwindException(ex)); 605 } 606 607 public void nextGlobalException(Exception e) { 608 throw new CayenneRuntimeException("Global exception.", Util.unwindException(e)); 609 } 610 611 public boolean isIteratedResult() { 612 return false; 613 } 614 } 615 | Popular Tags |