Package org.jpox.store.rdbms.table

Source Code of org.jpox.store.rdbms.table.AbstractClassTable

/**********************************************************************
Copyright (c) 2005 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.rdbms.table;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import org.jpox.ClassLoaderResolver;
import org.jpox.exceptions.JPOXException;
import org.jpox.exceptions.JPOXUserException;
import org.jpox.identity.OID;
import org.jpox.metadata.AbstractClassMetaData;
import org.jpox.metadata.AbstractMemberMetaData;
import org.jpox.metadata.ColumnMetaData;
import org.jpox.metadata.ColumnMetaDataContainer;
import org.jpox.metadata.DiscriminatorMetaData;
import org.jpox.metadata.FieldRole;
import org.jpox.metadata.IdentityStrategy;
import org.jpox.metadata.IdentityType;
import org.jpox.metadata.MetaData;
import org.jpox.metadata.VersionMetaData;
import org.jpox.plugin.ConfigurationElement;
import org.jpox.store.mapped.DatastoreClass;
import org.jpox.store.mapped.DatastoreContainerObject;
import org.jpox.store.mapped.DatastoreField;
import org.jpox.store.mapped.DatastoreIdentifier;
import org.jpox.store.mapped.IdentifierFactory;
import org.jpox.store.mapped.mapping.JavaTypeMapping;
import org.jpox.store.mapped.mapping.MappingConsumer;
import org.jpox.store.mapped.mapping.OIDMapping;
import org.jpox.store.mapped.mapping.PersistenceCapableMapping;
import org.jpox.store.rdbms.Column;
import org.jpox.store.rdbms.RDBMSManager;
import org.jpox.util.JPOXLogger;
import org.jpox.util.StringUtils;

/**
* Abstract representation of a table for a class.
* Abstracts out the common parts of a primary ClassTable
* and a SecondaryClassTable.
*
* <H3>Mappings</H3>
* A Table is built from a series of field mappings. Each Java class has a series of
* fields and each of these has an associated JavaTypeMapping. Each JavaTypeMapping has
* related DatastoreMapping(s). These are used in mapping the Java class to the table,
* and are used when populating the table, and when retrieving data from the table back
* to the object. There are several categories of mappings in this class
* <UL>
* <LI><B>fieldMappingsMap</B> - the set of mappings relating to the fields in the
* class. The mappings are keyed by the FieldMetaData of the field. Any embedded field
* will have a single mapping here of type EmbeddedPCMapping, with a set of datastore
* mappings attached.</LI>
* <LI><B>datastoreIDMapping</B> - the Identity mapping when using "datastore identity"</LI>
* <LI><B>pkMappings</B> - the mappings for the primary key column(s).</LI>
* <LI><B>discriminatorMapping</B> - mapping for any discriminator column. This is only
* used where classes share this table and some of them use "superclass-table" strategy</LI>
* <LI><B>versionMapping</B> - mapping for any versioning column</LI>
* </UL>
*
* @version $Revision: 1.56 $
*/
public abstract class AbstractClassTable extends TableImpl
{
    /** Mappings for fields mapped to this table, keyed by the FieldMetaData. */
    protected Map fieldMappingsMap = new HashMap();

    /** Mapping for datastore identity (optional). */
    protected JavaTypeMapping datastoreIDMapping;

    /** Mappings for application identity (optional). */
    protected JavaTypeMapping[] pkMappings;

    /** Mapping for the id of the table. */
    protected JavaTypeMapping idMapping;

    /** Mapping for any version/timestamp column. */
    protected JavaTypeMapping versionMapping;

    /** MetaData for versioning of objects stored in this table. */
    protected VersionMetaData versionMetaData;

    /** MetaData for discriminator for objects stored in this table. */
    protected DiscriminatorMetaData discriminatorMetaData;

    /** Mapping for any discriminator column. */
    protected JavaTypeMapping discriminatorMapping;

    /** Highest absolute field number managed by this table */
    protected int highestFieldNumber=0;

    /**
     * Constructor.
     * @param tableName Name of the table
     * @param storeMgr Store Manager that is managing this instance
     */
    public AbstractClassTable(DatastoreIdentifier tableName, RDBMSManager storeMgr)
    {
        super(tableName, storeMgr);
    }

