Package org.datanucleus.store.mapped.scostore

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

/**********************************************************************
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 - coding standards
2004 Andy Jefferson - addition of getGetStatement for inherited values
2004 Andy Jefferson - addition of query methods
2004 Marco Schulze  - replaced catch(NotPersistenceCapableException ...) by
                     advance-check via TypeManager.isSupportedType(...)
2004 Andy Jefferson - moved statements to AbstractMapStore
2005 Andy Jefferson - allow for embedded keys/values
    ...
**********************************************************************/
package org.datanucleus.store.mapped.scostore;

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

import org.datanucleus.ClassLoaderResolver;
import org.datanucleus.api.ApiAdapter;
import org.datanucleus.exceptions.NucleusDataStoreException;
import org.datanucleus.metadata.AbstractMemberMetaData;
import org.datanucleus.metadata.MapMetaData;
import org.datanucleus.store.ExecutionContext;
import org.datanucleus.store.ObjectProvider;
import org.datanucleus.store.connection.ManagedConnection;
import org.datanucleus.store.mapped.DatastoreContainerObject;
import org.datanucleus.store.mapped.exceptions.MappedDatastoreException;
import org.datanucleus.store.mapped.mapping.JavaTypeMapping;
import org.datanucleus.store.scostore.SetStore;
import org.datanucleus.util.ClassUtils;
import org.datanucleus.util.NucleusLogger;

/**
* Representation of the backing store for a Map.
* Uses a join table to link the owning container object with the values/keys
* contained in the map.
**/
public abstract class JoinMapStore extends AbstractMapStore
{
    private SetStore keySetStore = null;
    private SetStore valueSetStore = null;
    private SetStore entrySetStore = null;
   
    /**
     * when the element mappings columns can't be part of the primary key
     * by datastore limitations like BLOB types.
     * An adapter mapping is used to be a kind of "index"
     */
    protected final JavaTypeMapping adapterMapping;   

    protected ClassLoaderResolver clr;

    /**
     * Constructor for an Inverse Map.
     * @param mapTable Join table for the Map
     * @param clr The ClassLoaderResolver
     * @param specialization The Specialization
     **/
    public JoinMapStore(DatastoreContainerObject mapTable, ClassLoaderResolver clr,
                        JavaTypeMapping ownerMapping, JavaTypeMapping keyMapping, JavaTypeMapping valueMapping, JavaTypeMapping orderMapping,
                        String keyType, boolean isEmbeddedKey, boolean isSerialisedKey, String valueType, boolean isEmbeddedValue,
                        boolean isSerialisedValue, AbstractMemberMetaData ownerMemberMetaData, AbstractMapStoreSpecialization specialization)
    {
        super(mapTable.getStoreManager(), specialization);
        this.clr = clr;

        this.mapTable = mapTable;
        setOwner(ownerMemberMetaData, clr);

        this.ownerMapping = ownerMapping;
        this.keyMapping = keyMapping;
        this.valueMapping = valueMapping;
        this.adapterMapping = orderMapping;

        this.keyType = keyType;
        this.keysAreEmbedded = isEmbeddedKey;
        this.keysAreSerialised = isSerialisedKey;
        this.valueType = valueType;
        this.valuesAreEmbedded = isEmbeddedValue;
        this.valuesAreSerialised = isSerialisedValue;

        Class key_class=clr.classForName(keyType);
        kmd = storeMgr.getNucleusContext().getMetaDataManager().getMetaDataForClass(key_class, clr);
        Class value_class=clr.classForName(valueType);
        if (ClassUtils.isReferenceType(value_class))
        {
            NucleusLogger.PERSISTENCE.warn(LOCALISER.msg("056066", value_class.getName()));
            vmd = storeMgr.getNucleusContext().getMetaDataManager().getMetaDataForImplementationOfReference(value_class,null,clr);
            if (vmd != null)
            {
                this.valueType = value_class.getName();
                // TODO This currently just grabs the cmd of the first implementation. It needs to
                // get the cmds for all implementations, so we can have a handle to all possible elements.
                // This would mean changing the SCO classes to have multiple valueTable/valueMapping etc.
                valueTable = storeMgr.getDatastoreClass(vmd.getFullClassName(), clr);
            }
        }
        else
        {
            vmd = storeMgr.getNucleusContext().getMetaDataManager().getMetaDataForClass(value_class, clr);
            if (vmd != null)
            {
                this.valueType = vmd.getFullClassName();
                if (valuesAreEmbedded)
                {
                    valueTable = null;
                }
                else
                {
                    valueTable = storeMgr.getDatastoreClass(valueType, clr);
                }
            }
        }
    }

