Search in sources :

Example 11 with Persistable

use of org.datanucleus.enhancement.Persistable 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);
    }
}
Also used : Detachable(org.datanucleus.enhancement.Detachable) ApiAdapter(org.datanucleus.api.ApiAdapter) Persistable(org.datanucleus.enhancement.Persistable) LoadFieldManager(org.datanucleus.store.fieldmanager.LoadFieldManager) MakeTransientFieldManager(org.datanucleus.store.fieldmanager.MakeTransientFieldManager) AttachFieldManager(org.datanucleus.store.fieldmanager.AttachFieldManager) UnsetOwnerFieldManager(org.datanucleus.store.fieldmanager.UnsetOwnerFieldManager) DetachFieldManager(org.datanucleus.store.fieldmanager.DetachFieldManager) PersistFieldManager(org.datanucleus.store.fieldmanager.PersistFieldManager) SingleValueFieldManager(org.datanucleus.store.fieldmanager.SingleValueFieldManager) DeleteFieldManager(org.datanucleus.store.fieldmanager.DeleteFieldManager) L2CachePopulateFieldManager(org.datanucleus.cache.L2CachePopulateFieldManager) FieldManager(org.datanucleus.store.fieldmanager.FieldManager) SingleTypeFieldManager(org.datanucleus.store.fieldmanager.SingleTypeFieldManager) L2CacheRetrieveFieldManager(org.datanucleus.cache.L2CacheRetrieveFieldManager) DetachFieldManager(org.datanucleus.store.fieldmanager.DetachFieldManager) NucleusUserException(org.datanucleus.exceptions.NucleusUserException) EndOfFetchPlanGraphException(org.datanucleus.store.fieldmanager.AbstractFetchDepthFieldManager.EndOfFetchPlanGraphException)

Example 12 with Persistable

use of org.datanucleus.enhancement.Persistable in project datanucleus-core by datanucleus.

the class StateManagerImpl method refreshFieldsInFetchPlan.

/**
 * Refreshes from the database all fields in fetch plan.
 * Called by life-cycle transitions when the object undergoes a "transitionRefresh".
 */
public void refreshFieldsInFetchPlan() {
    int[] fieldNumbers = myFP.getMemberNumbers();
    if (fieldNumbers != null && fieldNumbers.length > 0) {
        clearDirtyFlags(fieldNumbers);
        ClassUtils.clearFlags(loadedFields, fieldNumbers);
        // Can't refresh PK fields!
        markPKFieldsAsLoaded();
        boolean callPostLoad = myFP.isToCallPostLoadFetchPlan(this.loadedFields);
        // Refresh the fetch plan fields in this object
        // Make sure that the version is reset upon fetch
        setTransactionalVersion(null);
        loadFieldsFromDatastore(fieldNumbers);
        if (cmd.hasRelations(myEC.getClassLoaderResolver())) {
            // Check for cascade refreshes to related objects
            for (int i = 0; i < fieldNumbers.length; i++) {
                AbstractMemberMetaData fmd = cmd.getMetaDataForManagedMemberAtAbsolutePosition(fieldNumbers[i]);
                RelationType relationType = fmd.getRelationType(myEC.getClassLoaderResolver());
                if (relationType != RelationType.NONE && fmd.isCascadeRefresh()) {
                    // Need to refresh the related field object(s)
                    Object value = provideField(fieldNumbers[i]);
                    if (value != null) {
                        if (fmd.hasContainer()) {
                            // TODO This should replace the SCO wrapper with a new one, or reload the wrapper
                            ApiAdapter api = getExecutionContext().getApiAdapter();
                            ContainerHandler containerHandler = myEC.getTypeManager().getContainerHandler(fmd.getType());
                            for (Object object : containerHandler.getAdapter(value)) {
                                if (api.isPersistable(object)) {
                                    getExecutionContext().refreshObject(object);
                                }
                            }
                        } else if (value instanceof Persistable) {
                            // Refresh any PC fields
                            myEC.refreshObject(value);
                        }
                    }
                }
            }
        }
        updateLevel2CacheForFields(fieldNumbers);
        if (callPostLoad) {
            postLoad();
        }
        getCallbackHandler().postRefresh(myPC);
    }
}
Also used : ApiAdapter(org.datanucleus.api.ApiAdapter) Persistable(org.datanucleus.enhancement.Persistable) RelationType(org.datanucleus.metadata.RelationType) ContainerHandler(org.datanucleus.store.types.ContainerHandler) AbstractMemberMetaData(org.datanucleus.metadata.AbstractMemberMetaData)

Example 13 with Persistable

use of org.datanucleus.enhancement.Persistable in project datanucleus-core by datanucleus.

the class StateManagerImpl method updateLevel2CacheForFields.

/**
 * Convenience method to update a Level2 cached version of this object if cacheable
 * and has not been modified during this transaction.
 * @param fieldNumbers Numbers of fields to update in L2 cached object
 */
