Package org.datanucleus.store.mapped.scostore

Source Code of org.datanucleus.store.mapped.scostore.FKSetStore

/**********************************************************************
Copyright (c) 2002 Mike Martin (TJDO) and others. All rights reserved.
Licensed 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

    http://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.

Contributors:
2003 Andy Jefferson - added comments
2003 Erik Bengtson - removed unused variable and import
2003 Erik Bengtson - fixed bug [832635] Imcompatiblities in casting
                    inherited classes in query
2003 Andy Jefferson - coding standards
2004 Andy Jefferson - updated retrieval of ColumnLists
2004 Andy Jefferson - addition of removeAll()
2004 Andy Jefferson - moved statements to AbstractSetStore
2004 Andy Jefferson - added support for inverse 1-N unidirectional
2005 Andy Jefferson - added dependent-element when removed from collection
    ...
**********************************************************************/
package org.datanucleus.store.mapped.scostore;

import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;

import org.datanucleus.ClassLoaderResolver;
import org.datanucleus.FetchPlan;
import org.datanucleus.exceptions.NucleusUserException;
import org.datanucleus.metadata.AbstractClassMetaData;
import org.datanucleus.metadata.AbstractMemberMetaData;
import org.datanucleus.metadata.CollectionMetaData;
import org.datanucleus.metadata.Relation;
import org.datanucleus.store.ExecutionContext;
import org.datanucleus.store.FieldValues;
import org.datanucleus.store.ObjectProvider;
import org.datanucleus.store.mapped.DatastoreClass;
import org.datanucleus.store.mapped.MappedStoreManager;
import org.datanucleus.store.mapped.mapping.JavaTypeMapping;
import org.datanucleus.store.mapped.mapping.MappingConsumer;
import org.datanucleus.util.ClassUtils;
import org.datanucleus.util.NucleusLogger;
import org.datanucleus.util.StringUtils;

/**
* Representation of an Inverse Set as part of a relationship. This class is
* used where you have a 1-N and the tables are not joined via a link table.
* That is there is an owner table, and a collection table, and the collection
* table has a column being the id of the owner table. This is in contrast to
* NormalSetStore which represents 1-N relationships using a link table.
* There are 2 possible uses here
* <UL>
* <LI><B>bidirectional</B> - where the owner has a Collection of elements, and
* the element has an owner. In this case the element class will have an owner field
* which can be updated directly</LI>
* <LI><B>unidirectional</B> - where the owner has a Collection of elements, but
* the element knows nothing about the owner. In this case the element class has no
* owner field. In this case the column in the element table has to be updated
* directly.</LI>
* </UL>
*/
public abstract class FKSetStore extends AbstractSetStore
{
    /** Field number of owner link in element class. */
    private final int ownerFieldNumber;

