/** * Synchronizes with the parent channel, performing a flush or a commit. * * @since 1.2 */ GraphDiff flushToParent(boolean cascade) { if (this.getChannel() == null) { throw new CayenneRuntimeException("Cannot commit changes - channel is not set."); } int syncType = cascade ? DataChannel.FLUSH_CASCADE_SYNC : DataChannel.FLUSH_NOCASCADE_SYNC; ObjectStore objectStore = getObjectStore(); GraphDiff parentChanges = null; // prevent multiple commits occurring simultaneously synchronized (objectStore) { ObjectStoreGraphDiff changes = objectStore.getChanges(); boolean noop = isValidatingObjectsOnCommit() ? changes.validateAndCheckNoop() : changes.isNoop(); if (noop) { // need to clear phantom changes objectStore.postprocessAfterPhantomCommit(); } else { try { parentChanges = getChannel().onSync(this, changes, syncType); // note that this is a hack resulting from a fix to // CAY-766... To // support // valid object state in PostPersist callback, // 'postprocessAfterCommit' is // invoked by DataDomain.onSync(..). Unless the parent is // DataContext, // and // this method is not invoked!! As a result, PostPersist // will contain // temp // ObjectIds in nested contexts and perm ones in flat // contexts. // Pending better callback design ..... if (objectStore.hasChanges()) { objectStore.postprocessAfterCommit(parentChanges); } // this event is caught by peer nested DataContexts to // synchronize the // state fireDataChannelCommitted(this, changes); } // "catch" is needed to unwrap OptimisticLockExceptions catch (CayenneRuntimeException ex) { Throwable unwound = Util.unwindException(ex); if (unwound instanceof CayenneRuntimeException) { throw (CayenneRuntimeException) unwound; } else { throw new CayenneRuntimeException("Commit Exception", unwound); } } } // merge changes from parent as well as changes caused by lifecycle // event // callbacks/listeners... CompoundDiff diff = new CompoundDiff(); diff.addAll(objectStore.getLifecycleEventInducedChanges()); if (parentChanges != null) { diff.add(parentChanges); } // this event is caught by child DataContexts to update temporary // ObjectIds with permanent if (!diff.isNoop()) { fireDataChannelCommitted(getChannel(), diff); } return diff; } }