protected void updateLevel2CacheForFields(int[] fieldNumbers) {
    String updateMode = (String) myEC.getProperty(PropertyNames.PROPERTY_CACHE_L2_UPDATE_MODE);
    if (updateMode != null && updateMode.equalsIgnoreCase("commit-only")) {
        return;
    }
    if (fieldNumbers == null || fieldNumbers.length == 0) {
        return;
    }
    Level2Cache l2cache = myEC.getNucleusContext().getLevel2Cache();
    if (l2cache != null && myEC.getNucleusContext().isClassCacheable(cmd) && !myEC.isObjectModifiedInTransaction(myID)) {
        CachedPC<Persistable> cachedPC = l2cache.get(myID);
        if (cachedPC != null) {
            // This originally just updated the L2 cache for fields where the L2 cache didn't have a value for that field, like this
            /*
                int[] cacheFieldsToLoad = ClassUtils.getFlagsSetTo(cachedPC.getLoadedFields(), fieldNumbers, false);
                if (cacheFieldsToLoad == null || cacheFieldsToLoad.length == 0)
                {
                    return;
                }
                */
            int[] cacheFieldsToLoad = fieldNumbers;
            CachedPC copyCachedPC = cachedPC.getCopy();
            if (NucleusLogger.CACHE.isDebugEnabled()) {
                NucleusLogger.CACHE.debug(Localiser.msg("026033", StringUtils.toJVMIDString(getObject()), myID, StringUtils.intArrayToString(cacheFieldsToLoad)));
            }
            provideFields(cacheFieldsToLoad, new L2CachePopulateFieldManager(this, copyCachedPC));
            // Replace the current L2 cached object with this one
            myEC.getNucleusContext().getLevel2Cache().put(getInternalObjectId(), copyCachedPC);
        }
    }
}
Also used : Persistable(org.datanucleus.enhancement.Persistable) Level2Cache(org.datanucleus.cache.Level2Cache) CachedPC(org.datanucleus.cache.CachedPC) L2CachePopulateFieldManager(org.datanucleus.cache.L2CachePopulateFieldManager)

Example 14 with Persistable

use of org.datanucleus.enhancement.Persistable in project datanucleus-core by datanucleus.

the class StateManagerImpl method initialiseForPersistentNew.

/**
 * Initialises a state manager to manage a transient instance that is becoming newly persistent.
 * A new object ID for the instance is obtained from the store manager and the object is inserted in the data store.
 * <p>
 * This constructor is used for assigning state managers to existing instances that are transitioning to a persistent state.
 * @param pc the instance being make persistent.
 * @param preInsertChanges Any changes to make before inserting
 */
