Package org.jpox.store.mapped

Source Code of org.jpox.store.mapped.MappedStoreManager

/**********************************************************************
Copyright (c) 2007 Andy Jefferson 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:
    ...
**********************************************************************/
package org.jpox.store.mapped;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;

import org.jpox.ClassLoaderResolver;
import org.jpox.OMFContext;
import org.jpox.PersistenceConfiguration;
import org.jpox.StateManager;
import org.jpox.api.ApiAdapter;
import org.jpox.metadata.AbstractClassMetaData;
import org.jpox.metadata.AbstractMemberMetaData;
import org.jpox.metadata.InheritanceStrategy;
import org.jpox.store.AbstractStoreManager;
import org.jpox.store.StoreData;
import org.jpox.store.StoreManagerFactory;
import org.jpox.store.exceptions.NoTableManagedException;
import org.jpox.store.fieldmanager.FieldManager;
import org.jpox.store.mapped.mapping.JavaTypeMapping;
import org.jpox.store.mapped.mapping.MappingManager;
import org.jpox.util.JPOXLogger;

/**
* Manager for a datastore that has a schema and maps classes to associated objects in the datastore.
* Datastores such as RDBMS will extend this type of StoreManager.
* <p>
* In a "mapped" datastore, a class is associated with a DatastoreClass. Similarly a field of a class is associated
* with a DatastoreField. Where a relation is stored separately this is associated with a DatastoreContainerObject.
* In an RDBMS datastore this will be
* <ul>
* <li>class <-> table</li>
* <li>field <-> column</li>
* <li>relation <-> join-table / foreign-key</li>
* </ul>
* </p>
* <p>
* This type of StoreManager allows creation/validation of the schema. A schema is split into
* "tables", "columns", "constraints" currently. The PMF/EMF allows specification of a set of properties
* that will apply to this type of datastore.
* </p>
*
* @version $Revision: 1.11 $
*/
public abstract class MappedStoreManager extends AbstractStoreManager
{
    /** Adapter for the datastore being used. */
    protected DatastoreAdapter dba;

    /** Factory for identifiers for this datastore. Really should be on MappedStoreManager. */
    protected IdentifierFactory identifierFactory;

    /** Whether to auto create any tables. */
    protected final boolean autoCreateTables;

    /** Whether to auto create any columns that are missing. */
    protected final boolean autoCreateColumns;

    /** Whether to auto create any constraints */
    protected final boolean autoCreateConstraints;

    /** Whether to warn only when any errors occur on auto-create. */
    protected final boolean autoCreateWarnOnError;

    /** Whether to validate any tables */
    protected final boolean validateTables;

    /** Whether to validate any columns */
    protected final boolean validateColumns;

    /** Whether to validate any constraints */
    protected final boolean validateConstraints;

    /**
     * Map of all managed datastore containers (tables) keyed by the datastore identifier.
     * Only currently used for storing SequenceTable.
     */
    protected Map datastoreContainerByIdentifier = new HashMap();

    /**
     * Constructor. Stores the basic information required for the datastore management.
     * @param key Key for this StoreManager
     * @param clr the ClassLoaderResolver
     * @param omfContext The corresponding ObjectManagerFactory context.
     * @see StoreManagerFactory
     */
    protected MappedStoreManager(String key, ClassLoaderResolver clr, OMFContext omfContext)
    {
        super(key, clr, omfContext);

        PersistenceConfiguration conf = omfContext.getPersistenceConfiguration();
        if (readOnlyDatastore || fixedDatastore)
        {
            autoCreateTables = false;
            autoCreateColumns = false;
            autoCreateConstraints = false;
        }
        else
        {
            boolean autoCreateSchema = conf.getBooleanProperty("org.jpox.autoCreateSchema");
            if (autoCreateSchema)
            {
                autoCreateTables = true;
                autoCreateColumns = true;
                autoCreateConstraints = true;
            }
            else
            {
                autoCreateColumns = conf.getBooleanProperty("org.jpox.autoCreateColumns");
                autoCreateTables = conf.getBooleanProperty("org.jpox.autoCreateTables");
                autoCreateConstraints = conf.getBooleanProperty("org.jpox.autoCreateConstraints");
            }
        }
        autoCreateWarnOnError = conf.getBooleanProperty("org.jpox.autoCreateWarnOnError");

        validateTables = conf.getBooleanProperty("org.jpox.validateTables");
        if (!validateTables)
        {
            validateColumns = false;
        }
        else
        {
            validateColumns = conf.getBooleanProperty("org.jpox.validateColumns");
        }
        validateConstraints = conf.getBooleanProperty("org.jpox.validateConstraints");
    }