    /**
     * Constructor for the relationship representation.
     * @param fmd The MetaData for the field that this represents
     * @param storeMgr The MappedStoreManager managing the associated datastore.
     * @param clr The ClassLoaderResolver
     * @param specialization The datastore-specific specialization
     **/
    public FKSetStore(AbstractMemberMetaData fmd, MappedStoreManager storeMgr, ClassLoaderResolver clr,
                      AbstractSetStoreSpecialization specialization)
    {
        super(storeMgr, clr, specialization);

        setOwner(fmd, clr);
        CollectionMetaData colmd = fmd.getCollection();
        if (colmd == null)
        {
            throw new NucleusUserException(LOCALISER.msg("056001", fmd.getFullFieldName()));
        }

        // Load the element class
        elementType = colmd.getElementType();
        Class element_class = clr.classForName(elementType);

        if (ClassUtils.isReferenceType(element_class))
        {
            elementIsPersistentInterface = storeMgr.getNucleusContext().getMetaDataManager().isPersistentInterface(element_class.getName());
            if (elementIsPersistentInterface)
            {
                emd = storeMgr.getNucleusContext().getMetaDataManager().getMetaDataForInterface(element_class,clr);
            }
            else
            {
                // Take the metadata for the first implementation of the reference type
                emd = storeMgr.getNucleusContext().getMetaDataManager().getMetaDataForImplementationOfReference(element_class,null,clr);
                if (emd != null)
                {
                    // Pretend we have a relationship with this one implementation
                    //elementType = emd.getFullClassName();
                }
            }
        }
        else
        {
            // Check that the element class has MetaData
            emd = storeMgr.getNucleusContext().getMetaDataManager().getMetaDataForClass(element_class, clr);
        }
        if (emd == null)
        {
            throw new NucleusUserException(LOCALISER.msg("056003", element_class.getName(), fmd.getFullFieldName()));
        }

        elementInfo = getElementInformationForClass();
        if (elementInfo == null || elementInfo.length == 0)
        {
            throw new NucleusUserException(LOCALISER.msg("056075", ownerMemberMetaData.getFullFieldName(), elementType));
        }
        elementMapping = elementInfo[0].getDatastoreClass().getIdMapping(); // Just use the first element type as the guide for the element mapping
        elementsAreEmbedded = false; // Can't embed element when using FK relation
        elementsAreSerialised = false; // Can't serialise element when using FK relation

        // Get the field in the element table (if any)
        if (fmd.getMappedBy() != null)
        {
            // 1-N FK bidirectional
            // The element class has a field for the owner.
            AbstractMemberMetaData eofmd = emd.getMetaDataForMember(fmd.getMappedBy());
            if (eofmd == null)
            {
                throw new NucleusUserException(LOCALISER.msg("056024", fmd.getFullFieldName(),
                    fmd.getMappedBy(), element_class.getName()));
            }

            // Check that the type of the element "mapped-by" field is consistent with the owner type
            //TODO check does not work if mapped by has relation to super class. Enable this
            //and run the PersistentInterfacesTest to reproduce the issue
            //TODO there is equivalent code in FKList and FKMap that was
            //not commented out. When fixing, add tests for all types of Inverses
            /*
            if (!clr.isAssignableFrom(eofmd.getType(), mmd.getAbstractClassMetaData().getFullClassName()))
            {
                throw new NucleusUserException(LOCALISER.msg("056025", mmd.getFullFieldName(),
                    eofmd.getFullFieldName(), eofmd.getTypeName(), mmd.getAbstractClassMetaData().getFullClassName()));
            }
            */
            String ownerFieldName = eofmd.getName();
            ownerFieldNumber = emd.getAbsolutePositionOfMember(ownerFieldName);
            ownerMapping = elementInfo[0].getDatastoreClass().getMemberMapping(eofmd);
            if (ownerMapping == null)
            {
                throw new NucleusUserException(LOCALISER.msg("056029",
                    fmd.getAbstractClassMetaData().getFullClassName(), fmd.getName(), elementType, ownerFieldName));
            }
            if (isEmbeddedMapping(ownerMapping))
            {
                throw new NucleusUserException(LOCALISER.msg("056026",
                    ownerFieldName, elementType, eofmd.getTypeName(), fmd.getClassName()));
            }
        }
        else
        {
            // 1-N FK unidirectional
            // The element class knows nothing about the owner (but its table has external mappings)
            ownerFieldNumber = -1;
            ownerMapping = elementInfo[0].getDatastoreClass().getExternalMapping(fmd, MappingConsumer.MAPPING_TYPE_EXTERNAL_FK);
            if (ownerMapping == null)
            {
                throw new NucleusUserException(LOCALISER.msg("056030",
                    fmd.getAbstractClassMetaData().getFullClassName(), fmd.getName(), elementType));
            }
        }

        relationDiscriminatorMapping = elementInfo[0].getDatastoreClass().getExternalMapping(fmd, MappingConsumer.MAPPING_TYPE_EXTERNAL_FK_DISCRIM);
        if (relationDiscriminatorMapping != null)
        {
            relationDiscriminatorValue = fmd.getValueForExtension("relation-discriminator-value");
            if (relationDiscriminatorValue == null)
            {
                // No value defined so just use the field name
                relationDiscriminatorValue = fmd.getFullFieldName();
            }
        }

        // TODO Remove use of containerTable - just use elementTable[0] or equivalent
        containerTable = elementInfo[0].getDatastoreClass();
        if (fmd.getMappedBy() != null && ownerMapping.getDatastoreContainer() != containerTable)
        {
            // Element and owner don't have consistent tables so use the one with the mapping
            // e.g collection is of subclass, yet superclass has the link back to the owner
            containerTable = ownerMapping.getDatastoreContainer();
        }
    }

