Package org.jpox.store.rdbms.extent

Source Code of org.jpox.store.rdbms.extent.ClassTableExtent

/**********************************************************************
Copyright (c) 2003 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 Erik Bengtson - changed newQueryStatement to support JPOX ID
2003 Erik Bengtson - fixed BUG [800693]
2003 Erik Bengtson - refactored and commented newQueryStatement method
2003 Andy Jefferson - fixed BUG [804902]
2003 Andy Jefferson - updated logic for getSubClasses and added localiser
2004 Erik Bengtson - removed unused import
2004 Andy Jefferson - added capability to handle multiple level inheritance
2004 Andy Jefferson - split out AbstractRDBMSExtent
2005 Andy Jefferson - added ability to use discriminator in IteratorStatements
    ...
**********************************************************************/
package org.jpox.store.rdbms.extent;

import org.jpox.ClassLoaderResolver;
import org.jpox.ObjectManager;
import org.jpox.FetchPlan.FetchPlanForClass;
import org.jpox.exceptions.ClassNotResolvedException;
import org.jpox.exceptions.JPOXUserException;
import org.jpox.metadata.AbstractClassMetaData;
import org.jpox.metadata.IdentityType;
import org.jpox.metadata.InheritanceStrategy;
import org.jpox.store.mapped.DatastoreClass;
import org.jpox.store.mapped.DatastoreIdentifier;
import org.jpox.store.mapped.IdentifierFactory;
import org.jpox.store.mapped.MappedStoreManager;
import org.jpox.store.mapped.StatementExpressionIndex;
import org.jpox.store.mapped.expression.QueryExpression;
import org.jpox.store.mapped.mapping.AbstractContainerMapping;
import org.jpox.store.mapped.mapping.JavaTypeMapping;
import org.jpox.store.mapped.mapping.Mappings;
import org.jpox.store.mapped.query.Queryable;
import org.jpox.store.query.ResultObjectFactory;
import org.jpox.store.rdbms.RDBMSManager;
import org.jpox.store.rdbms.query.DiscriminatorIteratorStatement;
import org.jpox.store.rdbms.query.PersistentIDROF;
import org.jpox.store.rdbms.query.ResultClassROF;
import org.jpox.store.rdbms.query.UnionIteratorStatement;
import org.jpox.util.Localiser;

/**
* An Extent of all persistent objects backed by a class table.
* There are 3 inheritance strategies, and each is handled by this class but
* in different ways.
* <H3>subclass-table Strategy</H3>
* In this case a class does not have its own table and its fields are
* persisted to the table of a subclass. If the class requiring the Extent
* is the "subclass-table" class, then there will be an array of tables where objects
* of this type can be persisted (as the base table). For this reason the caller
* passes in an array of tables.
* <H3>new-table Strategy</H3>
* In this case the class has its own table, and so there is only one base
* table for the class. The array of tables passed in will be of size 1.
* <H3>superclass-table Strategy</H3>
* In this case the class doesn't have its own table, BUT will be persisted
* into the table of its superclass. In this case the array of tables passed
* in will be of size 1 (the superclass' table).
*
* @version $Revision: 1.61 $
*/
public class ClassTableExtent extends AbstractRDBMSExtent implements Queryable
{
    /** Localised messages */
    protected static final Localiser LOCALISER_RDBMS = Localiser.getInstance("org.jpox.store.rdbms.Localisation",
        RDBMSManager.class.getClassLoader());

    /** Table(s) required for this Extent. */
    private final DatastoreClass[] tables;

    /** Whether this Extent is across multiple tables ("subclass-table" strategy). */
    private final boolean multipleTableCase;

    private final DatastoreIdentifier elmIdentifier;

    /**
     * Constructor. Used for classes that have "new-table" or "superclass-table" inheritance strategies.
     * @param om ObjectManager
     * @param table Table representing the class whose extent we want
     * @param cls Candidate class
     * @param subclasses Whether to include subclasses of the class.
     * @param cmd MetaData for the candidate class
     */
    public ClassTableExtent(ObjectManager om, DatastoreClass table, Class cls, boolean subclasses, AbstractClassMetaData cmd)
    {
        super(om, cls, subclasses, cmd);

        this.tables = new DatastoreClass[1];
        tables[0] = table;
        multipleTableCase = false;

        elmIdentifier = ((MappedStoreManager)storeMgr).getIdentifierFactory().newIdentifier(IdentifierFactory.TABLE, "ELEMENT");
    }