    /* (non-Javadoc)
     * @see org.jpox.store.StoreManager#close()
     */
    public void close()
    {
        dba = null;
        super.close();
    }

    /**
     * Accessor for whether this value strategy is supported.
     * Overrides the setting in the superclass for identity/sequence if the adapter doesnt support them.
     * @param strategy The strategy
     * @return Whether it is supported.
     */
    public boolean supportsValueStrategy(String strategy)
    {
        if (super.supportsValueStrategy(strategy))
        {
            if (strategy.equalsIgnoreCase("IDENTITY") && !dba.supportsIdentityFields())
            {
                return false; // adapter doesnt support identity so we dont
            }
            else if (strategy.equalsIgnoreCase("SEQUENCE") && !dba.supportsSequences())
            {
                return false; // adapter doesnt support identity so we dont
            }
        }
        return false;
    }

    /* (non-Javadoc)
     * @see org.jpox.store.StoreManager#getIdentifierFactory()
     */
    public IdentifierFactory getIdentifierFactory()
    {
        return identifierFactory;
    }

    /**
     * Gets the DatastoreAdapter to use for this store.
     * @return Returns the DatastoreAdapter
     */
    public DatastoreAdapter getDatastoreAdapter()
    {
        return dba;
    }

    /**
     * Gets the MappingManager to use for this store.
     * @return Returns the MappingManager.
     */
    public MappingManager getMappingManager()
    {
        return dba.getMappingManager();
    }

    /**
     * Method to create a new fetch statement for the passed table.
     * @param table The table to fetch from
     * @return The fetch statement
     */
    public abstract FetchStatement getFetchStatement(DatastoreContainerObject table);

    /**
     * Called by Mapping objects to request the creation of a DatastoreObject (table).
     * @param fmd The field metadata describing the field.
     * @param clr The ClassLoaderResolver
     * @return The DatastoreContainerObject
     */
    public abstract DatastoreContainerObject newJoinDatastoreContainerObject(AbstractMemberMetaData fmd, ClassLoaderResolver clr);

    /**
     * Utility to return all StoreData for a Datastore Container identifier.
     * Returns StoreData with this table identifier and where the class is the owner of the table.
     * @param tableIdentifier Identifier for the table
     * @return The StoreData for this table (if managed).
     */
    public synchronized StoreData[] getStoreDataForDatastoreContainerObject(DatastoreIdentifier tableIdentifier)
    {
        return storeDataMgr.getStoreDataForProperties("tableId", tableIdentifier, "table-owner", "true");
    }

    /**
     * Returns the datastore container (table) for the specified field.
     * Returns 'null' if the field is not (yet) known to the store manager.
     * @param fmd The metadata for the field.
     * @return The corresponding datastore container, or 'null'.
     */
    public synchronized DatastoreContainerObject getDatastoreContainerObject(AbstractMemberMetaData fmd)
    {
        StoreData sd = storeDataMgr.get(fmd);
        if (sd != null && sd instanceof MappedStoreData)
        {
            return ((MappedStoreData)sd).getDatastoreContainerObject();
        }
        else
        {
            return null;
        }
    }

    /**
     * Method to add a datastore container to the managed datastore classes
     * @param table The datastore container
     */
    public void addDatastoreContainer(DatastoreContainerObject table)
    {
        if (table != null && datastoreContainerByIdentifier.get(table.getIdentifier()) == null)
        {
            datastoreContainerByIdentifier.put(table, table.getIdentifier());
        }
    }

    /**
     * Method to create the necessary datastore columns for a reference field.
     * @param m (Java) Mapping for the field
     * @param table The datastore container where the datastore columns will be created
     * @param fmd MetaData for the field/property
     * @param clr ClassLoader resolver
     * @param embedded Whether the field is embedded
     */
    public abstract void createDatastoreColumnsForReferenceField(JavaTypeMapping m, DatastoreContainerObject table,
            AbstractMemberMetaData fmd, ClassLoaderResolver clr, boolean embedded);

