This is an automated email from the ASF dual-hosted git repository.
ntimofeev pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/cayenne.git
The following commit(s) were added to refs/heads/master by this push:
new 715c74f86 CAY-2830 Cleanup DataContext - delegate snapshot creation
to a separate handler - cleanup comments
715c74f86 is described below
commit 715c74f867ed8391664bb4372462a815a708ff63
Author: stariy95 <[email protected]>
AuthorDate: Tue Dec 5 18:35:08 2023 +0400
CAY-2830 Cleanup DataContext
- delegate snapshot creation to a separate handler
- cleanup comments
---
.../org/apache/cayenne/access/DataContext.java | 208 ++++-----------------
.../cayenne/access/DataContextSnapshotBuilder.java | 148 +++++++++++++++
2 files changed, 184 insertions(+), 172 deletions(-)
diff --git a/cayenne/src/main/java/org/apache/cayenne/access/DataContext.java
b/cayenne/src/main/java/org/apache/cayenne/access/DataContext.java
index 6cf895aa2..0803ed9ba 100644
--- a/cayenne/src/main/java/org/apache/cayenne/access/DataContext.java
+++ b/cayenne/src/main/java/org/apache/cayenne/access/DataContext.java
@@ -36,7 +36,6 @@ import org.apache.cayenne.DataChannel;
import org.apache.cayenne.DataObject;
import org.apache.cayenne.DataRow;
import org.apache.cayenne.DeleteDenyException;
-import org.apache.cayenne.Fault;
import org.apache.cayenne.FaultFailureException;
import org.apache.cayenne.ObjectContext;
import org.apache.cayenne.ObjectId;
@@ -57,13 +56,9 @@ import org.apache.cayenne.graph.CompoundDiff;
import org.apache.cayenne.graph.GraphDiff;
import org.apache.cayenne.graph.GraphEvent;
import org.apache.cayenne.graph.GraphManager;
-import org.apache.cayenne.map.DbJoin;
-import org.apache.cayenne.map.DbRelationship;
import org.apache.cayenne.map.EntityResolver;
import org.apache.cayenne.map.LifecycleEvent;
-import org.apache.cayenne.map.ObjAttribute;
import org.apache.cayenne.map.ObjEntity;
-import org.apache.cayenne.map.ObjRelationship;
import org.apache.cayenne.query.*;
import org.apache.cayenne.reflect.AttributeProperty;
import org.apache.cayenne.reflect.ClassDescriptor;
@@ -386,7 +381,7 @@ public class DataContext implements ObjectContext {
/**
* Returns a list of objects that are registered with this DataContext and
- * have a state PersistenceState.DELETED
+ * have a state {@link PersistenceState#DELETED}
*/
@Override
public Collection<?> deletedObjects() {
@@ -422,16 +417,15 @@ public class DataContext implements ObjectContext {
ObjectContextDeleteAction action = new ObjectContextDeleteAction(this);
- // Make a copy to iterate over to avoid
ConcurrentModificationException.
- List<Object> copy = new ArrayList<>(objects);
- for (Object object : copy) {
+ // Make a copy to iterate over to avoid ConcurrentModificationException
+ for (Object object : List.copyOf(objects)) {
action.performDelete((Persistent) object);
}
}
/**
* Returns a list of objects that are registered with this DataContext and
- * have a state PersistenceState.MODIFIED
+ * have a state {@link PersistenceState#MODIFIED}
*/
@Override
public Collection<?> modifiedObjects() {
@@ -453,7 +447,6 @@ public class DataContext implements ObjectContext {
// guess target collection size
Collection<Object> objects = new ArrayList<>(len > 100 ? len / 2 :
len);
-
Iterator<?> it = getObjectStore().getObjectIterator();
while (it.hasNext()) {
Persistent object = (Persistent) it.next();
@@ -553,112 +546,21 @@ public class DataContext implements ObjectContext {
}
/**
- * Returns a DataRow reflecting current, possibly uncommitted, object
state.
+ * Returns a {@link DataRow} reflecting current, possibly uncommitted,
object state.
* <p>
* <strong>Warning:</strong> This method will return a partial snapshot if
* an object or one of its related objects that propagate their keys to
this
* object have temporary ids. DO NOT USE this method if you expect a
DataRow
* to represent a complete object state.
* </p>
- *
+ *
+ * @param object persistent object to create snapshot for
+ * @return current snapshot of the persistent object
* @since 1.1
*/
public DataRow currentSnapshot(final Persistent object) {
-
- // for a HOLLOW object return snapshot from cache
- if (object.getPersistenceState() == PersistenceState.HOLLOW &&
object.getObjectContext() != null) {
-
- return getObjectStore().getSnapshot(object.getObjectId());
- }
-
- ObjEntity entity = getEntityResolver().getObjEntity(object);
- final ClassDescriptor descriptor =
getEntityResolver().getClassDescriptor(entity.getName());
- final DataRow snapshot = new DataRow(10);
- snapshot.setEntityName(entity.getName());
-
- descriptor.visitProperties(new PropertyVisitor() {
-
- public boolean visitAttribute(AttributeProperty property) {
- ObjAttribute objAttr = property.getAttribute();
-
- // processing compound attributes correctly
- snapshot.put(objAttr.getDbAttributePath(),
property.readPropertyDirectly(object));
- return true;
- }
-
- public boolean visitToMany(ToManyProperty property) {
- // do nothing
- return true;
- }
-
- public boolean visitToOne(ToOneProperty property) {
- ObjRelationship rel = property.getRelationship();
-
- // if target doesn't propagates its key value, skip it
- if (rel.isSourceIndependentFromTargetChange()) {
- return true;
- }
-
- Object targetObject = property.readPropertyDirectly(object);
- if (targetObject == null) {
- return true;
- }
-
- // if target is Fault, get id attributes from stored snapshot
- // to avoid unneeded fault triggering
- if (targetObject instanceof Fault) {
- DataRow storedSnapshot =
getObjectStore().getSnapshot(object.getObjectId());
- if (storedSnapshot == null) {
- throw new CayenneRuntimeException("No matching objects
found for ObjectId %s"
- + ". Object may have been deleted
externally.", object.getObjectId());
- }
-
- DbRelationship dbRel = rel.getDbRelationships().get(0);
- for (DbJoin join : dbRel.getJoins()) {
- String key = join.getSourceName();
- snapshot.put(key, storedSnapshot.get(key));
- }
-
- return true;
- }
-
- // target is resolved and we have an FK->PK to it,
- // so extract it from target...
- Persistent target = (Persistent) targetObject;
- Map<String, Object> idParts =
target.getObjectId().getIdSnapshot();
-
- // this may happen in uncommitted objects - see the warning in
- // the JavaDoc
- // of
- // this method.
- if (idParts.isEmpty()) {
- return true;
- }
-
- DbRelationship dbRel = rel.getDbRelationships().get(0);
- Map<String, Object> fk =
dbRel.srcFkSnapshotWithTargetSnapshot(idParts);
- snapshot.putAll(fk);
- return true;
- }
- });
-
- // process object id map
- // we should ignore any object id values if a corresponding attribute
- // is a part of relationship "toMasterPK", since those values have been
- // set above when db relationships where processed.
- Map<String, Object> thisIdParts = object.getObjectId().getIdSnapshot();
- if (thisIdParts != null) {
-
- // put only those that do not exist in the map
- for (Map.Entry<String, Object> entry : thisIdParts.entrySet()) {
- String nextKey = entry.getKey();
- if (!snapshot.containsKey(nextKey)) {
- snapshot.put(nextKey, entry.getValue());
- }
- }
- }
-
- return snapshot;
+ return new DataContextSnapshotBuilder(getEntityResolver(),
getObjectStore(), object)
+ .build();
}
@Override
@@ -877,8 +779,7 @@ public class DataContext implements ObjectContext {
* all transient persistent objects attached to this object via
* relationships.
* <p>
- * <i>Note that since 3.0 this method takes Object as an argument instead
of
- * a {@link DataObject}.</i>
+ * <i>Note that since 3.0 this method takes Object as an argument instead
of a {@link DataObject}.</i>
*
* @param object
* new object that needs to be made persistent.
@@ -918,8 +819,7 @@ public class DataContext implements ObjectContext {
injectInitialValue(object);
// now we need to find all arc changes, inject missing value holders
and
- // pull in
- // all transient connected objects
+ // pull in all transient connected objects
descriptor.visitProperties(new PropertyVisitor() {
@@ -968,8 +868,8 @@ public class DataContext implements ObjectContext {
}
/**
- * If ObjEntity qualifier is set, asks it to inject initial value to an
- * object. Also performs all Persistent initialization operations
+ * If ObjEntity qualifier is set, asks it to inject initial value to an
object.
+ * Also performs all Persistent initialization operations
*/
protected void injectInitialValue(Object obj) {
// must follow this exact order of property initialization per CAY-653,
@@ -1006,9 +906,8 @@ public class DataContext implements ObjectContext {
}
/**
- * Unregisters a Collection of DataObjects from the DataContext and the
- * underlying ObjectStore. This operation also unsets DataContext for
- * each object and changes its state to TRANSIENT.
+ * Unregisters a Collection of DataObjects from the DataContext and the
underlying ObjectStore.
+ * This operation also unsets DataContext for each object and changes its
state to {@link PersistenceState#TRANSIENT}
*
* @see #invalidateObjects(Collection)
*/
@@ -1071,10 +970,7 @@ public class DataContext implements ObjectContext {
if (objectStore.hasChanges()) {
GraphDiff diff = getObjectStore().getChanges();
- // call channel with changes BEFORE reverting them, so that any
- // interceptors
- // could record them
-
+ // call channel with changes BEFORE reverting them, so that any
interceptors could record them
if (channel != null) {
channel.onSync(this, diff, DataChannel.ROLLBACK_CASCADE_SYNC);
}
@@ -1094,8 +990,7 @@ public class DataContext implements ObjectContext {
* channel is a DataContext, it updates its objects with this context's
* changes, without a database update. If it is a DataDomain (the most
* common case), the changes are written to the database. To cause
cascading
- * commit all the way to the database, one must use {@link
#commitChanges()}
- * .
+ * commit all the way to the database, one must use {@link
#commitChanges()} .
*
* @since 1.2
* @see #commitChanges()
@@ -1163,27 +1058,17 @@ public class DataContext implements ObjectContext {
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.
+ // 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
+ // this event is caught by peer nested DataContexts to
synchronize the state
fireDataChannelCommitted(this, changes);
}
// "catch" is needed to unwrap OptimisticLockExceptions
@@ -1198,10 +1083,7 @@ public class DataContext implements ObjectContext {
}
}
- // merge changes from parent as well as changes caused by lifecycle
- // event
- // callbacks/listeners...
-
+ // merge changes from parent as well as changes caused by
lifecycle event callbacks/listeners...
CompoundDiff diff = new CompoundDiff();
diff.addAll(objectStore.getLifecycleEventInducedChanges());
@@ -1209,8 +1091,7 @@ public class DataContext implements ObjectContext {
diff.add(parentChanges);
}
- // this event is caught by child DataContexts to update temporary
- // ObjectIds with permanent
+ // this event is caught by child DataContexts to update temporary
ObjectIds with permanent
if (!diff.isNoop()) {
fireDataChannelCommitted(getChannel(), diff);
}
@@ -1496,11 +1377,6 @@ public class DataContext implements ObjectContext {
// serialization support
private void readObject(ObjectInputStream in) throws IOException,
ClassNotFoundException {
-
- // TODO: most of this should be in the superclass, especially the code
- // connecting
- // super transient ivars
-
// read non-transient properties
in.defaultReadObject();
@@ -1510,12 +1386,10 @@ public class DataContext implements ObjectContext {
objectStore.setDataRowCache(cache);
}
- // CayenneDataObjects have a transient DataContext
- // because at deserialize time the datacontext may need to be different
- // than the one at serialize time (for programmer defined reasons).
- // So, when a DataObject is resurrected because it's DataContext was
- // serialized, it will then set the objects DataContext to the correct
- // one
+ // CayenneDataObjects have a transient DataContext because at
deserialize time
+ // the DataContext may need to be different from the one at serialize
time (for programmer defined reasons).
+ // So, when a DataObject is resurrected because it's DataContext was
serialized,
+ // it will then set the objects DataContext to the correct one.
// If deserialized "otherwise", it will not have a DataContext.
synchronized (getObjectStore()) {
@@ -1526,11 +1400,8 @@ public class DataContext implements ObjectContext {
}
}
- // ... deferring initialization of transient properties of this context
- // till first
- // access, so that it can attach to Cayenne runtime using appropriate
- // thread
- // injector.
+ // ... deferring initialization of transient properties of this
context till first access,
+ // so that it can attach to Cayenne runtime using appropriate thread
injector.
}
/**
@@ -1556,13 +1427,9 @@ public class DataContext implements ObjectContext {
throw new IllegalArgumentException("Null ObjectId");
}
- // have to synchronize almost the entire method to prevent multiple
- // threads from
- // messing up dataobjects per CAY-845. Originally only parts of "else"
- // were
- // synchronized, but we had to expand the lock scope to ensure
- // consistent
- // behavior.
+ // have to synchronize almost the entire method to prevent multiple
threads from
+ // messing up DataObjects per CAY-845. Originally only parts of "else"
were synchronized,
+ // but we had to expand the lock scope to ensure consistent behavior.
synchronized (getGraphManager()) {
Persistent cachedObject = (Persistent)
getGraphManager().getNode(id);
@@ -1571,12 +1438,9 @@ public class DataContext implements ObjectContext {
int state = cachedObject.getPersistenceState();
- // TODO: Andrus, 1/24/2006 implement smart merge for modified
- // objects...
+ // TODO: Andrus, 1/24/2006 implement smart merge for modified
objects...
if (state != PersistenceState.MODIFIED && state !=
PersistenceState.DELETED) {
-
ClassDescriptor descriptor =
getEntityResolver().getClassDescriptor(id.getEntityName());
-
descriptor.injectValueHolders(cachedObject);
}
diff --git
a/cayenne/src/main/java/org/apache/cayenne/access/DataContextSnapshotBuilder.java
b/cayenne/src/main/java/org/apache/cayenne/access/DataContextSnapshotBuilder.java
new file mode 100644
index 000000000..77ab70849
--- /dev/null
+++
b/cayenne/src/main/java/org/apache/cayenne/access/DataContextSnapshotBuilder.java
@@ -0,0 +1,148 @@
+/*****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ ****************************************************************/
+
+package org.apache.cayenne.access;
+
+import org.apache.cayenne.CayenneRuntimeException;
+import org.apache.cayenne.DataRow;
+import org.apache.cayenne.Fault;
+import org.apache.cayenne.PersistenceState;
+import org.apache.cayenne.Persistent;
+import org.apache.cayenne.map.DbJoin;
+import org.apache.cayenne.map.DbRelationship;
+import org.apache.cayenne.map.EntityResolver;
+import org.apache.cayenne.map.ObjAttribute;
+import org.apache.cayenne.map.ObjEntity;
+import org.apache.cayenne.map.ObjRelationship;
+import org.apache.cayenne.reflect.AttributeProperty;
+import org.apache.cayenne.reflect.ClassDescriptor;
+import org.apache.cayenne.reflect.PropertyVisitor;
+import org.apache.cayenne.reflect.ToManyProperty;
+import org.apache.cayenne.reflect.ToOneProperty;
+
+import java.util.Map;
+
+/**
+ * {@link DataContext} delegates object snapshot creation to this class
+ *
+ * @see DataContext#currentSnapshot(Persistent)
+ */
+class DataContextSnapshotBuilder implements PropertyVisitor {
+
+ private final EntityResolver resolver;
+ private final ObjectStore objectStore;
+ private final Persistent object;
+ private DataRow snapshot;
+
+ DataContextSnapshotBuilder(EntityResolver resolver, ObjectStore
objectStore, Persistent object) {
+ this.resolver = resolver;
+ this.objectStore = objectStore;
+ this.object = object;
+ }
+
+ public DataRow build() {
+ if (object.getPersistenceState() == PersistenceState.HOLLOW
+ && object.getObjectContext() != null) {
+ return objectStore.getSnapshot(object.getObjectId());
+ }
+
+ ObjEntity entity = resolver.getObjEntity(object);
+ final ClassDescriptor descriptor =
resolver.getClassDescriptor(entity.getName());
+ snapshot = new DataRow(10);
+ snapshot.setEntityName(entity.getName());
+
+ descriptor.visitProperties(this);
+
+ // process object id map
+ // we should ignore any object id values if a corresponding attribute
+ // is a part of relationship "toMasterPK", since those values have been
+ // set above when db relationships where processed.
+ Map<String, Object> thisIdParts = object.getObjectId().getIdSnapshot();
+ if (thisIdParts != null) {
+
+ // put only those that do not exist in the map
+ for (Map.Entry<String, Object> entry : thisIdParts.entrySet()) {
+ String nextKey = entry.getKey();
+ if (!snapshot.containsKey(nextKey)) {
+ snapshot.put(nextKey, entry.getValue());
+ }
+ }
+ }
+
+ return snapshot;
+ }
+
+ public boolean visitAttribute(AttributeProperty property) {
+ ObjAttribute objAttr = property.getAttribute();
+ // processing compound attributes correctly
+ snapshot.put(objAttr.getDbAttributePath(),
property.readPropertyDirectly(object));
+ return true;
+ }
+
+ public boolean visitToMany(ToManyProperty property) {
+ // do nothing
+ return true;
+ }
+
+ public boolean visitToOne(ToOneProperty property) {
+ ObjRelationship rel = property.getRelationship();
+
+ // if target doesn't propagate its key value, skip it
+ if (rel.isSourceIndependentFromTargetChange()) {
+ return true;
+ }
+
+ Object targetObject = property.readPropertyDirectly(object);
+ if (targetObject == null) {
+ return true;
+ }
+
+ // if target is Fault, get id attributes from stored snapshot to avoid
unneeded fault triggering
+ if (targetObject instanceof Fault) {
+ DataRow storedSnapshot =
objectStore.getSnapshot(object.getObjectId());
+ if (storedSnapshot == null) {
+ throw new CayenneRuntimeException("No matching objects found
for ObjectId %s"
+ + ". Object may have been deleted externally.",
object.getObjectId());
+ }
+
+ DbRelationship dbRel = rel.getDbRelationships().get(0);
+ for (DbJoin join : dbRel.getJoins()) {
+ String key = join.getSourceName();
+ snapshot.put(key, storedSnapshot.get(key));
+ }
+
+ return true;
+ }
+
+ // target is resolved, and we have an FK->PK to it, so extract it from
target...
+ Persistent target = (Persistent) targetObject;
+ Map<String, Object> idParts = target.getObjectId().getIdSnapshot();
+
+ // this may happen in uncommitted objects - see the warning in
+ // the JavaDoc of this method.
+ if (idParts.isEmpty()) {
+ return true;
+ }
+
+ DbRelationship dbRel = rel.getDbRelationships().get(0);
+ Map<String, Object> fk =
dbRel.srcFkSnapshotWithTargetSnapshot(idParts);
+ snapshot.putAll(fk);
+ return true;
+ }
+}