    /**
     * Method to put all elements from a Map into our Map.
     * @param sm State Manager for the Map
     * @param m The Map to add
     **/
    public void putAll(ObjectProvider sm, Map m)
    {
        if (m == null || m.size() == 0)
        {
            return;
        }

        HashSet puts = new HashSet();
        HashSet updates = new HashSet();

        Iterator i = m.entrySet().iterator();
        while (i.hasNext())
        {
            Map.Entry e = (Map.Entry)i.next();
            Object key = e.getKey();
            Object value = e.getValue();

            // Make sure the related objects are persisted (persistence-by-reachability)
            validateKeyForWriting(sm, key);
            validateValueForWriting(sm, value);

            // Check if this is a new entry, or an update
            try
            {
                Object oldValue = getValue(sm, key);
                if (oldValue != value)
                {
                    updates.add(e);
                }
            }
            catch (NoSuchElementException nsee)
            {
                puts.add(e);
            }
        }

        boolean batched = allowsBatching();

        // Put any new entries
        if (puts.size() > 0)
        {
            try
            {
                ExecutionContext ec = sm.getExecutionContext();
                ManagedConnection mconn = storeMgr.getConnection(ec);
                try
                {
                    // Loop through all entries
                    Iterator iter = puts.iterator();
                    while (iter.hasNext())
                    {
                        // Add the row to the join table
                        Map.Entry entry = (Map.Entry)iter.next();
                        internalPut(sm, mconn, batched, entry.getKey(), entry.getValue(), (!iter.hasNext()));
                    }
                }
                finally
                {
                    mconn.release();
                }
            }
            catch (MappedDatastoreException e)
            {
                throw new NucleusDataStoreException(LOCALISER.msg("056016", e.getMessage()), e);
            }
        }

        // Update any changed entries
        if (updates.size() > 0)
        {
            try
            {
                ExecutionContext ec = sm.getExecutionContext();
                ManagedConnection mconn = storeMgr.getConnection(ec);
                try
                {
                    // Loop through all entries
                    Iterator iter = updates.iterator();
                    while (iter.hasNext())
                    {
                        // Update the row in the join table
                        Map.Entry entry = (Map.Entry)iter.next();
                        internalUpdate(sm, mconn, batched, entry.getKey(), entry.getValue(), !iter.hasNext());
                    }
                }
                finally
                {
                    mconn.release();
                }
            }
            catch (MappedDatastoreException mde)
            {
                throw new NucleusDataStoreException(LOCALISER.msg("056016", mde.getMessage()), mde);
            }
        }
    }

    /**
     * Method to put an item in the Map.
     * @param sm State Manager for the map.
     * @param key The key to store the value against
     * @param value The value to store.
     * @return The value stored.
     **/
    public Object put(ObjectProvider sm, Object key, Object value)
    {
        validateKeyForWriting(sm, key);
        validateValueForWriting(sm, value);

        boolean exists = false;
        Object oldValue;
        try
        {
            oldValue = getValue(sm, key);
            exists = true;
        }
        catch (NoSuchElementException e)
        {
            oldValue = null;
            exists = false;
        }

        if (oldValue != value)
        {
            // Value changed so update the map
            try
            {
                ExecutionContext ec = sm.getExecutionContext();
                ManagedConnection mconn = storeMgr.getConnection(ec);
                try
                {
                    if (exists)
                    {
                        internalUpdate(sm, mconn, false, key, value, true);
                    }
                    else
                    {
                        internalPut(sm, mconn, false, key, value, true);
                    }
                }
                finally
                {
                    mconn.release();
                }
            }
            catch (MappedDatastoreException e)
            {
                throw new NucleusDataStoreException(LOCALISER.msg("056016", e.getMessage()), e);
            }
        }

        MapMetaData mapmd = ownerMemberMetaData.getMap();
        if (mapmd.isDependentValue() && !mapmd.isEmbeddedValue() && oldValue != null)
        {
            // Delete the old value if it is no longer contained and is dependent
            if (!containsValue(sm, oldValue))
            {
                sm.getExecutionContext().deleteObjectInternal(oldValue);
            }
        }

        return oldValue;
    }