    /**
     * Convenience method to return the primary table.
     * @return The primary table for this table
     */
    public DatastoreContainerObject getPrimaryDatastoreContainerObject()
    {
        return this;
    }

    /**
     * Method to initialise the table primary key field(s).
     * @param clr The ClassLoaderResolver
     **/
    protected abstract void initializePK(ClassLoaderResolver clr);

    /**
     * Convenience method for whether the (fully-specified) field is managed by this table
     * @param fieldName Fully qualified name of the field
     * @return Whether it is managed
     */
    public boolean managesField(String fieldName)
    {
        if (fieldName == null)
        {
            return false;
        }

        return (getMappingForFieldName(fieldName) != null);
    }

    /**
     * Accessor for the JavaTypeMapping that is handling the field of the specified name.
     * Returns the first one that matches.
     * @param fieldName Name of the field
     * @return The java type mapping
     */
    protected JavaTypeMapping getMappingForFieldName(String fieldName)
    {
        Set fields = fieldMappingsMap.keySet();
        Iterator fieldsIter = fields.iterator();
        while (fieldsIter.hasNext())
        {
            AbstractMemberMetaData fmd = (AbstractMemberMetaData)fieldsIter.next();
            if (fmd.getFullFieldName().equals(fieldName))
            {
                return (JavaTypeMapping)fieldMappingsMap.get(fmd);
            }
        }
        return null;
    }

    /**
     * Utility to create the application identity columns and mapping.
     * Uses the id mapping of the specified class table and copies the mappings
     * and columns, whilst retaining the passed preferences for column namings.
     * This is used to copy the PK mappings of a superclass table so we have the same PK.
     * @param columnContainer The container of column MetaData with any namings
     * @param refTable The table that we use as reference
     * @param clr The ClassLoaderResolver
     * @param cmd The ClassMetaData
     */
    final void addApplicationIdUsingClassTableId(ColumnMetaDataContainer columnContainer, DatastoreClass refTable, ClassLoaderResolver clr, AbstractClassMetaData cmd)
    {
        ColumnMetaData[] userdefinedCols = null;
        int nextUserdefinedCol = 0;
        if (columnContainer != null)
        {
            userdefinedCols = columnContainer.getColumnMetaData();
        }

        pkMappings = new JavaTypeMapping[cmd.getPKMemberPositions().length];
        for (int i=0; i<cmd.getPKMemberPositions().length; i++)
        {
            AbstractMemberMetaData fmd = cmd.getMetaDataForManagedMemberAtAbsolutePosition(cmd.getPKMemberPositions()[i]);
            JavaTypeMapping mapping = refTable.getFieldMapping(fmd);
            if (mapping == null)
            {
                //probably due to invalid metadata defined by the user
                throw new JPOXUserException("Cannot find mapping for field " + fmd.getFullFieldName()+
                    " in table " + refTable.toString() + " " +
                    StringUtils.objectArrayToString(refTable.getDatastoreFieldsMetaData()));
            }

            JavaTypeMapping masterMapping = dba.getMapping(clr.classForName(mapping.getType()), storeMgr);
            masterMapping.setFieldInformation(fmd, this); // Update field info in mapping
            pkMappings[i] = masterMapping;

            // Loop through each id column in the reference table and add the same here
            // applying the required names from the columnContainer
            for (int j=0; j<mapping.getNumberOfDatastoreFields(); j++)
            {
                JavaTypeMapping m = masterMapping;
                DatastoreField refColumn = mapping.getDataStoreMapping(j).getDatastoreField();
                if (mapping instanceof PersistenceCapableMapping)
                {
                    m = dba.getMapping(clr.classForName(refColumn.getMapping().getType()), storeMgr);
                    ((PersistenceCapableMapping)masterMapping).addJavaTypeMapping(m);
                }

                ColumnMetaData userdefinedColumn = null;
                if (userdefinedCols != null)
                {
                    for (int k=0;k<userdefinedCols.length;k++)
                    {
                        if (refColumn.getIdentifier().toString().equals(userdefinedCols[k].getTarget()))
                        {
                            userdefinedColumn = userdefinedCols[k];
                            break;
                        }
                    }
                    if (userdefinedColumn == null && nextUserdefinedCol < userdefinedCols.length)
                    {
                        userdefinedColumn = userdefinedCols[nextUserdefinedCol++];
                    }
                }

                // Add this application identity column
                DatastoreField idColumn = null;
                if (userdefinedColumn != null)
                {
                    // User has provided a name for this column
                    // Currently we only use the column namings from the users definition but we could easily
                    // take more of their details.
                    idColumn = addDatastoreField(refColumn.getStoredJavaType(),
                        storeMgr.getIdentifierFactory().newIdentifier(IdentifierFactory.COLUMN, userdefinedColumn.getName()),
                        m, refColumn.getMetaData());
                }
                else
                {
                    // No name provided so take same as superclass
                    idColumn = addDatastoreField(refColumn.getStoredJavaType(), refColumn.getIdentifier(),
                        m, refColumn.getMetaData());
                }
                if (mapping != null && mapping.getDataStoreMapping(j).getDatastoreField().getMetaData() != null)
                {
                    refColumn.copyConfigurationTo(idColumn);
                }
                idColumn.setAsPrimaryKey();

                // Set the column type based on the field.getType()
                getStoreManager().getMappingManager().createDatastoreMapping(m, storeMgr, idColumn,
                    refColumn.getMapping().getType());
            }

            int absoluteFieldNumber = fmd.getAbsoluteFieldNumber();
            if (absoluteFieldNumber > highestFieldNumber)
            {
                highestFieldNumber = absoluteFieldNumber;
            }
        }
    }
   