public void initialiseForPersistentNew(Persistable pc, FieldValues preInsertChanges) {
    myPC = pc;
    myLC = myEC.getNucleusContext().getApiAdapter().getLifeCycleState(LifeCycleState.P_NEW);
    persistenceFlags = Persistable.READ_OK;
    for (int i = 0; i < loadedFields.length; ++i) {
        loadedFields[i] = true;
    }
    // Assign this StateManager to the PC
    replaceStateManager(myPC, this);
    myPC.dnReplaceFlags();
    saveFields();
    // Populate all fields that have "value-strategy" and are not datastore populated
    populateValueGenerationFields();
    if (preInsertChanges != null) {
        // Apply any pre-insert field updates
        preInsertChanges.fetchFields(this);
    }
    if (cmd.getIdentityType() == IdentityType.APPLICATION) {
        // Make sure any PK fields that are Persistable are persisted first, so we have an id to assign our identity
        int[] pkFieldNumbers = cmd.getPKMemberPositions();
        for (int i = 0; i < pkFieldNumbers.length; i++) {
            int fieldNumber = pkFieldNumbers[i];
            AbstractMemberMetaData fmd = cmd.getMetaDataForManagedMemberAtAbsolutePosition(fieldNumber);
            if (myEC.getMetaDataManager().getMetaDataForClass(fmd.getType(), getExecutionContext().getClassLoaderResolver()) != null) {
                try {
                    if (myEC.getMultithreaded()) {
                        myEC.getLock().lock();
                        lock.lock();
                    }
                    FieldManager prevFM = currFM;
                    try {
                        currFM = new SingleValueFieldManager();
                        myPC.dnProvideField(fieldNumber);
                        Persistable pkFieldPC = (Persistable) ((SingleValueFieldManager) currFM).fetchObjectField(fieldNumber);
                        if (pkFieldPC == null) {
                            throw new NucleusUserException(Localiser.msg("026016", fmd.getFullFieldName()));
                        }
                        if (!myEC.getApiAdapter().isPersistent(pkFieldPC)) {
                            // Make sure the PC field is persistent - can cause the insert of our object being managed by this SM via flush() when bidir relation
                            Object persistedFieldPC = myEC.persistObjectInternal(pkFieldPC, null, null, -1, ObjectProvider.PC);
                            replaceField(myPC, fieldNumber, persistedFieldPC, false);
                        }
                    } finally {
                        currFM = prevFM;
                    }
                } finally {
                    if (myEC.getMultithreaded()) {
                        lock.unlock();
                        myEC.getLock().unlock();
                    }
                }
            }
        }
    }
    // Set the identity of this object
    setIdentity(false);
    if (myEC.getTransaction().isActive()) {
        myEC.enlistInTransaction(this);
    }
    // Now in PERSISTENT_NEW so call any callbacks/listeners
    getCallbackHandler().postCreate(myPC);
    if (myEC.getManageRelations()) {
        // Managed Relations : register non-null bidir fields for later processing
        ClassLoaderResolver clr = myEC.getClassLoaderResolver();
        int[] relationPositions = cmd.getRelationMemberPositions(clr);
        if (relationPositions != null) {
            for (int i = 0; i < relationPositions.length; i++) {
                AbstractMemberMetaData mmd = cmd.getMetaDataForManagedMemberAtAbsolutePosition(relationPositions[i]);
                if (RelationType.isBidirectional(mmd.getRelationType(clr))) {
                    Object value = provideField(relationPositions[i]);
                    if (value != null) {
                        // Store the field with value of null so it gets checked
                        myEC.getRelationshipManager(this).relationChange(relationPositions[i], null, value);
                    }
                }
            }
        }
    }
}
Also used : LoadFieldManager(org.datanucleus.store.fieldmanager.LoadFieldManager) MakeTransientFieldManager(org.datanucleus.store.fieldmanager.MakeTransientFieldManager) AttachFieldManager(org.datanucleus.store.fieldmanager.AttachFieldManager) UnsetOwnerFieldManager(org.datanucleus.store.fieldmanager.UnsetOwnerFieldManager) DetachFieldManager(org.datanucleus.store.fieldmanager.DetachFieldManager) PersistFieldManager(org.datanucleus.store.fieldmanager.PersistFieldManager) SingleValueFieldManager(org.datanucleus.store.fieldmanager.SingleValueFieldManager) DeleteFieldManager(org.datanucleus.store.fieldmanager.DeleteFieldManager) L2CachePopulateFieldManager(org.datanucleus.cache.L2CachePopulateFieldManager) FieldManager(org.datanucleus.store.fieldmanager.FieldManager) SingleTypeFieldManager(org.datanucleus.store.fieldmanager.SingleTypeFieldManager) L2CacheRetrieveFieldManager(org.datanucleus.cache.L2CacheRetrieveFieldManager) Persistable(org.datanucleus.enhancement.Persistable) SingleValueFieldManager(org.datanucleus.store.fieldmanager.SingleValueFieldManager) NucleusUserException(org.datanucleus.exceptions.NucleusUserException) ClassLoaderResolver(org.datanucleus.ClassLoaderResolver) AbstractMemberMetaData(org.datanucleus.metadata.AbstractMemberMetaData)

Aggregations

Persistable (org.datanucleus.enhancement.Persistable)14 ExecutionContext (org.datanucleus.ExecutionContext)4 ObjectProvider (org.datanucleus.state.ObjectProvider)4 L2CachePopulateFieldManager (org.datanucleus.cache.L2CachePopulateFieldManager)3 L2CacheRetrieveFieldManager (org.datanucleus.cache.L2CacheRetrieveFieldManager)3 NucleusUserException (org.datanucleus.exceptions.NucleusUserException)3 AbstractMemberMetaData (org.datanucleus.metadata.AbstractMemberMetaData)3 DetachFieldManager (org.datanucleus.store.fieldmanager.DetachFieldManager)3 ApiAdapter (org.datanucleus.api.ApiAdapter)2 Level2Cache (org.datanucleus.cache.Level2Cache)2 Detachable (org.datanucleus.enhancement.Detachable)2 ClassNotResolvedException (org.datanucleus.exceptions.ClassNotResolvedException)2 EndOfFetchPlanGraphException (org.datanucleus.store.fieldmanager.AbstractFetchDepthFieldManager.EndOfFetchPlanGraphException)2 AttachFieldManager (org.datanucleus.store.fieldmanager.AttachFieldManager)2 DeleteFieldManager (org.datanucleus.store.fieldmanager.DeleteFieldManager)2 FieldManager (org.datanucleus.store.fieldmanager.FieldManager)2 LoadFieldManager (org.datanucleus.store.fieldmanager.LoadFieldManager)2 MakeTransientFieldManager (org.datanucleus.store.fieldmanager.MakeTransientFieldManager)2 PersistFieldManager (org.datanucleus.store.fieldmanager.PersistFieldManager)2 SingleTypeFieldManager (org.datanucleus.store.fieldmanager.SingleTypeFieldManager)2