    /**
     * Utility to update a foreign-key (and distinguisher) in the element in the case of
     * a unidirectional 1-N relationship.
     * @param sm StateManager for the owner
     * @param element The element to update
     * @param owner The owner object to set in the FK
     * @return Whether it was performed successfully
     */
    private boolean updateElementFk(ObjectProvider sm, Object element, Object owner)
    {
        if (element == null)
        {
            return false;
        }
        validateElementForWriting(sm, element, null);

        return updateElementFkInternal(sm, element, owner);
    }

    /**
     * This seems to return the field number in the element of the relation when it is a bidirectional
     * relation.
     * @param sm StateManager of the owner
     * @return The field number in the element for this relation
     */
    protected int getFieldNumberInElementForBidirectional(ObjectProvider sm)
    {
        if (ownerFieldNumber < 0)
        {
            // Unidirectional
            return -1;
        }
        // This gives a different result when using persistent interfaces.
        // For example with the JDO2 TCK, org.apache.jdo.tck.pc.company.PIDepartmentImpl.employees will
        // return 3, yet the ownerMemberMetaData.getRelatedMetaData returns 8 since the generated implementation
        // will have all fields in a single MetaData (numbering from 0), whereas in a normal inheritance
        // tree there will be multiple MetaData (the root starting from 0)
        return sm.getClassMetaData().getAbsolutePositionOfMember(ownerMemberMetaData.getMappedBy());
    }

    /**
     * Method to update the collection to be the supplied collection of elements.
     * @param sm StateManager of the object
     * @param coll The collection to use
     */
    public void update(ObjectProvider sm, Collection coll)
    {
        if (coll == null || coll.isEmpty())
        {
            clear(sm);
            return;
        }

        // Find existing elements, and remove any that are no longer present
        Iterator elemIter = iterator(sm);
        Collection existing = new HashSet();
        while (elemIter.hasNext())
        {
            Object elem = elemIter.next();
            if (!coll.contains(elem))
            {
                remove(sm, elem, -1, true);
            }
            else
            {
                existing.add(elem);
            }
        }

        if (existing.size() != coll.size())
        {
            // Add any elements that aren't already present
            Iterator iter = coll.iterator();
            while (iter.hasNext())
            {
                Object elem = iter.next();
                if (!existing.contains(elem))
                {
                    add(sm, elem, 0);
                }
            }
        }
    }