    /**
     * Method to create the necessary datastore columns for a field using subclass-table inheritance.
     * @param m (Java) Mapping for the field
     * @param table The datastore container where the datastore columns will be created
     * @param fmd MetaData for the field/property
     * @param clr ClassLoader resolver
     */
    public abstract void createDatastoreColumnsForFieldUsingSubclassTable(JavaTypeMapping m,
        DatastoreContainerObject table, AbstractMemberMetaData fmd, ClassLoaderResolver clr);

    /**
     * Accessor for a FieldManager capable of processing the results of a query.
     * @param sm StateManager for the object
     * @param rs The results
     * @param stmtExprIndx Statement expression indices for results -> fields mapping
     * @return The FieldManager
     */
    public abstract FieldManager getFieldManagerForResultProcessing(StateManager sm, Object rs, StatementExpressionIndex[] stmtExprIndx);

    /**
     * Returns the primary datastore container serving as backing for the given class.
     * If the class is not yet known to the store manager, {@link #addClass}is called
     * to add it. Classes which have inheritance strategy of "new-table" and
     * "superclass-table" will return a table here, whereas "subclass-table" will
     * return null since it doesn't have a table as such.
     * <p>
     * @param className Name of the class whose table is be returned.
     * @param clr The ClassLoaderResolver
     * @return The corresponding class table.
     * @exception NoTableManagedException If the given class has no table managed in the database.
     */
    public DatastoreClass getDatastoreClass(String className, ClassLoaderResolver clr)
    {
        DatastoreClass ct = null;
        if (className == null)
        {
            JPOXLogger.PERSISTENCE.error(LOCALISER.msg("032015"));
            return null;
        }

        StoreData sd = storeDataMgr.get(className);
        if (sd != null && sd instanceof MappedStoreData)
        {
            ct = (DatastoreClass) ((MappedStoreData)sd).getDatastoreContainerObject();
            if (ct != null)
            {
                // Class known about
                return ct;
            }
        }

        // Class not known so consider adding it to our list of supported classes.
        // Currently we only consider PC classes
        boolean toBeAdded = false;
        if (clr != null)
        {
            Class cls = clr.classForName(className);
            ApiAdapter api = getApiAdapter();
            if (cls != null && !cls.isInterface() && api.isPersistable(cls))
            {
                toBeAdded = true;
            }
        }
        else
        {
            toBeAdded = true;
        }

        boolean classKnown = false;
        if (toBeAdded)
        {
            // Add the class to our supported list
            addClass(className, clr);

            // Retry
            synchronized (storeDataMgr)
            {
                sd = storeDataMgr.get(className);
                if (sd != null && sd instanceof MappedStoreData)
                {
                    classKnown = true;
                    ct = (DatastoreClass) ((MappedStoreData)sd).getDatastoreContainerObject();
                }
            }
        }

        // Throw an exception if class still not known and no table
        // Note : "subclass-table" inheritance strategies will return null from this method
        if (!classKnown && ct == null)
        {
            throw new NoTableManagedException(className);
        }

        return ct;
    }

    /**
     * Returns the JDO table having the given SQL identifier.
     * Returns 'null' if no such table is (yet) known to the store manager.
     * @param name The identifier name of the table.
     * @return The corresponding JDO table, or 'null'
     */
    public synchronized DatastoreClass getDatastoreClass(DatastoreIdentifier name)
    {
        Iterator iterator = storeDataMgr.getManagedStoreData().iterator();
        while (iterator.hasNext())
        {
            StoreData sd = (StoreData) iterator.next();
            if (sd instanceof MappedStoreData)
            {
                MappedStoreData tsd = (MappedStoreData)sd;
                if (tsd.hasTable() && tsd.getDatastoreIdentifier().equals(name))
                {
                    return (DatastoreClass) tsd.getDatastoreContainerObject();
                }
            }
        }
        return null;
    }