    /**
     * Constructor. Used for classes that have "subclass-table" inheritance strategy, and hence
     * can be persisted into multiple tables.
     * @param om ObjectManager
     * @param tables Tables representing the class whose extent we want
     * @param cls Candidate class
     * @param subclasses Whether to include subclasses of the class.
     * @param cmd MetaData for the candidate class
     */
    public ClassTableExtent(ObjectManager om, DatastoreClass[] tables, Class cls, boolean subclasses, AbstractClassMetaData cmd)
    {
        super(om, cls, subclasses, cmd);

        this.tables = tables;
        multipleTableCase = true;

        elmIdentifier = ((MappedStoreManager)storeMgr).getIdentifierFactory().newIdentifier(IdentifierFactory.TABLE, "ELEMENT");
    }

    /**
     * Create a new query to search for the candidate classes etc.
     * @return The new QueryStatement.
     */
    public QueryExpression newQueryStatement()
    {
        return newQueryStatement(getCandidateClass(), null);
    }

    /**
     * Create a query to search for instances of the candidateClass and its subclasses (if required).
     * @param candidateClass The candidate class
     * @param candidateAlias Alias for the candidate
     * @return The QueryStatement that will return the instances
     */
    public QueryExpression newQueryStatement(Class candidateClass, DatastoreIdentifier candidateAlias)
    {
        final ClassLoaderResolver clr = om.getClassLoaderResolver();
        QueryExpression query = null;

        if (tables == null)
        {
            return null;
        }

        // In the multiple table case we pass in the class of the table rather than the candidate class since
        // the instances of that table can only be of the tables type ("subclass-table" strategy)
        for (int i = 0; i < tables.length; i++)
        {
            final int tableNo = i;

            // Load using the class loader of the candidate class where possible - JDO spec 12.5
            Class cls = null;
            try
            {
                cls = clr.classForName(tables[tableNo].getType(), this.candidateClass.getClassLoader());
            }
            catch (ClassNotResolvedException cnfe)
            {
                throw new JPOXUserException(LOCALISER_RDBMS.msg("053002",
                    candidateClass.getName(), tables[tableNo].getType())).setFatal();
            }

            if (queryUsingDiscriminator())
            {
                // Use a single select with discrim since all in 1 table, and select the discrim column for identification
                Class[] candidates = null;
                if (storeMgr.getOMFContext().getTypeManager().isReferenceType(candidateClass))
                {
                    // Take the metadata for the first implementation of the reference type
                    String[] clsNames = storeMgr.getOMFContext().getMetaDataManager().getClassesImplementingInterface(candidateClass.getName(), clr);
                    candidates = new Class[clsNames.length];
                    for (int j=0; j<clsNames.length; j++)
                    {
                        candidates[j] = clr.classForName(clsNames[j]);
                    }
                }
                else
                {
                    candidates = new Class[] {candidateClass};
                }
                query = (new DiscriminatorIteratorStatement(clr,
                    candidates, subclasses, storeMgr, true)).getQueryStatement(candidateAlias);
            }
            else
            {
                // Use a UNION since we need to join across multiple tables
                boolean completeTableInheritance =
                    cmd.getInheritanceMetaData().getStrategyValue() == InheritanceStrategy.COMPLETE_TABLE;
                boolean useSubclassesInStatement = subclasses;
                if (completeTableInheritance)
                {
                    // "complete-table" so ignore subclasses since each table is just that class
                    useSubclassesInStatement = false;
                }

                QueryExpression query_table = new UnionIteratorStatement(clr,
                    multipleTableCase ? cls : candidateClass, useSubclassesInStatement, storeMgr,
                    clr.classForName(tables[tableNo].getType()),
                    tables[tableNo].getIDMapping(), tables[tableNo], false, Boolean.TRUE,
                    !completeTableInheritance, false).getQueryStatement(candidateAlias);
               
                if (query != null)
                {
                    query.union(query_table);
                }
                else
                {
                    query = query_table;
                }
            }
        }

        return query;
    }