    /**
     * Method to remove an item from the map.
     * @param sm State Manager for the map.
     * @param key Key of the item to remove.
     * @return The value that was removed.
     **/
    public Object remove(ObjectProvider sm, Object key)
    {
        if (!validateKeyForReading(sm, key))
        {
            return null;
        }

        Object oldValue;
        boolean exists;
        try
        {
            oldValue = getValue(sm, key);
            exists = true;
        }
        catch (NoSuchElementException e)
        {
            oldValue = null;
            exists = false;
        }

        ExecutionContext ec = sm.getExecutionContext();
        if (exists)
        {
            removeInternal(sm, key);
        }

        MapMetaData mapmd = ownerMemberMetaData.getMap();
        ApiAdapter api = ec.getApiAdapter();
        if (mapmd.isDependentKey() && !mapmd.isEmbeddedKey() && api.isPersistable(key))
        {
            // Delete the key if it is dependent
            ec.deleteObjectInternal(key);
        }

        if (mapmd.isDependentValue() && !mapmd.isEmbeddedValue() && api.isPersistable(oldValue))
        {
            if (!containsValue(sm, oldValue))
            {
                // Delete the value if it is dependent and is not keyed by another key
                ec.deleteObjectInternal(oldValue);
            }
        }

        return oldValue;
    }

  protected abstract void removeInternal(ObjectProvider sm, Object key);

  /**
     * Method to clear the map of all values.
     * @param ownerSM State Manager for the map.
     */
    public void clear(ObjectProvider ownerSM)
    {
        Collection dependentElements = null;
        if (ownerMemberMetaData.getMap().isDependentKey() || ownerMemberMetaData.getMap().isDependentValue())
        {
            // Retain the PC dependent keys/values that need deleting after clearing
            dependentElements = new HashSet();
            ApiAdapter api = ownerSM.getExecutionContext().getApiAdapter();
            Iterator iter = entrySetStore().iterator(ownerSM);
            while (iter.hasNext())
            {
                Map.Entry entry = (Map.Entry)iter.next();
                MapMetaData mapmd = ownerMemberMetaData.getMap();
                if (api.isPersistable(entry.getKey()) && mapmd.isDependentKey() && !mapmd.isEmbeddedKey())
                {
                    dependentElements.add(entry.getKey());
                }
                if (api.isPersistable(entry.getValue()) && mapmd.isDependentValue() && !mapmd.isEmbeddedValue())
                {
                    dependentElements.add(entry.getValue());
                }
            }
        }
        clearInternal(ownerSM);

        if (dependentElements != null && dependentElements.size() > 0)
        {
            // Delete all dependent objects
            ownerSM.getExecutionContext().deleteObjects(dependentElements.toArray());
        }
    }

  protected abstract void clearInternal(ObjectProvider ownerSM);

  /**
     * Accessor for the keys in the Map.
     * @return The keys
     **/
    public synchronized SetStore keySetStore()
    {
        if (keySetStore == null)
        {
            keySetStore = newMapKeySetStore();
        }
        return keySetStore;
    }

    /**
     * Accessor for the values in the Map.
     * @return The values.
     **/
    public synchronized SetStore valueSetStore()
    {
        if (valueSetStore == null)
        {
            valueSetStore = newMapValueSetStore();
        }
        return valueSetStore;
    }

    /**
     * Accessor for the map entries in the Map.
     * @return The map entries.
     */
    public synchronized SetStore entrySetStore()
    {
        if (entrySetStore == null)
        {
            entrySetStore = newMapEntrySetStore();
        }
        return entrySetStore;
    }

    public JavaTypeMapping getAdapterMapping()
    {
        return adapterMapping;
    }

    protected abstract MapKeySetStore newMapKeySetStore();
    protected abstract MapValueSetStore newMapValueSetStore();
    protected abstract MapEntrySetStore newMapEntrySetStore();
    protected abstract void internalUpdate(ObjectProvider ownerSM, ManagedConnection conn, boolean batched,
            Object key, Object value, boolean executeNow) throws MappedDatastoreException;
    protected abstract int[] internalPut(ObjectProvider ownerSM, ManagedConnection conn, boolean batched,
            Object key, Object value, boolean executeNow) throws MappedDatastoreException;
}
TOP

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

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.