    /**
     * Method to add an object to the relationship at the collection end.
     * @param sm StateManager of the owner of the Set
     * @param element Element to be added
     * @return Success indicator
     */
    public boolean add(final ObjectProvider sm, Object element, int size)
    {
        if (element == null)
        {
            // Sets allow no duplicates
            throw new NucleusUserException(LOCALISER.msg("056039"));
        }

        // Make sure that the element is persisted in the datastore (reachability)
        final Object newOwner = sm.getObject();
        ExecutionContext ec = sm.getExecutionContext();
        boolean inserted = validateElementForWriting(sm, element, new FieldValues()
        {
            public void fetchFields(ObjectProvider esm)
            {
                // Find the (element) table storing the FK back to the owner
                boolean isPersistentInterface = storeMgr.getNucleusContext().getMetaDataManager().isPersistentInterface(elementType);
                DatastoreClass elementTable = null;
                if (isPersistentInterface)
                {
                    elementTable = storeMgr.getDatastoreClass(
                        storeMgr.getNucleusContext().getMetaDataManager().getImplementationNameForPersistentInterface(elementType), clr);
                }
                else
                {
                    elementTable = storeMgr.getDatastoreClass(elementType, clr);
                }
                if (elementTable == null)
                {
                    // "subclass-table", persisted into table of other class
                    AbstractClassMetaData[] managingCmds = storeMgr.getClassesManagingTableForClass(emd, clr);
                    if (managingCmds != null && managingCmds.length > 0)
                    {
                        // Find which of these subclasses is appropriate for this element
                        for (int i=0;i<managingCmds.length;i++)
                        {
                            Class tblCls = clr.classForName(managingCmds[i].getFullClassName());
                            if (tblCls.isAssignableFrom(esm.getObject().getClass()))
                            {
                                elementTable = storeMgr.getDatastoreClass(managingCmds[i].getFullClassName(), clr);
                                break;
                            }
                        }
                    }
                }
                if (elementTable != null)
                {
                    JavaTypeMapping externalFKMapping = elementTable.getExternalMapping(ownerMemberMetaData,
                        MappingConsumer.MAPPING_TYPE_EXTERNAL_FK);
                    if (externalFKMapping != null)
                    {
                        // The element has an external FK mapping so set the value it needs to use in the INSERT
                        esm.setAssociatedValue(externalFKMapping, sm.getObject());
                    }
                    if (relationDiscriminatorMapping != null)
                    {
                        // Element type has a shared FK so set the discriminator value for this relation
                        esm.setAssociatedValue(relationDiscriminatorMapping, relationDiscriminatorValue);
                    }
                }

                if (getFieldNumberInElementForBidirectional(esm) >= 0)
                {
                    // TODO Move this into RelationshipManager
                    // Managed Relations : 1-N bidir, so make sure owner is correct at persist
                    Object currentOwner = esm.provideField(getFieldNumberInElementForBidirectional(esm));
                    if (currentOwner == null)
                    {
                        // No owner, so correct it
                        NucleusLogger.PERSISTENCE.info(LOCALISER.msg("056037",
                            sm.toPrintableID(), ownerMemberMetaData.getFullFieldName(),
                            StringUtils.toJVMIDString(esm.getObject())));
                        esm.replaceFieldMakeDirty(getFieldNumberInElementForBidirectional(esm), newOwner);
                    }
                    else if (currentOwner != newOwner && sm.getReferencedPC() == null)
                    {
                        // Owner of the element is neither this container and not being attached
                        // Inconsistent owner, so throw exception
                        throw new NucleusUserException(LOCALISER.msg("056038",
                            sm.toPrintableID(), ownerMemberMetaData.getFullFieldName(),
                            StringUtils.toJVMIDString(esm.getObject()),
                            StringUtils.toJVMIDString(currentOwner)));
                    }
                }
            }

            public void fetchNonLoadedFields(ObjectProvider sm)
            {
            }
            public FetchPlan getFetchPlanForLoading()
            {
                return null;
            }
        });

        if (inserted)
        {
            // Element has just been persisted so the FK will be set
            return true;
        }
        else
        {
            // Element was already persistent so make sure the FK is in place
            // TODO This is really "ManagedRelationships" so needs to go in RelationshipManager
            ObjectProvider elementSM = ec.findObjectProvider(element);
            if (getFieldNumberInElementForBidirectional(elementSM) >= 0)
            {
                // Managed Relations : 1-N bidir, so update the owner of the element
                ec.getApiAdapter().isLoaded(elementSM, getFieldNumberInElementForBidirectional(elementSM)); // Ensure is loaded
                Object oldOwner = elementSM.provideField(getFieldNumberInElementForBidirectional(elementSM));
                if (oldOwner != newOwner)
                {
                    if (NucleusLogger.PERSISTENCE.isDebugEnabled())
                    {
                        NucleusLogger.PERSISTENCE.debug(LOCALISER.msg("055009", sm.toPrintableID(),
                            ownerMemberMetaData.getFullFieldName(), StringUtils.toJVMIDString(element)));
                    }

                    int relatedFieldNumber = getFieldNumberInElementForBidirectional(elementSM);
                    elementSM.replaceFieldMakeDirty(relatedFieldNumber, newOwner);
                    if (ec.getManageRelations())
                    {
                        // Managed Relationships - add the change we've made here to be analysed at flush
                        ec.getRelationshipManager(elementSM).relationChange(relatedFieldNumber, oldOwner, newOwner);
                    }

                    if (ec.isFlushing())
                    {
                        elementSM.flush();
                    }
                }
                return oldOwner != newOwner;
            }
            else
            {
                // 1-N unidir so update the FK if not set to be contained in the set
                boolean contained = contains(sm, element);
                return (contained ? false : updateElementFk(sm, element, newOwner));
            }
        }
    }
    /**
     * Method to add a collection of object to the relationship at the
     * collection end.
     * @param sm StateManager of the Set
     * @param elements Elements to be added
     * @return Success indicator
     **/
    public boolean addAll(ObjectProvider sm, Collection elements, int size)
    {
        if (elements == null || elements.size() == 0)
        {
            return false;
        }

        boolean success = false;

        Iterator iter = elements.iterator();
        while (iter.hasNext())
        {
            if (add(sm, iter.next(), -1))
            {
                success = true;
            }
        }

        return success;
    }

