1 19 20 package org.apache.cayenne.access; 21 22 import java.io.IOException ; 23 import java.io.ObjectInputStream ; 24 import java.io.ObjectOutputStream ; 25 import java.util.ArrayList ; 26 import java.util.Collection ; 27 import java.util.Collections ; 28 import java.util.HashMap ; 29 import java.util.Iterator ; 30 import java.util.List ; 31 import java.util.Map ; 32 33 import org.apache.cayenne.BaseContext; 34 import org.apache.cayenne.CayenneException; 35 import org.apache.cayenne.CayenneRuntimeException; 36 import org.apache.cayenne.DataChannel; 37 import org.apache.cayenne.DataObject; 38 import org.apache.cayenne.DataObjectUtils; 39 import org.apache.cayenne.DataRow; 40 import org.apache.cayenne.DeleteDenyException; 41 import org.apache.cayenne.Fault; 42 import org.apache.cayenne.LifecycleListener; 43 import org.apache.cayenne.ObjectContext; 44 import org.apache.cayenne.ObjectId; 45 import org.apache.cayenne.PersistenceState; 46 import org.apache.cayenne.Persistent; 47 import org.apache.cayenne.QueryResponse; 48 import org.apache.cayenne.access.event.DataContextEvent; 49 import org.apache.cayenne.access.util.IteratedSelectObserver; 50 import org.apache.cayenne.cache.QueryCache; 51 import org.apache.cayenne.cache.QueryCacheFactory; 52 import org.apache.cayenne.conf.Configuration; 53 import org.apache.cayenne.event.EventManager; 54 import org.apache.cayenne.event.EventSubject; 55 import org.apache.cayenne.graph.CompoundDiff; 56 import org.apache.cayenne.graph.GraphDiff; 57 import org.apache.cayenne.graph.GraphEvent; 58 import org.apache.cayenne.graph.GraphManager; 59 import org.apache.cayenne.map.DbJoin; 60 import org.apache.cayenne.map.DbRelationship; 61 import org.apache.cayenne.map.EntityResolver; 62 import org.apache.cayenne.map.ObjAttribute; 63 import org.apache.cayenne.map.ObjEntity; 64 import org.apache.cayenne.map.ObjRelationship; 65 import org.apache.cayenne.query.NamedQuery; 66 import org.apache.cayenne.query.ObjectIdQuery; 67 import org.apache.cayenne.query.Query; 68 import org.apache.cayenne.query.QueryMetadata; 69 import org.apache.cayenne.query.RefreshQuery; 70 import org.apache.cayenne.reflect.AttributeProperty; 71 import org.apache.cayenne.reflect.ClassDescriptor; 72 import org.apache.cayenne.reflect.PropertyVisitor; 73 import org.apache.cayenne.reflect.ToManyProperty; 74 import org.apache.cayenne.reflect.ToOneProperty; 75 import org.apache.cayenne.util.EventUtil; 76 import org.apache.cayenne.util.GenericResponse; 77 import org.apache.cayenne.util.Util; 78 79 103 public class DataContext extends BaseContext implements DataChannel { 104 105 public static final EventSubject WILL_COMMIT = EventSubject.getSubject( 107 DataContext.class, 108 "DataContextWillCommit"); 109 public static final EventSubject DID_COMMIT = EventSubject.getSubject( 110 DataContext.class, 111 "DataContextDidCommit"); 112 public static final EventSubject DID_ROLLBACK = EventSubject.getSubject( 113 DataContext.class, 114 "DataContextDidRollback"); 115 116 121 protected static final ThreadLocal threadDataContext = new ThreadLocal (); 123 124 private static boolean transactionEventsEnabledDefault; 126 127 private boolean transactionEventsEnabled; 129 130 private DataContextDelegate delegate; 132 133 protected boolean usingSharedSnaphsotCache; 134 protected boolean validatingObjectsOnCommit; 135 protected ObjectStore objectStore; 136 protected QueryCache queryCache; 137 138 protected transient EntityResolver entityResolver; 141 142 protected transient DataContextMergeHandler mergeHandler; 143 144 DataContextGraphAction graphAction; 145 146 151 protected Map userProperties; 152 153 159 protected transient String lazyInitParentDomainName; 160 161 170 public static DataContext getThreadDataContext() throws IllegalStateException { 171 DataContext dc = (DataContext) threadDataContext.get(); 172 if (dc == null) { 173 throw new IllegalStateException ("Current thread has no bound DataContext."); 174 } 175 176 return dc; 177 } 178 179 186 public static void bindThreadDataContext(DataContext context) { 187 threadDataContext.set(context); 188 } 189 190 197 public static DataContext createDataContext() { 198 return Configuration.getSharedConfiguration().getDomain().createDataContext(); 199 } 200 201 210 public static DataContext createDataContext(boolean useSharedCache) { 211 return Configuration.getSharedConfiguration().getDomain().createDataContext( 212 useSharedCache); 213 } 214 215 220 public static DataContext createDataContext(String domainName) { 221 DataDomain domain = Configuration.getSharedConfiguration().getDomain(domainName); 222 if (domain == null) { 223 throw new IllegalArgumentException ("Non-existent domain: " + domainName); 224 } 225 return domain.createDataContext(); 226 } 227 228 235 public static DataContext createDataContext(String domainName, boolean useSharedCache) { 236 237 DataDomain domain = Configuration.getSharedConfiguration().getDomain(domainName); 238 if (domain == null) { 239 throw new IllegalArgumentException ("Non-existent domain: " + domainName); 240 } 241 return domain.createDataContext(useSharedCache); 242 } 243 244 247 public DataContext() { 248 this(null, null); 249 } 250 251 256 public DataContext(DataChannel channel, ObjectStore objectStore) { 257 setChannel(channel); 259 260 this.setTransactionEventsEnabled(transactionEventsEnabledDefault); 261 262 if (objectStore != null) { 264 this.objectStore = objectStore; 265 objectStore.setContext(this); 266 267 DataDomain domain = getParentDataDomain(); 268 this.usingSharedSnaphsotCache = domain != null 269 && objectStore.getDataRowCache() == domain.getSharedSnapshotCache(); 270 } 271 272 this.graphAction = new DataContextGraphAction(this); 273 } 274 275 282 public QueryCache getQueryCache() { 283 284 if (queryCache == null) { 285 synchronized (this) { 286 if (queryCache == null) { 287 queryCache = getParentDataDomain() 288 .getQueryCacheFactory() 289 .getQueryCache(Collections.EMPTY_MAP); 290 } 291 } 292 } 293 294 return queryCache; 295 } 296 297 302 public void setQueryCache(QueryCache queryCache) { 303 this.queryCache = queryCache; 304 } 305 306 311 protected Map getUserProperties() { 312 if (userProperties == null) { 315 userProperties = new HashMap (); 316 } 317 318 return userProperties; 319 } 320 321 326 public DataContext createChildDataContext() { 327 DataContextFactory factory = getParentDataDomain().getDataContextFactory(); 328 329 ObjectStore objectStore = new ObjectStore(); 332 333 DataContext child = factory != null ? factory 334 .createDataContext(this, objectStore) : new DataContext( 335 (DataChannel) this, 336 objectStore); 337 338 child.setValidatingObjectsOnCommit(isValidatingObjectsOnCommit()); 339 child.usingSharedSnaphsotCache = isUsingSharedSnapshotCache(); 340 return child; 341 } 342 343 349 public Object getUserProperty(String key) { 350 return getUserProperties().get(key); 351 } 352 353 359 public void setUserProperty(String key, Object value) { 360 getUserProperties().put(key, value); 361 } 362 363 366 public void setChannel(DataChannel channel) { 367 if (this.channel != channel) { 368 369 if (this.mergeHandler != null) { 370 this.mergeHandler.setActive(false); 371 } 372 373 this.entityResolver = null; 374 this.mergeHandler = null; 375 376 this.channel = channel; 377 378 if (channel != null) { 379 380 this.entityResolver = channel.getEntityResolver(); 383 384 EventManager eventManager = channel.getEventManager(); 385 386 if (eventManager != null) { 387 this.mergeHandler = new DataContextMergeHandler(this); 388 389 EventUtil.listenForChannelEvents(channel, mergeHandler); 393 } 394 } 395 } 396 } 397 398 public DataChannel getChannel() { 399 awakeFromDeserialization(); 400 return super.getChannel(); 401 } 402 403 412 public DataDomain getParentDataDomain() { 413 awakeFromDeserialization(); 414 415 if (channel == null) { 416 return null; 417 } 418 419 if (channel instanceof DataDomain) { 420 return (DataDomain) channel; 421 } 422 423 List response = channel.onQuery(this, new DataDomainQuery()).firstList(); 424 425 if (response != null 426 && response.size() > 0 427 && response.get(0) instanceof DataDomain) { 428 return (DataDomain) response.get(0); 429 } 430 431 return null; 432 } 433 434 440 public void setDelegate(DataContextDelegate delegate) { 441 this.delegate = delegate; 442 } 443 444 449 public DataContextDelegate getDelegate() { 450 return delegate; 451 } 452 453 459 DataContextDelegate nonNullDelegate() { 460 return (delegate != null) ? delegate : NoopDelegate.noopDelegate; 461 } 462 463 466 public ObjectStore getObjectStore() { 467 return objectStore; 468 } 469 470 474 public boolean hasChanges() { 475 return getObjectStore().hasChanges(); 476 } 477 478 482 public Collection newObjects() { 483 return getObjectStore().objectsInState(PersistenceState.NEW); 484 } 485 486 490 public Collection deletedObjects() { 491 return getObjectStore().objectsInState(PersistenceState.DELETED); 492 } 493 494 498 public Collection modifiedObjects() { 499 return getObjectStore().objectsInState(PersistenceState.MODIFIED); 500 } 501 502 507 public Collection uncommittedObjects() { 508 509 int len = getObjectStore().registeredObjectsCount(); 510 if (len == 0) { 511 return Collections.EMPTY_LIST; 512 } 513 514 Collection objects = new ArrayList (len > 100 ? len / 2 : len); 516 517 Iterator it = getObjectStore().getObjectIterator(); 518 while (it.hasNext()) { 519 Persistent object = (Persistent) it.next(); 520 int state = object.getPersistenceState(); 521 if (state == PersistenceState.MODIFIED 522 || state == PersistenceState.NEW 523 || state == PersistenceState.DELETED) { 524 525 objects.add(object); 526 } 527 } 528 529 return objects; 530 } 531 532 543 public DataRow currentSnapshot(final Persistent object) { 544 545 if (object.getPersistenceState() == PersistenceState.HOLLOW 547 && object.getObjectContext() != null) { 548 549 return getObjectStore().getSnapshot(object.getObjectId()); 550 } 551 552 ObjEntity entity = getEntityResolver().lookupObjEntity(object); 553 final ClassDescriptor descriptor = getEntityResolver().getClassDescriptor( 554 entity.getName()); 555 final DataRow snapshot = new DataRow(10); 556 557 descriptor.visitProperties(new PropertyVisitor() { 558 559 public boolean visitAttribute(AttributeProperty property) { 560 ObjAttribute objAttr = property.getAttribute(); 561 562 snapshot.put(objAttr.getDbAttributePath(), property 564 .readPropertyDirectly(object)); 565 return true; 566 } 567 568 public boolean visitToMany(ToManyProperty property) { 569 return true; 571 } 572 573 public boolean visitToOne(ToOneProperty property) { 574 ObjRelationship rel = property.getRelationship(); 575 576 if (rel.isSourceIndependentFromTargetChange()) { 578 return true; 579 } 580 581 Object targetObject = property.readPropertyDirectly(object); 582 if (targetObject == null) { 583 return true; 584 } 585 586 if (targetObject instanceof Fault) { 589 DataRow storedSnapshot = getObjectStore().getSnapshot( 590 object.getObjectId()); 591 if (storedSnapshot == null) { 592 throw new CayenneRuntimeException( 593 "No matching objects found for ObjectId " 594 + object.getObjectId() 595 + ". Object may have been deleted externally."); 596 } 597 598 DbRelationship dbRel = (DbRelationship) rel.getDbRelationships().get( 599 0); 600 Iterator joins = dbRel.getJoins().iterator(); 601 while (joins.hasNext()) { 602 DbJoin join = (DbJoin) joins.next(); 603 String key = join.getSourceName(); 604 snapshot.put(key, storedSnapshot.get(key)); 605 } 606 607 return true; 608 } 609 610 Persistent target = (Persistent) targetObject; 613 Map idParts = target.getObjectId().getIdSnapshot(); 614 615 if (idParts.isEmpty()) { 619 return true; 620 } 621 622 DbRelationship dbRel = (DbRelationship) rel.getDbRelationships().get(0); 623 Map fk = dbRel.srcFkSnapshotWithTargetSnapshot(idParts); 624 snapshot.putAll(fk); 625 return true; 626 } 627 }); 628 629 Map thisIdParts = object.getObjectId().getIdSnapshot(); 634 if (thisIdParts != null) { 635 636 Iterator idIterator = thisIdParts.entrySet().iterator(); 638 while (idIterator.hasNext()) { 639 Map.Entry entry = (Map.Entry ) idIterator.next(); 640 Object nextKey = entry.getKey(); 641 if (!snapshot.containsKey(nextKey)) { 642 snapshot.put(nextKey, entry.getValue()); 643 } 644 } 645 } 646 647 return snapshot; 648 } 649 650 655 public List objectsFromDataRows( 656 ObjEntity entity, 657 List dataRows, 658 boolean refresh, 659 boolean resolveInheritanceHierarchy) { 660 661 ClassDescriptor descriptor = getEntityResolver().getClassDescriptor( 665 entity.getName()); 666 return new ObjectResolver(this, descriptor, refresh, resolveInheritanceHierarchy) 667 .synchronizedObjectsFromDataRows(dataRows); 668 } 669 670 678 public List objectsFromDataRows( 679 Class objectClass, 680 List dataRows, 681 boolean refresh, 682 boolean resolveInheritanceHierarchy) { 683 ObjEntity entity = this.getEntityResolver().lookupObjEntity(objectClass); 684 685 if (entity == null) { 686 throw new CayenneRuntimeException("Unmapped Java class: " + objectClass); 687 } 688 689 return objectsFromDataRows(entity, dataRows, refresh, resolveInheritanceHierarchy); 690 } 691 692 698 public DataObject objectFromDataRow( 699 Class objectClass, 700 DataRow dataRow, 701 boolean refresh) { 702 List list = objectsFromDataRows( 703 objectClass, 704 Collections.singletonList(dataRow), 705 refresh, 706 true); 707 return (DataObject) list.get(0); 708 } 709 710 713 public DataObject createAndRegisterNewObject(String objEntityName) { 714 return (DataObject) newObject(objEntityName); 715 } 716 717 722 public Persistent newObject(Class persistentClass) { 723 if (persistentClass == null) { 724 throw new NullPointerException ("Null 'persistentClass'"); 725 } 726 727 ObjEntity entity = getEntityResolver().lookupObjEntity(persistentClass); 728 if (entity == null) { 729 throw new IllegalArgumentException ("Class is not mapped with Cayenne: " 730 + persistentClass.getName()); 731 } 732 733 return newObject(entity.getName()); 734 } 735 736 744 public Persistent newObject(String entityName) { 745 ClassDescriptor descriptor = getEntityResolver().getClassDescriptor(entityName); 746 if (descriptor == null) { 747 throw new IllegalArgumentException ("Invalid entity name: " + entityName); 748 } 749 750 Persistent object; 751 try { 752 object = (Persistent) descriptor.createObject(); 753 } 754 catch (Exception ex) { 755 throw new CayenneRuntimeException("Error instantiating object.", ex); 756 } 757 758 descriptor.injectValueHolders(object); 760 761 ObjectId id = new ObjectId(entityName); 762 763 object.setObjectId(id); 766 object.setObjectContext(this); 767 object.setPersistenceState(PersistenceState.NEW); 768 getObjectStore().registerNode(id, object); 769 getObjectStore().nodeCreated(id); 770 771 return object; 772 } 773 774 781 public DataObject createAndRegisterNewObject(Class objectClass) { 782 if (objectClass == null) { 783 throw new NullPointerException ("DataObject class can't be null."); 784 } 785 786 ObjEntity entity = getEntityResolver().lookupObjEntity(objectClass); 787 if (entity == null) { 788 throw new IllegalArgumentException ("Class is not mapped with Cayenne: " 789 + objectClass.getName()); 790 } 791 792 return createAndRegisterNewObject(entity.getName()); 793 } 794 795 803 public void registerNewObject(Object object) { 804 if (object == null) { 805 throw new NullPointerException ("Can't register null object."); 806 } 807 808 ObjEntity entity = getEntityResolver().lookupObjEntity(object); 809 if (entity == null) { 810 throw new IllegalArgumentException ( 811 "Can't find ObjEntity for DataObject class: " 812 + object.getClass().getName() 813 + ", class is likely not mapped."); 814 } 815 816 final Persistent persistent = (Persistent) object; 817 818 if (persistent.getObjectId() != null) { 820 if (persistent.getObjectContext() == this) { 821 return; 823 } 824 else if (persistent.getObjectContext() != null) { 825 throw new IllegalStateException ( 826 "DataObject is already registered with another DataContext. " 827 + "Try using 'localObjects()' instead."); 828 } 829 } 830 else { 831 persistent.setObjectId(new ObjectId(entity.getName())); 832 } 833 834 persistent.setObjectContext(this); 835 persistent.setPersistenceState(PersistenceState.NEW); 836 837 getObjectStore().registerNode(persistent.getObjectId(), object); 838 getObjectStore().nodeCreated(persistent.getObjectId()); 839 840 843 ClassDescriptor descriptor = getEntityResolver().getClassDescriptor( 844 entity.getName()); 845 if (descriptor == null) { 846 throw new IllegalArgumentException ("Invalid entity name: " + entity.getName()); 847 } 848 849 descriptor.visitProperties(new PropertyVisitor() { 850 851 public boolean visitToMany(ToManyProperty property) { 852 property.injectValueHolder(persistent); 853 854 if (!property.isFault(persistent)) { 855 Iterator it = ((Collection ) property.readProperty(persistent)) 856 .iterator(); 857 while (it.hasNext()) { 858 Object target = it.next(); 859 860 if (target instanceof DataObject) { 861 DataObject targetDO = (DataObject) target; 862 863 registerNewObject(targetDO); 865 getObjectStore().arcCreated( 866 persistent.getObjectId(), 867 targetDO.getObjectId(), 868 property.getName()); 869 } 870 } 871 } 872 return true; 873 } 874 875 public boolean visitToOne(ToOneProperty property) { 876 Object target = property.readPropertyDirectly(persistent); 877 878 if (target instanceof DataObject) { 879 880 DataObject targetDO = (DataObject) target; 881 882 registerNewObject(targetDO); 884 getObjectStore().arcCreated( 885 persistent.getObjectId(), 886 targetDO.getObjectId(), 887 property.getName()); 888 } 889 return true; 890 } 891 892 public boolean visitAttribute(AttributeProperty property) { 893 return true; 894 } 895 }); 896 } 897 898 905 public void unregisterObjects(Collection dataObjects) { 906 getObjectStore().objectsUnregistered(dataObjects); 907 } 908 909 917 public void invalidateObjects(Collection objects) { 918 performGenericQuery(new RefreshQuery(objects)); 919 } 920 921 935 public void deleteObjects(Collection objects) { 936 if (objects.isEmpty()) { 937 return; 938 } 939 940 Iterator it = new ArrayList (objects).iterator(); 943 while (it.hasNext()) { 944 Persistent object = (Persistent) it.next(); 945 deleteObject(object); 946 } 947 } 948 949 960 public void deleteObject(Object object) throws DeleteDenyException { 961 new DataContextDeleteAction(this).performDelete((Persistent) object); 962 } 963 964 973 public DataObject refetchObject(ObjectId oid) { 974 975 if (oid == null) { 976 throw new NullPointerException ("Null ObjectId"); 977 } 978 979 if (oid.isTemporary()) { 980 throw new CayenneRuntimeException("Can't refetch ObjectId " 981 + oid 982 + ", as it is a temporary id."); 983 } 984 985 synchronized (getObjectStore()) { 986 DataObject object = (DataObject) objectStore.getNode(oid); 987 988 if (object != null) { 990 this.invalidateObjects(Collections.singleton(object)); 991 } 992 } 993 994 DataObject object = (DataObject) DataObjectUtils.objectForQuery( 995 this, 996 new ObjectIdQuery(oid)); 997 998 if (object == null) { 999 throw new CayenneRuntimeException( 1000 "Refetch failure: no matching objects found for ObjectId " + oid); 1001 } 1002 1003 return object; 1004 } 1005 1006 1012 public void rollbackChangesLocally() { 1014 if (getChannel() instanceof DataDomain) { 1015 rollbackChanges(); 1016 } 1017 else { 1018 throw new CayenneRuntimeException("Implementation pending."); 1019 } 1020 } 1021 1022 1026 public void rollbackChanges() { 1027 1028 if (objectStore.hasChanges()) { 1029 GraphDiff diff = getObjectStore().getChanges(); 1030 1031 1034 if (channel != null) { 1035 channel.onSync(this, diff, DataChannel.ROLLBACK_CASCADE_SYNC); 1036 } 1037 1038 getObjectStore().objectsRolledBack(); 1039 fireDataChannelRolledback(this, diff); 1040 } 1041 } 1042 1043 1053 public void commitChangesToParent() { 1054 flushToParent(false); 1055 } 1056 1057 1061 public void commitChanges() throws CayenneRuntimeException { 1062 flushToParent(true); 1063 } 1064 1065 1070 public EventManager getEventManager() { 1071 return channel != null ? channel.getEventManager() : null; 1072 } 1073 1074 1080 public GraphDiff onSync( 1081 ObjectContext originatingContext, 1082 GraphDiff changes, 1083 int syncType) { 1084 switch (syncType) { 1086 case DataChannel.ROLLBACK_CASCADE_SYNC: 1087 return onContextRollback(originatingContext); 1088 case DataChannel.FLUSH_NOCASCADE_SYNC: 1089 return onContextFlush(originatingContext, changes, false); 1090 case DataChannel.FLUSH_CASCADE_SYNC: 1091 return onContextFlush(originatingContext, changes, true); 1092 default: 1093 throw new CayenneRuntimeException("Unrecognized SyncMessage type: " 1094 + syncType); 1095 } 1096 } 1097 1098 GraphDiff onContextRollback(ObjectContext originatingContext) { 1099 rollbackChanges(); 1100 return new CompoundDiff(); 1101 } 1102 1103 GraphDiff onContextFlush( 1104 ObjectContext originatingContext, 1105 GraphDiff changes, 1106 boolean cascade) { 1107 1108 if (this != originatingContext && changes != null) { 1109 changes.apply(new ChildDiffLoader(this)); 1110 fireDataChannelChanged(originatingContext, changes); 1111 } 1112 1113 return (cascade) ? flushToParent(true) : new CompoundDiff(); 1114 } 1115 1116 1121 GraphDiff flushToParent(boolean cascade) { 1122 1123 if (this.getChannel() == null) { 1124 throw new CayenneRuntimeException( 1125 "Cannot commit changes - channel is not set."); 1126 } 1127 1128 int syncType = cascade 1129 ? DataChannel.FLUSH_CASCADE_SYNC 1130 : DataChannel.FLUSH_NOCASCADE_SYNC; 1131 1132 ObjectStore objectStore = getObjectStore(); 1133 1134 synchronized (objectStore) { 1136 1137 DataContextFlushEventHandler eventHandler = null; 1138 1139 ObjectStoreGraphDiff changes = objectStore.getChanges(); 1140 boolean noop = isValidatingObjectsOnCommit() 1141 ? changes.validateAndCheckNoop() 1142 : changes.isNoop(); 1143 1144 if (noop) { 1145 objectStore.postprocessAfterPhantomCommit(); 1147 return new CompoundDiff(); 1148 } 1149 1150 if (isTransactionEventsEnabled()) { 1151 eventHandler = new DataContextFlushEventHandler(this); 1152 eventHandler.registerForDataContextEvents(); 1153 fireWillCommit(); 1154 } 1155 1156 try { 1157 GraphDiff returnChanges = getChannel().onSync(this, changes, syncType); 1158 1159 if (objectStore.hasChanges()) { 1166 objectStore.postprocessAfterCommit(returnChanges); 1167 } 1168 1169 fireTransactionCommitted(); 1171 1172 fireDataChannelCommitted(this, changes); 1175 1176 if (!returnChanges.isNoop()) { 1179 fireDataChannelCommitted(getChannel(), returnChanges); 1180 } 1181 1182 return returnChanges; 1183 } 1184 catch (CayenneRuntimeException ex) { 1186 fireTransactionRolledback(); 1187 1188 Throwable unwound = Util.unwindException(ex); 1189 1190 if (unwound instanceof CayenneRuntimeException) { 1191 throw (CayenneRuntimeException) unwound; 1192 } 1193 else { 1194 throw new CayenneRuntimeException("Commit Exception", unwound); 1195 } 1196 } 1197 finally { 1198 1199 if (isTransactionEventsEnabled()) { 1200 eventHandler.unregisterFromDataContextEvents(); 1201 } 1202 } 1203 } 1204 } 1205 1206 1214 public ResultIterator performIteratedQuery(Query query) throws CayenneException { 1215 if (Transaction.getThreadTransaction() != null) { 1216 return internalPerformIteratedQuery(query); 1217 } 1218 else { 1219 1220 Transaction tx = getParentDataDomain().createTransaction(); 1223 Transaction.bindThreadTransaction(tx); 1224 1225 ResultIterator result; 1226 try { 1227 result = internalPerformIteratedQuery(query); 1228 } 1229 catch (Exception e) { 1230 Transaction.bindThreadTransaction(null); 1231 tx.setRollbackOnly(); 1232 throw new CayenneException(e); 1233 } 1234 finally { 1235 1239 if (tx.getStatus() == Transaction.STATUS_MARKED_ROLLEDBACK) { 1240 try { 1241 tx.rollback(); 1242 } 1243 catch (Exception rollbackEx) { 1244 } 1245 } 1246 } 1247 1248 return new TransactionResultIteratorDecorator(result, tx); 1249 } 1250 } 1251 1252 1257 ResultIterator internalPerformIteratedQuery(Query query) throws CayenneException { 1258 IteratedSelectObserver observer = new IteratedSelectObserver(); 1261 getParentDataDomain().performQueries(Collections.singletonList(query), observer); 1262 return observer.getResultIterator(); 1263 } 1264 1265 1270 public QueryResponse performGenericQuery(Query query) { 1271 1272 query = nonNullDelegate().willPerformGenericQuery(this, query); 1273 if (query == null) { 1274 return new GenericResponse(); 1275 } 1276 1277 if (this.getChannel() == null) { 1278 throw new CayenneRuntimeException( 1279 "Can't run query - parent DataChannel is not set."); 1280 } 1281 1282 return onQuery(this, query); 1283 } 1284 1285 1304 public List performQuery(Query query) { 1305 query = nonNullDelegate().willPerformQuery(this, query); 1306 if (query == null) { 1307 return new ArrayList (1); 1308 } 1309 1310 List result = onQuery(this, query).firstList(); 1311 return result != null ? result : new ArrayList (1); 1312 } 1313 1314 1320 public QueryResponse onQuery(ObjectContext context, Query query) { 1321 return new DataContextQueryAction(this, context, query).execute(); 1322 } 1323 1324 1330 public int[] performNonSelectingQuery(Query query) { 1331 int[] count = performGenericQuery(query).firstUpdateCount(); 1332 return count != null ? count : new int[0]; 1333 } 1334 1335 1341 public int[] performNonSelectingQuery(String queryName) { 1342 return performNonSelectingQuery(new NamedQuery(queryName)); 1343 } 1344 1345 1351 public int[] performNonSelectingQuery(String queryName, Map parameters) { 1352 return performNonSelectingQuery(new NamedQuery(queryName, parameters)); 1353 } 1354 1355 1367 public List performQuery(String queryName, boolean expireCachedLists) { 1368 return performQuery(queryName, Collections.EMPTY_MAP, expireCachedLists); 1369 } 1370 1371 1384 public List performQuery(String queryName, Map parameters, boolean expireCachedLists) { 1385 NamedQuery query = new NamedQuery(queryName, parameters); 1386 query.setForceNoCache(expireCachedLists); 1387 return performQuery(query); 1388 } 1389 1390 1394 public EntityResolver getEntityResolver() { 1395 awakeFromDeserialization(); 1396 return entityResolver; 1397 } 1398 1399 1405 public static void setTransactionEventsEnabledDefault(boolean flag) { 1406 transactionEventsEnabledDefault = flag; 1407 } 1408 1409 1415 public void setTransactionEventsEnabled(boolean flag) { 1416 this.transactionEventsEnabled = flag; 1417 } 1418 1419 1423 public boolean isTransactionEventsEnabled() { 1424 return this.transactionEventsEnabled; 1425 } 1426 1427 1433 public boolean isUsingSharedSnapshotCache() { 1434 return usingSharedSnaphsotCache; 1435 } 1436 1437 1443 public boolean isValidatingObjectsOnCommit() { 1444 return validatingObjectsOnCommit; 1445 } 1446 1447 1453 public void setValidatingObjectsOnCommit(boolean flag) { 1454 this.validatingObjectsOnCommit = flag; 1455 } 1456 1457 1461 void fireWillCommit() { 1462 if (this.transactionEventsEnabled) { 1464 DataContextEvent commitChangesEvent = new DataContextEvent(this); 1465 getEventManager().postEvent(commitChangesEvent, DataContext.WILL_COMMIT); 1466 } 1467 } 1468 1469 1473 void fireTransactionRolledback() { 1474 if ((this.transactionEventsEnabled)) { 1476 DataContextEvent commitChangesEvent = new DataContextEvent(this); 1477 getEventManager().postEvent(commitChangesEvent, DataContext.DID_ROLLBACK); 1478 } 1479 } 1480 1481 1485 void fireTransactionCommitted() { 1486 if ((this.transactionEventsEnabled)) { 1488 DataContextEvent commitChangesEvent = new DataContextEvent(this); 1489 getEventManager().postEvent(commitChangesEvent, DataContext.DID_COMMIT); 1490 } 1491 1492 } 1493 1494 1497 void fireDataChannelCommitted(Object postedBy, GraphDiff changes) { 1498 EventManager manager = getEventManager(); 1499 1500 if (manager != null) { 1501 GraphEvent e = new GraphEvent(this, postedBy, changes); 1502 manager.postEvent(e, DataChannel.GRAPH_FLUSHED_SUBJECT); 1503 } 1504 } 1505 1506 1509 void fireDataChannelRolledback(Object postedBy, GraphDiff changes) { 1510 EventManager manager = getEventManager(); 1511 1512 if (manager != null) { 1513 GraphEvent e = new GraphEvent(this, postedBy, changes); 1514 manager.postEvent(e, DataChannel.GRAPH_ROLLEDBACK_SUBJECT); 1515 } 1516 } 1517 1518 1521 void fireDataChannelChanged(Object postedBy, GraphDiff changes) { 1522 EventManager manager = getEventManager(); 1523 1524 if (manager != null) { 1525 GraphEvent e = new GraphEvent(this, postedBy, changes); 1526 manager.postEvent(e, DataChannel.GRAPH_CHANGED_SUBJECT); 1527 } 1528 } 1529 1530 1534 private void writeObject(ObjectOutputStream out) throws IOException { 1535 out.defaultWriteObject(); 1536 1537 1542 if (this.channel == null && this.lazyInitParentDomainName != null) { 1543 out.writeObject(lazyInitParentDomainName); 1544 } 1545 else if (this.channel instanceof DataDomain) { 1546 DataDomain domain = (DataDomain) this.channel; 1547 out.writeObject(domain.getName()); 1548 } 1549 else { 1550 out.writeObject(this.channel); 1552 } 1553 1554 if (!isUsingSharedSnapshotCache()) { 1556 out.writeObject(objectStore.getDataRowCache()); 1557 } 1558 } 1559 1560 private void readObject(ObjectInputStream in) throws IOException , 1562 ClassNotFoundException { 1563 1564 in.defaultReadObject(); 1566 1567 Object value = in.readObject(); 1569 if (value instanceof DataChannel) { 1570 this.channel = (DataChannel) value; 1572 } 1573 else if (value instanceof String ) { 1574 this.lazyInitParentDomainName = (String ) value; 1576 } 1577 else { 1578 throw new CayenneRuntimeException( 1579 "Parent attribute of DataContext was neither a QueryEngine nor " 1580 + "the name of a valid DataDomain:" 1581 + value); 1582 } 1583 1584 if (!isUsingSharedSnapshotCache()) { 1586 DataRowStore cache = (DataRowStore) in.readObject(); 1587 objectStore.setDataRowCache(cache); 1588 } 1589 1590 1597 synchronized (getObjectStore()) { 1598 Iterator it = objectStore.getObjectIterator(); 1599 while (it.hasNext()) { 1600 Persistent object = (Persistent) it.next(); 1601 object.setObjectContext(this); 1602 } 1603 } 1604 } 1605 1606 private final void awakeFromDeserialization() { 1613 if (channel == null && lazyInitParentDomainName != null) { 1614 1615 setChannel(Configuration.getSharedConfiguration().getDomain( 1617 lazyInitParentDomainName)); 1618 } 1619 } 1620 1621 1624 public void propertyChanged( 1625 Persistent object, 1626 String property, 1627 Object oldValue, 1628 Object newValue) { 1629 graphAction.handlePropertyChange(object, property, oldValue, newValue); 1630 } 1631 1632 1637 public GraphManager getGraphManager() { 1638 return objectStore; 1639 } 1640 1641 1652 public Persistent localObject(ObjectId id, Object prototype) { 1653 1654 1657 if (id == null) { 1658 throw new IllegalArgumentException ("Null ObjectId"); 1659 } 1660 1661 ClassDescriptor descriptor = getEntityResolver().getClassDescriptor( 1665 id.getEntityName()); 1666 1667 Persistent cachedObject = (Persistent) getGraphManager().getNode(id); 1668 1669 if (cachedObject != null) { 1671 1672 int state = cachedObject.getPersistenceState(); 1673 1674 if (cachedObject != prototype 1676 && state != PersistenceState.MODIFIED 1677 && state != PersistenceState.DELETED) { 1678 1679 descriptor.injectValueHolders(cachedObject); 1680 1681 if (prototype != null 1682 && ((Persistent) prototype).getPersistenceState() != PersistenceState.HOLLOW) { 1683 1684 descriptor.shallowMerge(prototype, cachedObject); 1685 1686 if (state == PersistenceState.HOLLOW) { 1687 cachedObject.setPersistenceState(PersistenceState.COMMITTED); 1688 } 1689 } 1690 } 1691 1692 return cachedObject; 1693 } 1694 else { 1696 1697 Persistent localObject; 1698 1699 synchronized (getGraphManager()) { 1700 localObject = (Persistent) descriptor.createObject(); 1701 1702 localObject.setObjectContext(this); 1703 localObject.setObjectId(id); 1704 1705 getGraphManager().registerNode(id, localObject); 1706 } 1707 1708 if (prototype != null 1709 && ((Persistent) prototype).getPersistenceState() != PersistenceState.HOLLOW) { 1710 localObject.setPersistenceState(PersistenceState.COMMITTED); 1711 descriptor.injectValueHolders(localObject); 1712 descriptor.shallowMerge(prototype, localObject); 1713 } 1714 else { 1715 localObject.setPersistenceState(PersistenceState.HOLLOW); 1716 } 1717 1718 return localObject; 1719 } 1720 } 1721} 1722 | Popular Tags |