    /**
     * Utility to create the datastore identity column and mapping.
     * This is used in 2 modes. The first is where we have a (primary) class table
     * and we aren't creating the OID mapping as a FK to another class. The second
     * is where we have a (secondary) class table and we are creating the OID mapping
     * as a FK to the primary class. In the second case the refTable will be specified.
     * @param columnContainer The container of column MetaData
     * @param refTable Table used as a reference (if any)
     * @param cmd The MetaData for the class
     */
    void addDatastoreId(ColumnMetaDataContainer columnContainer, DatastoreClass refTable, AbstractClassMetaData cmd)
    {
        datastoreIDMapping = new OIDMapping();
        datastoreIDMapping.initialize(dba, cmd.getFullClassName());

        // Create a ColumnMetaData in the container if none is defined
        ColumnMetaData colmd = null;
        if (columnContainer == null)
        {
            colmd = new ColumnMetaData(null, (String)null);
        }
        else if (columnContainer.getColumnMetaData().length < 1)
        {
            colmd = new ColumnMetaData((MetaData)columnContainer, (String)null);
        }
        else
        {
            colmd = columnContainer.getColumnMetaData()[0];
        }
        if (colmd.getName() == null)
        {
            // Provide default column naming if none is defined
            if (refTable != null)
            {
                colmd.setName(storeMgr.getIdentifierFactory().newDatastoreFieldIdentifier(refTable.getIdentifier().getIdentifier(),
                    this.storeMgr.getOMFContext().getTypeManager().isDefaultEmbeddedType(OID.class), FieldRole.ROLE_OWNER).getIdentifier());
            }
            else
            {
                colmd.setName(storeMgr.getIdentifierFactory().newDatastoreFieldIdentifier(identifier.getIdentifier(),
                    this.storeMgr.getOMFContext().getTypeManager().isDefaultEmbeddedType(OID.class), FieldRole.ROLE_NONE).getIdentifier());
            }
        }

        // Add the datastore identity column as the PK
        Column idColumn = (Column) addDatastoreField(OID.class.getName(),
            storeMgr.getIdentifierFactory().newIdentifier(IdentifierFactory.COLUMN, colmd.getName()), datastoreIDMapping, colmd);
        idColumn.setAsPrimaryKey();

        // Set the identity column type based on the IdentityStrategy
        String strategyName = cmd.getIdentityMetaData().getValueStrategy().toString();
        if (cmd.getIdentityMetaData().getValueStrategy().equals(IdentityStrategy.CUSTOM))
        {
            strategyName = cmd.getIdentityMetaData().getValueStrategy().getCustomName();
        }

        // Check the POID type being stored
        Class poidClass = Long.class;
        ConfigurationElement elem =
            storeMgr.getOMFContext().getPluginManager().getConfigurationElementForExtension(
                "org.jpox.store_valuegenerator",
                new String[]{"name", "unique"}, new String[] {strategyName, "true"});
        if (elem == null)
        {
            // Not datastore-independent, so try for this datastore
            elem = storeMgr.getOMFContext().getPluginManager().getConfigurationElementForExtension(
                    "org.jpox.store_valuegenerator",
                    new String[]{"name", "datastore"}, new String[] {strategyName, storeMgr.getStoreManagerKey()});
        }
        if (elem != null)
        {
            // Set the generator name (for use by the PoidManager)
            String generatorClassName = elem.getAttribute("class-name");
            Class generatorClass =
                getStoreManager().getOMFContext().getClassLoaderResolver(null).classForName(generatorClassName);
            try
            {
                Method storageClassMethod = generatorClass.getMethod("getStorageClass", null);
                poidClass = (Class) storageClassMethod.invoke(null, null);
            }
            catch (Exception e)
            {
                // Unable to get the storage class from the PoidGenerator class
                JPOXLogger.POID.warn("Error retrieving storage class for POID generator " + generatorClassName +
                    " " + e.getMessage());
            }
        }

        dba.getMappingManager().createDatastoreMapping(datastoreIDMapping, storeMgr, idColumn,
            poidClass.getName());

        // Handle any auto-increment requirement
        if (isObjectIDDatastoreAttributed())
        {
            if (this instanceof DatastoreClass && ((DatastoreClass)this).isBaseDatastoreClass())
            {
                // Only the base class can be autoincremented
                idColumn.setAutoIncrement(true);
            }
        }

        // Check if auto-increment and that it is supported by this RDBMS
        if (idColumn.isAutoIncrement() && !dba.supportsIdentityFields())
        {
            throw new JPOXException(LOCALISER.msg("057020",
                cmd.getFullClassName(), "datastore-identity")).setFatal();
        }
    }