    /**
     * Utility to navigate the inheritance hierarchy to find the base class that defines the primary keys
     * for this tree. This will either go up to the next class in the hierarchy that has a table
     * OR go up to the base class, whichever is first.
     * @param cmd AbstractClassMetaData for this class
     * @param clr The ClassLoaderResolver
     * @return The AbstractClassMetaData for the class defining the primary keys
     */
    public AbstractClassMetaData getClassWithPrimaryKeyForClass(AbstractClassMetaData cmd, ClassLoaderResolver clr)
    {
        if (cmd == null)
        {
            return null;
        }

        // Base class will have primary key fields
        if (cmd.getSuperAbstractClassMetaData() == null)
        {
            return cmd;
        }
        // Class has its own table so has the PK fields already
        else if (getDatastoreClass(cmd.getFullClassName(), clr) != null)
        {
            return cmd;
        }

        return getClassWithPrimaryKeyForClass(cmd.getSuperAbstractClassMetaData(), clr);
    }

    /**
     * Method to return the class(es) that has a table managing the persistence of
     * the fields of the supplied class. For the 3 inheritance strategies, the following
     * occurs :-
     * <UL>
     * <LI>new-table : will return the same ClassMetaData</LI>
     * <LI>subclass-table : will return all subclasses that have a table managing its fields</LI>
     * <LI>superclass-table : will return the next superclass that has a table</LI>
     * </UL>
     * @param cmd The supplied class.
     * @param clr ClassLoader resolver
     * @return The ClassMetaData's managing the fields of the supplied class
     */
    public AbstractClassMetaData[] getClassesManagingTableForClass(AbstractClassMetaData cmd, ClassLoaderResolver clr)
    {
        // Null input, so just return null;
        if (cmd == null)
        {
            return null;
        }

        if (cmd.getInheritanceMetaData().getStrategyValue() == InheritanceStrategy.COMPLETE_TABLE ||
            cmd.getInheritanceMetaData().getStrategyValue() == InheritanceStrategy.NEW_TABLE)
        {
            // Class manages a table so return the classes metadata.
            return new AbstractClassMetaData[] {cmd};
        }
        else if (cmd.getInheritanceMetaData().getStrategyValue() == InheritanceStrategy.SUBCLASS_TABLE)
        {
            // Check the subclasses that we have metadata for and make sure they are managed before proceeding
            String[] subclasses = getMetaDataManager().getSubclassesForClass(cmd.getFullClassName(), true);
            if (subclasses != null)
            {
                for (int i=0;i<subclasses.length;i++)
                {
                    if (!storeDataMgr.managesClass(subclasses[i]))
                    {
                        addClass(subclasses[i], clr);
                    }
                }
            }

            // Find subclasses who manage the tables winto which our class is persisted
            HashSet managingClasses=new HashSet();
            Iterator managedClassesIter = storeDataMgr.getManagedStoreData().iterator();
            while (managedClassesIter.hasNext())
            {
                StoreData data = (StoreData)managedClassesIter.next();
                if (data.isFCO() && ((AbstractClassMetaData)data.getMetaData()).getSuperAbstractClassMetaData() != null &&
                    ((AbstractClassMetaData)data.getMetaData()).getSuperAbstractClassMetaData().getFullClassName().equals(cmd.getFullClassName()))
                {
                    AbstractClassMetaData[] superCmds = getClassesManagingTableForClass((AbstractClassMetaData)data.getMetaData(), clr);
                    if (superCmds != null)
                    {
                        for (int i=0;i<superCmds.length;i++)
                        {
                            managingClasses.add(superCmds[i]);
                        }
                    }
                }
            }

            Iterator managingClassesIter = managingClasses.iterator();
            AbstractClassMetaData managingCmds[] = new AbstractClassMetaData[managingClasses.size()];
            int i=0;
            while (managingClassesIter.hasNext())
            {
                managingCmds[i++] = (AbstractClassMetaData)(managingClassesIter.next());
            }
            return managingCmds;
        }
        else if (cmd.getInheritanceMetaData().getStrategyValue() == InheritanceStrategy.SUPERCLASS_TABLE)
        {
            // Fields managed by superclass, so recurse to that
            return getClassesManagingTableForClass(cmd.getSuperAbstractClassMetaData(), clr);
        }
        return null;
    }
}
TOP

Related Classes of org.jpox.store.mapped.MappedStoreManager

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.