use of org.datanucleus.store.fieldmanager.AbstractFetchDepthFieldManager.EndOfFetchPlanGraphException in project datanucleus-core by datanucleus.
the class StateManagerImpl method detach.
/**
* Method to detach this object.
* If the object is detachable then it will be migrated to DETACHED state, otherwise will migrate to TRANSIENT. Used by "DetachAllOnCommit"/"DetachAllOnRollback"
* @param state State for the detachment process
*/
public void detach(FetchPlanState state) {
if (myEC == null) {
return;
}
ApiAdapter api = myEC.getApiAdapter();
if (myLC.isDeleted() || api.isDetached(myPC) || isDetaching()) {
// Already deleted, detached or being detached
return;
}
// Check if detachable ... if so then we detach a copy, otherwise we return a transient copy
boolean detachable = api.isDetachable(myPC);
if (detachable) {
if (NucleusLogger.PERSISTENCE.isDebugEnabled()) {
NucleusLogger.PERSISTENCE.debug(Localiser.msg("010009", StringUtils.toJVMIDString(myPC), "" + state.getCurrentFetchDepth()));
}
// Call any "pre-detach" listeners
getCallbackHandler().preDetach(myPC);
}
try {
setDetaching(true);
String detachedState = myEC.getNucleusContext().getConfiguration().getStringProperty(PropertyNames.PROPERTY_DETACH_DETACHED_STATE);
if (detachedState.equalsIgnoreCase("all")) {
loadUnloadedFields();
} else if (detachedState.equalsIgnoreCase("loaded")) {
// Do nothing since just using currently loaded fields
} else {
// Using fetch-groups, so honour detachmentOptions for loading/unloading
if ((myEC.getFetchPlan().getDetachmentOptions() & FetchPlan.DETACH_LOAD_FIELDS) != 0) {
// Load any unloaded fetch-plan fields
loadUnloadedFieldsInFetchPlan();
}
if ((myEC.getFetchPlan().getDetachmentOptions() & FetchPlan.DETACH_UNLOAD_FIELDS) != 0) {
// Unload any loaded fetch-plan fields that aren't in the current fetch plan
unloadNonFetchPlanFields();
// Remove the values from the detached object - not required by the spec
int[] unloadedFields = ClassUtils.getFlagsSetTo(loadedFields, cmd.getAllMemberPositions(), false);
if (unloadedFields != null && unloadedFields.length > 0) {
Persistable dummyPC = myPC.dnNewInstance(this);
myPC.dnCopyFields(dummyPC, unloadedFields);
replaceStateManager(dummyPC, null);
}
}
}
// Detach all (loaded) fields in the FetchPlan
FieldManager detachFieldManager = new DetachFieldManager(this, cmd.getSCOMutableMemberFlags(), myFP, state, false);
for (int i = 0; i < loadedFields.length; i++) {
if (loadedFields[i]) {
try {
// Just fetch the field since we are usually called in postCommit() so dont want to update it
detachFieldManager.fetchObjectField(i);
} catch (EndOfFetchPlanGraphException eofpge) {
Object value = provideField(i);
if (api.isPersistable(value)) {
// PC field beyond end of graph
ObjectProvider valueOP = myEC.findObjectProvider(value);
if (!api.isDetached(value) && !(valueOP != null && valueOP.isDetaching())) {
// Field value is not detached or being detached so unload it
String fieldName = cmd.getMetaDataForManagedMemberAtAbsolutePosition(i).getName();
if (NucleusLogger.PERSISTENCE.isDebugEnabled()) {
NucleusLogger.PERSISTENCE.debug(Localiser.msg("026032", StringUtils.toJVMIDString(myPC), IdentityUtils.getPersistableIdentityForId(myID), fieldName));
}
unloadField(fieldName);
}
}
// TODO What if we have collection/map that includes some objects that are not detached?
// Currently we just leave as persistent etc but should we????
// The problem is that with 1-N bidir fields we could unload the field incorrectly
}
}
}
if (detachable) {
// Migrate the lifecycle state to DETACHED_CLEAN
myLC = myLC.transitionDetach(this);
// Update the object with its detached state
myPC.dnReplaceFlags();
((Detachable) myPC).dnReplaceDetachedState();
// Call any "post-detach" listeners
// there is no copy, so give the same object
getCallbackHandler().postDetach(myPC, myPC);
Persistable toCheckPC = myPC;
Object toCheckID = myID;
disconnect();
if (!toCheckPC.dnIsDetached()) {
// Sanity check on the objects detached state
throw new NucleusUserException(Localiser.msg("026025", toCheckPC.getClass().getName(), toCheckID));
}
} else {
// Warn the user since they selected detachAllOnCommit
NucleusLogger.PERSISTENCE.warn(Localiser.msg("026031", myPC.getClass().getName(), IdentityUtils.getPersistableIdentityForId(myID)));
// Make the object transient
makeTransient(null);
}
} finally {
setDetaching(false);
}
}
Aggregations