    /**
     * Utility to add the mapping for a field to the managed list.
     * @param fieldMapping The mapping for the field
     */
    protected void addFieldMapping(JavaTypeMapping fieldMapping)
    {
        AbstractMemberMetaData fmd = fieldMapping.getFieldMetaData();
        if (JPOXLogger.DATASTORE_SCHEMA.isDebugEnabled())
        {
            // Provide field->column mapping debug message
            StringBuffer columnsStr = new StringBuffer();
            for (int i=0;i<fieldMapping.getNumberOfDatastoreFields();i++)
            {
                if (i > 0)
                {
                    columnsStr.append(",");
                }
                columnsStr.append(fieldMapping.getDataStoreMapping(i).getDatastoreField());
            }
            if (fieldMapping.getNumberOfDatastoreFields() == 0)
            {
                columnsStr.append("[none]");
            }
            StringBuffer datastoreMappingTypes = new StringBuffer();
            for (int i=0;i<fieldMapping.getNumberOfDatastoreFields();i++)
            {
                if (i > 0)
                {
                    datastoreMappingTypes.append(',');
                }
                datastoreMappingTypes.append(fieldMapping.getDataStoreMapping(i).getClass().getName());
            }
            JPOXLogger.DATASTORE_SCHEMA.debug(LOCALISER.msg("057010",
                fmd.getFullFieldName(), columnsStr.toString(), fieldMapping.getClass().getName(), datastoreMappingTypes.toString()));
        }

        fieldMappingsMap.put(fmd, fieldMapping);

        int absoluteFieldNumber = fmd.getAbsoluteFieldNumber();
        if (absoluteFieldNumber > highestFieldNumber)
        {
            highestFieldNumber = absoluteFieldNumber;
        }
    }

    /**
     * Accessor for the identity-type.
     * @return identity-type tag value
     */   
    public abstract IdentityType getIdentityType();

    /**
     * Accessor for whether the table has its identity attributed
     * by the datastore (e.g using autoincrement)
     * @return Whether it is datastore attributed
     */
    public abstract boolean isObjectIDDatastoreAttributed();

    // -------------------------- Mapping Accessors --------------------------------

    /**
     * Accessor for a mapping for the datastore ID (OID) for this table.
     * @return The (OID) mapping.
     **/
    public JavaTypeMapping getDataStoreObjectIdMapping()
    {
        assertIsInitialized();

        return datastoreIDMapping;
    }