    /**
     * Method to remove the link to the collection object specified.
     * Depending on the column characteristics in the collection table, the id of the owner field may
     * be NULLed, or the record may be deleted completely (as per cascade-delete in EJB).
     * @param ownerSM The StateManager of the Set
     * @param element The element of the collection to be deleted.
     * @param allowDependentField Whether to allow any cascade deletes caused by this removal
     * @return A success indicator.
     */
    public boolean remove(ObjectProvider ownerSM, Object element, int size, boolean allowDependentField)
    {
        if (element == null)
        {
            return false;
        }
        if (!validateElementForReading(ownerSM, element))
        {
            return false;
        }

        // Find the state manager for the element
        Object elementToRemove = element;
        ExecutionContext ec = ownerSM.getExecutionContext();
        if (ec.getApiAdapter().isDetached(element)) // User passed in detached object to collection.remove()!
        {
            // Find an attached equivalent of this detached object (DON'T attach the object itself)
            elementToRemove = ec.findObject(ec.getApiAdapter().getIdForObject(element), true, false, element.getClass().getName());
        }
        ObjectProvider elementSM = ec.findObjectProvider(elementToRemove);

        Object oldOwner = null;
        if (ownerFieldNumber >= 0)
        {
            if (!ec.getApiAdapter().isDeleted(elementToRemove))
            {
                // Find the existing owner if the record hasn't already been deleted
                ec.getApiAdapter().isLoaded(elementSM, ownerFieldNumber);
                oldOwner = elementSM.provideField(ownerFieldNumber);
            }
        }
        else
        {
            // TODO Check if the element is managed by a different owner now
        }

        // Owner of the element has been changed
        if (ownerFieldNumber >= 0 && oldOwner != ownerSM.getObject() && oldOwner != null)
        {
            return false;
        }

        boolean deleteElement = checkRemovalOfElementShouldDelete(ownerSM);
        if (deleteElement)
        {
            if (ec.getApiAdapter().isPersistable(elementToRemove) && ec.getApiAdapter().isDeleted(elementToRemove))
            {
                // Element is waiting to be deleted so flush it (it has the FK)
                elementSM.flush();
            }
            else
            {
                // Element not yet marked for deletion so go through the normal process
                ec.deleteObjectInternal(elementToRemove);
            }
        }
        else
        {
            // Perform any necessary "managed relationships" updates on the element (when bidirectional)
            manageRemovalOfElement(ownerSM, elementToRemove);

            // Update the datastore FK
            updateElementFk(ownerSM, elementToRemove, null);
        }

        return true;
    }

    /**
     * Method to remove the links to a collection of elements specified.
     * Depending on the column characteristics in the collection table, the id
     * of the owner fields may be NULLed, or the records may be deleted
     * completely.
     *
     * @param sm The StateManager of the Set
     * @param elements The elements of the collection to be deleted.
     * @return A success indicator.
     **/
    public boolean removeAll(ObjectProvider sm, Collection elements, int size)
    {
        if (elements == null || elements.size() == 0)
        {
            return false;
        }

        // Check the first element for whether we can null the column or
        // whether we have to delete
        boolean success = true;

        Iterator iter=elements.iterator();
        while (iter.hasNext())
        {
            if (remove(sm, iter.next(), -1, true))
            {
                success = false;
            }
        }

        return success;
    }

    /**
     * Convenience method to return if the removal of an element should delete the element.
     * @param ownerSM StateManager for the owner
     * @return Whether we should delete the element on removing from the collection
     */
    protected boolean checkRemovalOfElementShouldDelete(ObjectProvider ownerSM)
    {
        boolean delete = false;
        boolean dependent = ownerMemberMetaData.getCollection().isDependentElement();
        if (ownerMemberMetaData.isCascadeRemoveOrphans())
        {
            dependent = true;
        }
        if (dependent)
        {
            // Elements are dependent and can't exist on their own, so delete them all
            if (NucleusLogger.DATASTORE.isDebugEnabled())
            {
                NucleusLogger.DATASTORE.debug(LOCALISER.msg("056034"));
            }
            delete = true;
        }
        else
        {
            if (ownerMapping.isNullable())
            {
                // Field is not dependent, but is nullable so we null the FK
                if (NucleusLogger.DATASTORE.isDebugEnabled())
                {
                    NucleusLogger.DATASTORE.debug(LOCALISER.msg("056036"));
                }
                delete = false;
            }
            else
            {
                // Field is not dependent, and is not nullable so we just delete the elements
                if (NucleusLogger.DATASTORE.isDebugEnabled())
                {
                    NucleusLogger.DATASTORE.debug(LOCALISER.msg("056035"));
                }
                delete = true;
            }
        }
        return delete;
    }