    /**
     * Create a new factory for objects from the ResultSet.
     * @param stmt The Query Statement
     * @param ignoreCache Whether to ignore dirty objects
     * @param resultClass Whether to create objects of a particular class
     * @param useFetchPlan whether to use the fetch plan to retrieve fields in the same query
     * @return The result object factory
     */
    public ResultObjectFactory newResultObjectFactory(QueryExpression stmt, boolean ignoreCache, Class resultClass, boolean useFetchPlan)
    {
        int[] prefetchFieldNumbers = null;
        StatementExpressionIndex[] statementExpressionIndex = null;
        int[] datastoreIndex = null;
        int[] versionIndex = null;

        if (tables[0].getIdentityType() == IdentityType.DATASTORE)
        {
            // Select the datastore identity column (if present)
            if (multipleTableCase)
            {
                datastoreIndex = stmt.selectDatastoreIdentity("DATASTORE_ID", true);
            }
            else if (stmt.getTableExpression(elmIdentifier) != null)
            {
                datastoreIndex = stmt.select(elmIdentifier, tables[0].getDataStoreObjectIdMapping(), true);
            }
            else
            {
                datastoreIndex = stmt.select(stmt.getMainTableAlias(), tables[0].getDataStoreObjectIdMapping(), true);
            }
        }

        JavaTypeMapping versionMapping = tables[0].getVersionMapping(true);
        if (versionMapping != null)
        {
            // Select the version column (if present)
            if (multipleTableCase)
            {
                versionIndex = stmt.selectVersion("VERSION", true);
            }
            else if (stmt.getTableExpression(elmIdentifier) != null)
            {
                versionIndex = stmt.select(elmIdentifier, versionMapping, true);
            }
            else
            {
                versionIndex = stmt.select(stmt.getMainTableAlias(), versionMapping, true);
            }
        }

        if (multipleTableCase)
        {
            // Select application identity columns for the tables involved in the union.
            AbstractClassMetaData cmd = storeMgr.getOMFContext().getMetaDataManager().getMetaDataForClass(getCandidateClass(), stmt.getClassLoaderResolver());
            if (cmd.getIdentityType() == IdentityType.APPLICATION)
            {
                prefetchFieldNumbers = new int[cmd.getPKMemberPositions().length];
                int fieldCount = cmd.getNoOfInheritedManagedMembers() + cmd.getNoOfManagedMembers();
                statementExpressionIndex = new StatementExpressionIndex[fieldCount];
                for (int i = 0; i < prefetchFieldNumbers.length; ++i)
                {
                    prefetchFieldNumbers[i] = cmd.getPKMemberPositions()[i];
                    String fieldName = cmd.getMetaDataForManagedMemberAtAbsolutePosition(prefetchFieldNumbers[i]).getName();
                    // TODO This only selects one field of the application identity ... as APP_ID.
                    // If multiple we should have APP_ID1, APP_ID2, etc. It also only looks at the first table
                    JavaTypeMapping m = tables[0].getFieldMapping(cmd.getMetaDataForManagedMemberAtAbsolutePosition(prefetchFieldNumbers[i]));
                    if (m != null) // field is not stored in the table, e.g List, Set, etc or is transactional
                    {
                        if (m.includeInFetchStatement() && !(m instanceof AbstractContainerMapping))
                        {
                            statementExpressionIndex[prefetchFieldNumbers[i]] = new StatementExpressionIndex();
                            statementExpressionIndex[prefetchFieldNumbers[i]].setMapping(m);
                            statementExpressionIndex[prefetchFieldNumbers[i]].setExpressionIndex(stmt.selectField(fieldName, "APP_ID", true));
                        }
                    }
                }
            }
            // TODO Select fetch plan fields also ? This should be done for all fields that are in *all* subclass tables
        }
        else
        {
            // TODO When we have all possible candidates in single table and using discriminator
            // we should really select all fields here to save doing the retrieve of the other fields later.
            // Currently we only retrieve the fetch-plan/DFG fields for the base candidate class

            if (useFetchPlan)
            {
                // Select all fields in the FetchPlan
                // TODO This only utilises the first table. What if we have multiple ?
                AbstractClassMetaData cmd = storeMgr.getOMFContext().getMetaDataManager().getMetaDataForClass(getCandidateClass(), stmt.getClassLoaderResolver());
                getFetchPlan().manageFetchPlanForClass(cmd);
                FetchPlanForClass fpc = getFetchPlan().getFetchPlanForClass(cmd);
                int fieldNumbers[] = fpc.getFieldsInActualFetchPlan();
                int prefetchFieldCount = 0;
                int fieldCount = cmd.getNoOfInheritedManagedMembers() + cmd.getNoOfManagedMembers();
               
                int fn[] = new int[fieldNumbers.length];
                statementExpressionIndex = new StatementExpressionIndex[fieldCount];
                for (int i = 0; i < fieldNumbers.length; ++i)
                {
                    JavaTypeMapping m = tables[0].getFieldMapping(cmd.getMetaDataForManagedMemberAtAbsolutePosition(fieldNumbers[i]));
                    if (m != null)
                    {
                        // omit fields not stored in the table, e.g. List, Set, etc or transactional
                        if (m.includeInFetchStatement() && !(m instanceof AbstractContainerMapping))
                        {
                            statementExpressionIndex[fieldNumbers[i]] = new StatementExpressionIndex();
                            statementExpressionIndex[fieldNumbers[i]].setMapping(m);
                            fn[prefetchFieldCount++] = fieldNumbers[i];
                        }
                    }
                }
               
                prefetchFieldNumbers = new int[prefetchFieldCount];
                System.arraycopy(fn, 0, prefetchFieldNumbers, 0, prefetchFieldCount);
               
                // select the fetch-plan fields to be fetched from the database
                if (stmt.getTableExpression(elmIdentifier) != null)
                {
                    Mappings.selectMapping(stmt, elmIdentifier, statementExpressionIndex);
                }
                else
                {
                    Mappings.selectMapping(stmt, statementExpressionIndex);
                }
            }
            else
            {
                // if no fetchplan is used and is application identity, select only PK fields
                AbstractClassMetaData cmd = storeMgr.getOMFContext().getMetaDataManager().getMetaDataForClass(getCandidateClass(),stmt.getClassLoaderResolver());
                if (cmd.getIdentityType() == IdentityType.APPLICATION)
                {
                    prefetchFieldNumbers = new int[cmd.getPKMemberPositions().length];
                    int fieldCount = cmd.getNoOfInheritedManagedMembers() + cmd.getNoOfManagedMembers();
                    statementExpressionIndex = new StatementExpressionIndex[fieldCount];
                    for (int i = 0; i < prefetchFieldNumbers.length; ++i)
                    {
                        prefetchFieldNumbers[i] = cmd.getPKMemberPositions()[i];
                        JavaTypeMapping m = tables[0].getFieldMapping(cmd.getMetaDataForManagedMemberAtAbsolutePosition(prefetchFieldNumbers[i]));
                        if (m != null) // field is not stored in the table, e.g List, Set, etc or is transactional
                        {
                            if (m.includeInFetchStatement() && !(m instanceof AbstractContainerMapping))
                            {
                                statementExpressionIndex[prefetchFieldNumbers[i]] = new StatementExpressionIndex();
                                statementExpressionIndex[prefetchFieldNumbers[i]].setMapping(m);
                            }
                        }
                    }
                    // following the actual fetch plan, we select the fields to be fetched from the database
                    if (stmt.getTableExpression(elmIdentifier) != null)
                    {
                        Mappings.selectMapping(stmt, elmIdentifier, statementExpressionIndex);
                    }
                    else
                    {
                        Mappings.selectMapping(stmt, statementExpressionIndex);
                    }
                }
            }
        }

        if (resultClass == null)
        {
            return new PersistentIDROF(tables[0], prefetchFieldNumbers, cmd, statementExpressionIndex, datastoreIndex,
                versionIndex, ignoreCache, queryUsingDiscriminator(), stmt.hasMetaDataExpression(),
                getFetchPlan(), query.getCandidateClass());
        }
        else
        {
            return new ResultClassROF(resultClass, statementExpressionIndex);
        }
    }

    /**
     * Convenience method whether the query is to use a discriminator for
     * distinguishing between elements. We use a discriminator in the situation
     * where the candidate class is using a discriminator. This means that instances of that
     * class and all subclasses will have an entry in the discriminator column for the
     * type of the instance.
     * @return Whether we are to query using a discriminator
     */
    private boolean queryUsingDiscriminator()
    {
        boolean usingDiscriminator = true;
        if (tables.length == 1)
        {
            if (tables[0].getDiscriminatorMetaData() == null || tables[0].getDiscriminatorMapping(false) == null)
            {
                // No discriminator metadata available
                return false;
            }

            return usingDiscriminator;
        }
        else
        {
            return false;
        }
    }

    /**
     * Returns <tt>true</tt> if this collection contains no elements.
     * @return Whether the extent has no elements.
     */
    public boolean isEmpty()
    {
        // we don't know if we are empty until it calls the datastore
        return false;
    }
}
TOP

Related Classes of org.jpox.store.rdbms.extent.ClassTableExtent

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.