    /**
     * Accessor for the version mapping specified .
     * @param allowSuperclasses Whether we should return just the mapping from this table
     *     or whether we should return it when this table has none and the supertable has
     * @return The version mapping.
     */
    public JavaTypeMapping getVersionMapping(boolean allowSuperclasses)
    {
        // We dont have superclasstables here so just return what we have
        return versionMapping;
    }

    /**
     * Accessor for the discriminator mapping specified .
     * @return The mapping for the discriminator column
     */
    public JavaTypeMapping getDiscriminatorMapping(boolean allowSuperclasses)
    {
        return discriminatorMapping;
    }

    /**
     * Accessor for a mapping for the datastore ID (OID) for this table.
     * @param consumer Consumer for the mappings
     **/
    final public void provideDatastoreIdMappings(MappingConsumer consumer)
    {
        consumer.preConsumeMapping(highestFieldNumber + 1);

        if (getIdentityType() == IdentityType.DATASTORE)
        {
            consumer.consumeMapping(getDataStoreObjectIdMapping(), MappingConsumer.MAPPING_TYPE_DATASTORE_ID);
        }
    }

    /**
     * Provide the mappings to the consumer for all primary-key fields mapped to
     * this table (for application identity).
     * @param consumer Consumer for the mappings
     */
    public abstract void providePrimaryKeyMappings(MappingConsumer consumer);

    /**
     * Provide the mappings to the consumer for all non primary-key fields
     * mapped to this table.
     * @param consumer Consumer for the mappings
     */
    final public void provideNonPrimaryKeyMappings(MappingConsumer consumer)
    {
        consumer.preConsumeMapping(highestFieldNumber + 1);

        Set fieldNumbersSet = fieldMappingsMap.keySet();
        Iterator iter = fieldNumbersSet.iterator();
        while (iter.hasNext())
        {
            AbstractMemberMetaData fmd = (AbstractMemberMetaData) iter.next();
            JavaTypeMapping fieldMapping = (JavaTypeMapping)fieldMappingsMap.get(fmd);
            if (fieldMapping != null)
            {
                if (!fmd.isPrimaryKey())
                {
                    consumer.consumeMapping(fieldMapping, fmd);
                }
            }
        }
    }

    /**
     * Provide the mappings to the consumer for all absolute field Numbers in this table
     * that are container in the fieldNumbers parameter.
     * @param consumer Consumer for the mappings
     * @param fieldMetaData MetaData for the fields to provide mappings for
     * @param includeSecondaryTables Whether to provide fields in secondary tables
     */
    public void provideMappingsForFields(MappingConsumer consumer, AbstractMemberMetaData[] fieldMetaData, boolean includeSecondaryTables)
    {
        consumer.preConsumeMapping(highestFieldNumber + 1);
        for (int i = 0; i < fieldMetaData.length; i++)
        {
            JavaTypeMapping fieldMapping = (JavaTypeMapping)fieldMappingsMap.get(fieldMetaData[i]);
            if (fieldMapping != null)
            {
                if (!fieldMetaData[i].isPrimaryKey())
                {
                    consumer.consumeMapping(fieldMapping, fieldMetaData[i]);
                }
            }
        }
    }

    /**
     * Provide the version mappings.
     * @param consumer Consumer for the version mappings
     */
    final public void provideVersionMappings(MappingConsumer consumer)
    {
        consumer.preConsumeMapping(highestFieldNumber + 1);
        if (getVersionMapping(false) != null)
        {
            consumer.consumeMapping(getVersionMapping(false), MappingConsumer.MAPPING_TYPE_VERSION);
        }
    }

    /**
     * Provide the discriminator mappings
     * @param consumer Consumer for the mappings
     */
    final public void provideDiscriminatorMappings(MappingConsumer consumer)
    {
        consumer.preConsumeMapping(highestFieldNumber + 1);
        if (getDiscriminatorMapping(false) != null)
        {
            consumer.consumeMapping(getDiscriminatorMapping(false), MappingConsumer.MAPPING_TYPE_DISCRIMINATOR);
        }
    }
}
TOP

Related Classes of org.jpox.store.rdbms.table.AbstractClassTable

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.