    /**
     * Convenience method to manage the removal of an element from the collection, performing
     * any necessary "managed relationship" updates when the field is bidirectional.
     * @param ownerSM StateManager for the collection owner
     * @param element The element
     */
    protected void manageRemovalOfElement(ObjectProvider ownerSM, Object element)
    {
        ExecutionContext ec = ownerSM.getExecutionContext();
        if (relationType == Relation.ONE_TO_MANY_BI)
        {
            // TODO Move this into RelationshipManager
            // Managed Relations : 1-N bidirectional so null the owner on the elements
            if (!ec.getApiAdapter().isDeleted(element))
            {
                ObjectProvider elementSM = ec.findObjectProvider(element);
                if (elementSM != null)
                {
                    // Null the owner of the element
                    if (NucleusLogger.PERSISTENCE.isDebugEnabled())
                    {
                        NucleusLogger.PERSISTENCE.debug(LOCALISER.msg("055010",
                            ownerSM.toPrintableID(),
                            ownerMemberMetaData.getFullFieldName(),
                            StringUtils.toJVMIDString(element)));
                    }

                    int relatedFieldNumber = getFieldNumberInElementForBidirectional(elementSM);
                    Object currentValue = elementSM.provideField(relatedFieldNumber);
                    if (currentValue != null)
                    {
                        elementSM.replaceFieldMakeDirty(relatedFieldNumber, null);
                        if (ec.isFlushing())
                        {
                            // Make sure this change gets flushed
                            elementSM.flush();
                        }
                    }
                }
            }
        }
    }

    /**
     * Method to allow the Set relationship to be cleared out.
     * This is called by the List.clear() method, or when the container object is being deleted
     * and the elements are to be removed (maybe for dependent field), or also when updating a Collection
     * and removing all existing prior to adding all new.
     * @param ownerSM StateManager of the Set
     */
    public void clear(ObjectProvider ownerSM)
    {
        ExecutionContext ec = ownerSM.getExecutionContext();
        boolean deleteElements = checkRemovalOfElementShouldDelete(ownerSM);
        if (deleteElements)
        {
            // Find elements present in the datastore and delete them one-by-one
            Iterator elementsIter = iterator(ownerSM);
            if (elementsIter != null)
            {
                while (elementsIter.hasNext())
                {
                    Object element = elementsIter.next();
                    if (ec.getApiAdapter().isPersistable(element) && ec.getApiAdapter().isDeleted(element))
                    {
                        // Element is waiting to be deleted so flush it (it has the FK)
                        ObjectProvider elementSM = ec.findObjectProvider(element);
                        elementSM.flush();
                    }
                    else
                    {
                        // Element not yet marked for deletion so go through the normal process
                        ec.deleteObjectInternal(element);
                    }
                }
            }
        }
        else
        {
            // Perform any necessary "managed relationships" updates on the element
            ec.getApiAdapter().isLoaded(ownerSM, ownerMemberMetaData.getAbsoluteFieldNumber()); // Make sure the field is loaded
            Collection value = (Collection) ownerSM.provideField(ownerMemberMetaData.getAbsoluteFieldNumber());
            Iterator elementsIter = null;
            if (value != null && !value.isEmpty())
            {
                elementsIter = value.iterator();
            }
            else
            {
                // Maybe deleting the owner with optimistic transactions so the elements are no longer cached
                elementsIter = iterator(ownerSM);
            }
            if (elementsIter != null)
            {
                while (elementsIter.hasNext())
                {
                    Object element = elementsIter.next();
                    manageRemovalOfElement(ownerSM, element);
                }
            }
            clearInternal(ownerSM, ec);
        }
    }

    protected abstract void clearInternal(ObjectProvider ownerSM, ExecutionContext ec);
    protected abstract boolean updateElementFkInternal(ObjectProvider sm, Object element, Object owner);
}
TOP

Related Classes of org.datanucleus.store.mapped.scostore.FKSetStore

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.